Fix potential NRE in the PCSC transport, retry spurious failure of authentication in AuthenticateEV2First

This commit is contained in:
nicolas.dorier 2023-12-08 16:09:54 +09:00
parent ae80f9ab79
commit a329fa2f03
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
5 changed files with 45 additions and 9 deletions

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PCSC;
using PCSC.Exceptions;
using PCSC.Extensions;
namespace BTCPayServer.NTag424.PCSC;
internal static class Extensions
{
public static void ThrowEx(this SCardError sc)
{
try
{
sc.Throw();
}
catch (NullReferenceException)
{
throw new PCSCException(sc, $"Unknown PCSC error: {(int)sc}");
}
throw new PCSCException(sc);
}
}

View File

@ -24,7 +24,7 @@ public class PCSCAPDUTransport : IAPDUTransport
int received = resp.Length;
var sc = CardReader.Transmit(apdu, resp, ref received);
if (sc != SCardError.Success)
sc.Throw();
sc.ThrowEx();
var sw1sw2 = (ushort)(resp[received - 2] << 8 | resp[received - 1]);
var data = resp[..(received - 2)];
return new NtagResponse(data, sw1sw2);

View File

@ -125,13 +125,14 @@ public class Ntag424
{
return AuthenticateEV2(keyNo, key, false, cancellationToken);
}
public Task<Session> AuthenticateEV2First(int keyNo, AESKey key, CancellationToken cancellationToken = default)
public Task<Session> AuthenticateEV2First(int keyNo, AESKey? key, CancellationToken cancellationToken = default)
{
return AuthenticateEV2(keyNo, key, true, cancellationToken);
}
async Task<Session> AuthenticateEV2(int keyNo, AESKey key, bool first, CancellationToken cancellationToken = default)
async Task<Session> AuthenticateEV2(int keyNo, AESKey? key, bool first, CancellationToken cancellationToken = default)
{
int sessionCounter = CurrentSession?.Counter ?? 0;
key ??= AESKey.Default;
int sessionCounter;
if (first)
{
await IsoSelectFile(ISOLevel.Application);
@ -143,7 +144,8 @@ public class Ntag424
throw new InvalidOperationException("Authentication required for AuthenticateEV2NonFirst");
sessionCounter = CurrentSession.Counter;
}
bool illegalCommandThrown = false;
retry:
NtagResponse resp;
if (first)
{
@ -164,10 +166,19 @@ public class Ntag424
var rndA = RandomNumberGenerator.GetBytes(16);
var encRnd = key.Encrypt(Concat(rndA, rndBp));
var secondPart = first ? NtagCommands.AuthenticateEV2FirstPart2 : NtagCommands.AuthenticateEV2NonFirstPart2;
resp = await SendAPDU(secondPart with
try
{
Data = encRnd
}, cancellationToken);
resp = await SendAPDU(secondPart with
{
Data = encRnd
}, cancellationToken);
}
// Sometimes, the card returns this error, unsure why, but retrying the auth seems to work
catch (UnexpectedStatusException ex) when (first && !illegalCommandThrown && ex.Details?.Code.Equals("ILLEGAL_COMMAND_CODE", StringComparison.Ordinal) is true)
{
illegalCommandThrown = true;
goto retry;
}
var data = key.Decrypt(resp.Data);
var rndAp = RotateLeft(rndA);

View File

@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("BTCPayServer.NTag424.Tests")]
[assembly: InternalsVisibleTo("BTCPayServer.NTag424.PCSC")]

View File

@ -4,7 +4,6 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>