parent
f3663f1b41
commit
7a080befec
@ -29,14 +29,13 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
|
||||
<PackageReference Include="NdefLibrary" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\BTCPayServer.png" Pack="true" PackagePath="\" />
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||
<None Include="..\..\BTCPayServer.png" Pack="true" PackagePath="\"/>
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -3,10 +3,6 @@ using System.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using NdefLibrary.Ndef;
|
||||
using Org.BouncyCastle.Asn1.X9;
|
||||
using Org.BouncyCastle.Crypto.EC;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Signers;
|
||||
using static BTCPayServer.NTag424.Helpers;
|
||||
|
||||
namespace BTCPayServer.NTag424;
|
||||
@ -97,7 +93,6 @@ public class Ntag424
|
||||
Transport = transport;
|
||||
}
|
||||
public Session? CurrentSession { get; private set; }
|
||||
public static ECDsaSigner NXPPubKey { get; }
|
||||
|
||||
public async Task IsoSelectFile(ISOLevel level)
|
||||
{
|
||||
@ -380,33 +375,6 @@ public class Ntag424
|
||||
CurrentSession = null;
|
||||
}
|
||||
|
||||
static Ntag424()
|
||||
{
|
||||
var ecP = CustomNamedCurves.GetByName("secp224r1");
|
||||
var domainParameters = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
|
||||
var pubkey = new ECPublicKeyParameters(
|
||||
ecP.Curve.DecodePoint("048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410".HexToBytes()),
|
||||
domainParameters);
|
||||
NXPPubKey = new ECDsaSigner();
|
||||
NXPPubKey.Init(false, pubkey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure the card is genuine
|
||||
/// </summary>
|
||||
/// <returns>The UID</returns>
|
||||
/// <exception cref="SecurityException">Invalid signature</exception>
|
||||
public async Task<byte[]> CheckOriginality()
|
||||
{
|
||||
var uid = await GetCardUID();
|
||||
var resp = await SendAPDU(NtagCommands.Read_Sig);
|
||||
var r = new Org.BouncyCastle.Math.BigInteger(1, resp.Data[..28]);
|
||||
var s = new Org.BouncyCastle.Math.BigInteger(1, resp.Data[28..]);
|
||||
if (!NXPPubKey.VerifySignature(uid, r, s))
|
||||
throw new SecurityException("Invalid signature");
|
||||
return uid;
|
||||
}
|
||||
|
||||
public async Task ResetCard(AESKey issuerKey, uint batchId = 0)
|
||||
{
|
||||
if (CurrentSession is null)
|
||||
|
||||
@ -76,6 +76,24 @@ public record NTagCommand(string Name, byte CLA, byte INS, byte? P1, byte? P2, b
|
||||
list.Add(P2.Value);
|
||||
if (Data != null)
|
||||
{
|
||||
if (Lc.HasValue)
|
||||
{
|
||||
var realLc = Lc.Value;
|
||||
if (CommMode is NTag424.CommMode.Full)
|
||||
{
|
||||
var encDataSize = realLc - CommandHeaderSize;
|
||||
realLc = (byte)CommandHeaderSize;
|
||||
realLc += (byte)(16 - (encDataSize % 16)); // Padding
|
||||
realLc += 8; // Add mac
|
||||
}
|
||||
if (CommMode is NTag424.CommMode.MAC)
|
||||
{
|
||||
realLc += 8; // Add mac
|
||||
}
|
||||
|
||||
if (realLc != Data.Length)
|
||||
throw new InvalidOperationException("Invalid Data length");
|
||||
}
|
||||
list.Add((byte)(Data.Length));
|
||||
list.AddRange(Data);
|
||||
}
|
||||
@ -157,9 +175,9 @@ internal class NtagCommands
|
||||
internal readonly static NTagCommand GetVersionPart3 = new(Name: "GetVersionPart3", CLA: 0x90, INS: 0xAF, P1: 0, P2: 0, Lc: null, Data: null, Le: 0, ExpectedStatus: 0x9100, CommMode: CommMode.MAC);
|
||||
internal readonly static NTagCommand ISOReadBinary = new(Name: "ISOReadBinary", CLA: 0x00, INS: 0xB0, P1: null, P2: null, Lc: null, Data: null, Le: null, ExpectedStatus: 0x9000, CommMode: CommMode.Plain);
|
||||
internal readonly static NTagCommand ReadData = new(Name: "ReadData", CLA: 0x90, INS: 0xAD, P1: 0, P2: 0, Lc: null, Data: null, Le: 0, ExpectedStatus: 0x9100, CommMode: null, CommandHeaderSize: 7);
|
||||
internal readonly static NTagCommand Read_Sig = new(Name: "Read_Sig", CLA: 0x90, INS: 0x3C, P1: 0, P2: 0, Lc: 1, Data: new byte[1], Le: 0, ExpectedStatus: 0x9190, CommMode: CommMode.Full, CommandHeaderSize: 1);
|
||||
internal readonly static NTagCommand Read_Sig = new(Name: "Read_Sig", CLA: 0x90, INS: 0x3C, P1: 0, P2: 0, Lc: 1, Data: null, Le: 0, ExpectedStatus: 0x9100, CommMode: CommMode.Full);
|
||||
internal readonly static NTagCommand ISOSelectFile = new(Name: "ISOSelectFile", CLA: 0x00, INS: 0xA4, P1: null, P2: null, Lc: null, Data: null, Le: null, ExpectedStatus: 0x9000, CommMode: CommMode.Plain);
|
||||
internal readonly static NTagCommand SetConfiguration = new(Name: "SetConfiguration", CLA: 0x90, INS: 0x5C, P1: 0, P2: 0, Lc: null, Data: null, Le: 0, ExpectedStatus: 0x9100, CommMode: CommMode.Full, CommandHeaderSize: 0);
|
||||
internal readonly static NTagCommand SetConfiguration = new(Name: "SetConfiguration", CLA: 0x90, INS: 0x5C, P1: 0, P2: 0, Lc: null, Data: null, Le: 0, ExpectedStatus: 0x9100, CommMode: CommMode.Full, CommandHeaderSize: 1);
|
||||
internal readonly static NTagCommand ISOUpdateBinary = new(Name: "ISOUpdateBinary", CLA: 0x00, INS: 0xD6, P1: null, P2: null, Lc: null, Data: null, Le: null, ExpectedStatus: 0x9000, CommMode: CommMode.Plain);
|
||||
internal readonly static NTagCommand WriteData = new(Name: "WriteData", CLA: 0x90, INS: 0x8D, P1: 0, P2: 0, Lc: null, Data: null, Le: 0, ExpectedStatus: 0x9100, CommMode: null, CommandHeaderSize: 7);
|
||||
}
|
||||
|
||||
@ -3,18 +3,11 @@ using System.Text.RegularExpressions;
|
||||
using BTCPayServer.NTag424.PCSC;
|
||||
using NdefLibrary.Ndef;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace BTCPayServer.NTag424.Tests;
|
||||
|
||||
public class UnitTest1
|
||||
{
|
||||
public ITestOutputHelper Logs { get; }
|
||||
|
||||
public UnitTest1(ITestOutputHelper logs)
|
||||
{
|
||||
Logs = logs;
|
||||
}
|
||||
[Fact]
|
||||
public void CanCreateAPDUFromNtagCommand()
|
||||
{
|
||||
@ -174,16 +167,6 @@ public class UnitTest1
|
||||
await ntag.ChangeKey(1, key1, key2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCheckOriginality()
|
||||
{
|
||||
using var ctx = PCSCContext.Create();
|
||||
var ntag = ctx.CreateNTag424();
|
||||
await ntag.AuthenticateEV2First(0, AESKey.Default);
|
||||
await ntag.CheckOriginality();
|
||||
await ntag.CheckOriginality();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanDoBoltcard()
|
||||
{
|
||||
@ -206,22 +189,6 @@ public class UnitTest1
|
||||
await ntag.ResetCard(keys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestDeterministicBoltcard()
|
||||
{
|
||||
var uid = "04a39493cc8680".HexToBytes();
|
||||
uint batch = 1;
|
||||
var issuerKey = new AESKey("00000000000000000000000000000001".HexToBytes());
|
||||
var keys = BoltcardKeys.CreateDeterministicKeys(issuerKey, uid, batch);
|
||||
Logs.WriteLine("UID = " + uid.ToHex());
|
||||
Logs.WriteLine("Batch = " + Helpers.UIntToBytesLE(1).ToHex());
|
||||
Logs.WriteLine("K0 = " + issuerKey.ToBytes().ToHex());
|
||||
Logs.WriteLine("K1 = " + keys.EncryptionKey.ToBytes().ToHex());
|
||||
Logs.WriteLine("K2 = " + keys.AuthenticationKey.ToBytes().ToHex());
|
||||
Logs.WriteLine("K3 = " + keys.K3.ToBytes().ToHex());
|
||||
Logs.WriteLine("K4 = " + keys.K4.ToBytes().ToHex());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanDoDeterministicBoltcard()
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user