-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into fix/ExtractUriFromInternetIdentifier-bug
- Loading branch information
Showing
6 changed files
with
518 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using NBitcoin; | ||
using NBitcoin.Altcoins.Elements; | ||
using NBitcoin.Crypto; | ||
using NBitcoin.DataEncoders; | ||
using Newtonsoft.Json; | ||
using Xunit; | ||
|
@@ -9,43 +13,40 @@ namespace LNURL.Tests | |
{ | ||
public class UnitTest1 | ||
{ | ||
|
||
[Fact] | ||
public void CanHandlePayLinkEdgeCase() | ||
{ | ||
// from https://github.com/btcpayserver/btcpayserver/issues/4393 | ||
var json = | ||
|
||
"{"+ | ||
" \"callback\": \"https://coincorner.io/lnurl/withdrawreq/auth/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?picc_data=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\","+ | ||
" \"defaultDescription\": \"CoinCorner Withdrawal ⚡️\","+ | ||
" \"maxWithdrawable\": 363003000,"+ | ||
" \"minWithdrawable\": 1000,"+ | ||
" \"k1\": \"xxxxxxxxxx\","+ | ||
" \"tag\": \"withdrawRequest\","+ | ||
" \"payLink\": \"\"" + | ||
var json = | ||
"{" + | ||
" \"callback\": \"https://coincorner.io/lnurl/withdrawreq/auth/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?picc_data=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"," + | ||
" \"defaultDescription\": \"CoinCorner Withdrawal ⚡️\"," + | ||
" \"maxWithdrawable\": 363003000," + | ||
" \"minWithdrawable\": 1000," + | ||
" \"k1\": \"xxxxxxxxxx\"," + | ||
" \"tag\": \"withdrawRequest\"," + | ||
" \"payLink\": \"\"" + | ||
"}"; | ||
|
||
var req = JsonConvert.DeserializeObject<LNURLWithdrawRequest>(json); | ||
|
||
Assert.Null(req.PayLink); | ||
|
||
json = | ||
|
||
"{"+ | ||
" \"callback\": \"https://coincorner.io/lnurl/withdrawreq/auth/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?picc_data=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\","+ | ||
" \"defaultDescription\": \"CoinCorner Withdrawal ⚡️\","+ | ||
" \"maxWithdrawable\": 363003000,"+ | ||
" \"minWithdrawable\": 1000,"+ | ||
" \"k1\": \"xxxxxxxxxx\","+ | ||
" \"tag\": \"withdrawRequest\""+ | ||
|
||
json = | ||
"{" + | ||
" \"callback\": \"https://coincorner.io/lnurl/withdrawreq/auth/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?picc_data=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"," + | ||
" \"defaultDescription\": \"CoinCorner Withdrawal ⚡️\"," + | ||
" \"maxWithdrawable\": 363003000," + | ||
" \"minWithdrawable\": 1000," + | ||
" \"k1\": \"xxxxxxxxxx\"," + | ||
" \"tag\": \"withdrawRequest\"" + | ||
"}"; | ||
|
||
req = JsonConvert.DeserializeObject<LNURLWithdrawRequest>(json); | ||
|
||
Assert.Null(req.PayLink); | ||
} | ||
|
||
[Theory] | ||
[InlineData("[email protected]", "https://btcpay.kukks.org/.well-known/lnurlp/kukks")] | ||
[InlineData("[email protected]:4000", "https://btcpay.kukks.org:4000/.well-known/lnurlp/kukks")] | ||
|
@@ -54,7 +55,6 @@ public void CanHandlePayLinkEdgeCase() | |
public void CanParseLightningAddress(string lightningAddress, string expectedUrl) | ||
{ | ||
Assert.Equal(expectedUrl, LNURL.ExtractUriFromInternetIdentifier(lightningAddress).ToString()); | ||
|
||
} | ||
|
||
[Fact] | ||
|
@@ -71,21 +71,16 @@ public void CanEncodeDecodeLNUrl() | |
"LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS", | ||
LNURL.EncodeBech32(uri), StringComparer.InvariantCultureIgnoreCase); | ||
|
||
Assert.Equal("lightning:LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS", | ||
Assert.Equal( | ||
"lightning:LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS", | ||
LNURL.EncodeUri(uri, null, true).ToString(), StringComparer.InvariantCultureIgnoreCase); | ||
|
||
Assert.Throws<ArgumentNullException>(() => | ||
{ | ||
LNURL.EncodeUri(uri, null, false); | ||
}); | ||
Assert.Throws<ArgumentOutOfRangeException>(() => | ||
{ | ||
LNURL.EncodeUri(uri, "swddwdd", false); | ||
}); | ||
Assert.Throws<ArgumentNullException>(() => { LNURL.EncodeUri(uri, null, false); }); | ||
Assert.Throws<ArgumentOutOfRangeException>(() => { LNURL.EncodeUri(uri, "swddwdd", false); }); | ||
var payRequestUri = LNURL.EncodeUri(uri, "payRequest", false); | ||
Assert.Equal("lnurlp", payRequestUri.Scheme); | ||
|
||
|
||
uri = LNURL.Parse( | ||
"lnurlp://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df", | ||
out tag); | ||
|
@@ -98,11 +93,7 @@ public void CanEncodeDecodeLNUrl() | |
[Fact] | ||
public async Task CanUseLNURLAUTH() | ||
{ | ||
|
||
Assert.Throws<ArgumentException>(() => | ||
{ | ||
LNURL.EncodeUri(new Uri("https://kukks.org"), "login", true); | ||
}); | ||
Assert.Throws<ArgumentException>(() => { LNURL.EncodeUri(new Uri("https://kukks.org"), "login", true); }); | ||
Assert.Throws<ArgumentException>(() => | ||
{ | ||
LNURL.EncodeUri(new Uri("https://kukks.org?tag=login"), "login", true); | ||
|
@@ -116,16 +107,15 @@ public async Task CanUseLNURLAUTH() | |
var k1 = Encoders.Hex.EncodeData(RandomUtils.GetBytes(32)); | ||
LNURL.EncodeUri(new Uri($"https://kukks.org?tag=login&k1={k1}&action=xyz"), "login", true); | ||
}); | ||
|
||
var k1 = Encoders.Hex.EncodeData(RandomUtils.GetBytes(32)); | ||
var lnurl = LNURL.EncodeUri(new Uri($"https://kukks.org?tag=login&k1={k1}"), "login", true); | ||
|
||
var request = Assert.IsType<LNAuthRequest>(await LNURL.FetchInformation(lnurl, null)); | ||
var k1 = Encoders.Hex.EncodeData(RandomUtils.GetBytes(32)); | ||
var lnurl = LNURL.EncodeUri(new Uri($"https://kukks.org?tag=login&k1={k1}"), "login", true); | ||
|
||
var linkingKey = new Key(); | ||
var sig = request.SignChallenge(linkingKey); | ||
Assert.True( LNAuthRequest.VerifyChallenge(sig, linkingKey.PubKey, Encoders.Hex.DecodeData(k1))); | ||
var request = Assert.IsType<LNAuthRequest>(await LNURL.FetchInformation(lnurl, null)); | ||
|
||
var linkingKey = new Key(); | ||
var sig = request.SignChallenge(linkingKey); | ||
Assert.True(LNAuthRequest.VerifyChallenge(sig, linkingKey.PubKey, Encoders.Hex.DecodeData(k1))); | ||
} | ||
|
||
[Fact] | ||
|
@@ -151,37 +141,139 @@ public async Task payerDataSerializerTest() | |
} | ||
}; | ||
|
||
req = JsonConvert.DeserializeObject<LNURLPayRequest>(JsonConvert.SerializeObject(req)); | ||
Assert.NotNull(req.PayerData); | ||
Assert.True(req.PayerData.Pubkey.Mandatory); | ||
Assert.False(req.PayerData.Name.Mandatory); | ||
Assert.False(req.PayerData.Auth.Mandatory); | ||
Assert.Null(req.PayerData.Email); | ||
|
||
var k = new Key(); | ||
|
||
var resp = new LNURLPayRequest.LUD18PayerDataResponse() | ||
{ | ||
Auth = new LNURLPayRequest.LUD18AuthPayerDataResponse() | ||
{ | ||
K1 = req.PayerData.Auth.K1, | ||
Key = k.PubKey, | ||
Sig = k.Sign(new uint256(Encoders.Hex.DecodeData(req.PayerData.Auth.K1))) | ||
}, | ||
}; | ||
|
||
resp = JsonConvert.DeserializeObject<LNURLPayRequest.LUD18PayerDataResponse>(JsonConvert.SerializeObject(resp)); | ||
Assert.False(req.VerifyPayerData(resp)); | ||
resp.Pubkey = k.PubKey; | ||
resp = JsonConvert.DeserializeObject<LNURLPayRequest.LUD18PayerDataResponse>(JsonConvert.SerializeObject(resp)); | ||
Assert.True(req.VerifyPayerData(resp)); | ||
resp.Email = "[email protected]"; | ||
Assert.False(req.VerifyPayerData(resp)); | ||
|
||
resp.Email = null; | ||
resp.Name = "sdasds"; | ||
Assert.True(req.VerifyPayerData(resp)); | ||
req = JsonConvert.DeserializeObject<LNURLPayRequest>(JsonConvert.SerializeObject(req)); | ||
Assert.NotNull(req.PayerData); | ||
Assert.True(req.PayerData.Pubkey.Mandatory); | ||
Assert.False(req.PayerData.Name.Mandatory); | ||
Assert.False(req.PayerData.Auth.Mandatory); | ||
Assert.Null(req.PayerData.Email); | ||
|
||
var k = new Key(); | ||
|
||
var resp = new LNURLPayRequest.LUD18PayerDataResponse() | ||
{ | ||
Auth = new LNURLPayRequest.LUD18AuthPayerDataResponse() | ||
{ | ||
K1 = req.PayerData.Auth.K1, | ||
Key = k.PubKey, | ||
Sig = k.Sign(new uint256(Encoders.Hex.DecodeData(req.PayerData.Auth.K1))) | ||
}, | ||
}; | ||
|
||
resp = JsonConvert.DeserializeObject<LNURLPayRequest.LUD18PayerDataResponse>( | ||
JsonConvert.SerializeObject(resp)); | ||
Assert.False(req.VerifyPayerData(resp)); | ||
resp.Pubkey = k.PubKey; | ||
resp = JsonConvert.DeserializeObject<LNURLPayRequest.LUD18PayerDataResponse>( | ||
JsonConvert.SerializeObject(resp)); | ||
Assert.True(req.VerifyPayerData(resp)); | ||
resp.Email = "[email protected]"; | ||
Assert.False(req.VerifyPayerData(resp)); | ||
|
||
resp.Email = null; | ||
resp.Name = "sdasds"; | ||
Assert.True(req.VerifyPayerData(resp)); | ||
} | ||
|
||
[Fact] | ||
public async Task CanUseBoltCardHelper() | ||
Check warning on line 179 in LNURL.Tests/UnitTest1.cs GitHub Actions / build
|
||
{ | ||
var key = Convert.FromHexString("0c3b25d92b38ae443229dd59ad34b85d"); | ||
var cmacKey = Convert.FromHexString("b45775776cb224c75bcde7ca3704e933"); | ||
var result = BoltCardHelper.ExtractBoltCardFromRequest( | ||
new Uri("https://test.com?p=4E2E289D945A66BB13377A728884E867&c=E19CCB1FED8892CE"), | ||
key, out var error); | ||
|
||
Assert.Null(error); | ||
Assert.NotNull(result); | ||
Assert.Equal((uint) 3, result.Value.counter); | ||
Assert.Equal("04996c6a926980", result.Value.uid); | ||
Assert.True(BoltCardHelper.CheckCmac(result.Value.rawUid, result.Value.rawCtr, cmacKey, result.Value.c, | ||
out error)); | ||
|
||
var manualP = BoltCardHelper.CreatePValue(key, result.Value.counter, result.Value.uid); | ||
var manualPResult = BoltCardHelper.ExtractUidAndCounterFromP(manualP, key, out error); | ||
Assert.Null(error); | ||
Assert.NotNull(manualPResult); | ||
Assert.Equal((uint) 3, manualPResult.Value.counter); | ||
Assert.Equal("04996c6a926980", manualPResult.Value.uid); | ||
|
||
var manualC = BoltCardHelper.CreateCValue(result.Value.rawUid, result.Value.rawCtr, cmacKey); | ||
Assert.Equal(result.Value.c, manualC); | ||
} | ||
|
||
[Fact] | ||
public async Task DeterministicCards() | ||
Check warning on line 206 in LNURL.Tests/UnitTest1.cs GitHub Actions / build
|
||
{ | ||
var masterSeed = RandomUtils.GetBytes(64); | ||
var masterSeedSlip21 = Slip21Node.FromSeed(masterSeed); | ||
|
||
var i = Random.Shared.Next(0, 10000); | ||
var k1 = masterSeedSlip21.DeriveChild(i + "k1").Key.ToBytes().Take(16).ToArray(); | ||
var k2 = masterSeedSlip21.DeriveChild(i + "k2").Key.ToBytes().Take(16).ToArray(); | ||
|
||
var counter = (uint) Random.Shared.Next(0, 1000); | ||
var uid = Convert.ToHexString(RandomUtils.GetBytes(7)); | ||
var pParam = Convert.ToHexString(BoltCardHelper.CreatePValue(k1, counter, uid)); | ||
var cParam = Convert.ToHexString(BoltCardHelper.CreateCValue(uid, counter, k2)); | ||
var lnurlw = $"https://test.com?p={pParam}&c={cParam}"; | ||
|
||
var result = BoltCardHelper.ExtractBoltCardFromRequest(new Uri(lnurlw), k1, out var error); | ||
Assert.Null(error); | ||
Assert.NotNull(result); | ||
Assert.Equal(uid.ToLowerInvariant(), result.Value.uid.ToLowerInvariant()); | ||
Assert.Equal(counter, result.Value.counter); | ||
Assert.True(BoltCardHelper.CheckCmac(result.Value.rawUid, result.Value.rawCtr, k2, result.Value.c, | ||
out error)); | ||
Assert.Null(error); | ||
|
||
|
||
for (int j = 0; j <= 10000; j++) | ||
{ | ||
var brutek1 = masterSeedSlip21.DeriveChild(j + "k1").Key.ToBytes().Take(16).ToArray(); | ||
var brutek2 = masterSeedSlip21.DeriveChild(j + "k2").Key.ToBytes().Take(16).ToArray(); | ||
try | ||
{ | ||
var bruteResult = BoltCardHelper.ExtractBoltCardFromRequest(new Uri(lnurlw), brutek1, out error); | ||
Assert.Null(error); | ||
Assert.NotNull(bruteResult); | ||
Assert.Equal(uid.ToLowerInvariant(), bruteResult.Value.uid.ToLowerInvariant()); | ||
Assert.Equal(counter, bruteResult.Value.counter); | ||
Assert.True(BoltCardHelper.CheckCmac(bruteResult.Value.rawUid, bruteResult.Value.rawCtr, brutek2, | ||
bruteResult.Value.c, out error)); | ||
Assert.Null(error); | ||
|
||
break; | ||
} | ||
catch (Exception e) | ||
{ | ||
} | ||
} | ||
} | ||
//from https://github.com/boltcard/boltcard/blob/7745c9f20d5ad0129cb4b3fc534441038e79f5e6/docs/TEST_VECTORS.md | ||
[Theory] | ||
[InlineData("4E2E289D945A66BB13377A728884E867", "E19CCB1FED8892CE", "04996c6a926980", 3)] | ||
[InlineData("00F48C4F8E386DED06BCDC78FA92E2FE", "66B4826EA4C155B4", "04996c6a926980", 5)] | ||
[InlineData("0DBF3C59B59B0638D60B5842A997D4D1", "CC61660C020B4D96", "04996c6a926980", 7)] | ||
public void TestDecryptAndValidate(string pValueHex, string cValueHex, string expectedUidHex, uint expectedCtr) | ||
{ | ||
|
||
var aesDecryptKey = Convert.FromHexString("0c3b25d92b38ae443229dd59ad34b85d"); | ||
var aesCmacKey = Convert.FromHexString("b45775776cb224c75bcde7ca3704e933"); | ||
byte[] pValue = Convert.FromHexString(pValueHex); | ||
byte[] cValue = Convert.FromHexString(cValueHex); | ||
|
||
// Decrypt p value | ||
var res = BoltCardHelper.ExtractUidAndCounterFromP(pValue, aesDecryptKey, out _); | ||
|
||
// Check UID and counter | ||
Assert.Equal(expectedUidHex, res.Value.uid); | ||
Assert.Equal(expectedCtr, res.Value.counter); | ||
|
||
// Validate CMAC | ||
var cmacIsValid = BoltCardHelper.CheckCmac(res.Value.rawUid, res.Value.rawCtr, aesCmacKey, cValue, out _); | ||
Assert.True(cmacIsValid, "CMAC validation failed"); | ||
} | ||
|
||
} | ||
} | ||
} |
Oops, something went wrong.