Skip to content

Commit ea33a94

Browse files
committed
feat: throw InvalidOperationException on too large APDU size
1 parent a5fbe56 commit ea33a94

File tree

13 files changed

+906
-1430
lines changed

13 files changed

+906
-1430
lines changed

Yubico.Core/src/Yubico/Core/Devices/SmartCard/DesktopSmartCardConnection.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,18 +146,18 @@ public ResponseApdu Transmit(CommandApdu commandApdu)
146146
// The YubiKey likely will never return a buffer larger than 512 bytes without instead
147147
// using response chaining.
148148
byte[] outputBuffer = new byte[512];
149-
149+
150+
byte[] apdu = commandApdu.AsByteArray();
150151
uint result = SCardTransmit(
151152
_cardHandle,
152153
new SCARD_IO_REQUEST(_activeProtocol),
153-
commandApdu.AsByteArray(),
154+
apdu,
154155
IntPtr.Zero,
155156
outputBuffer,
156157
out int outputBufferSize
157158
);
158159

159160
_log.SCardApiCall(nameof(SCardTransmit), result);
160-
161161
_device.LogDeviceAccessTime();
162162

163163
if (result != ErrorCode.SCARD_S_SUCCESS)

Yubico.YubiKey/src/Resources/ExceptionMessages.Designer.cs

Lines changed: 532 additions & 1329 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Yubico.YubiKey/src/Resources/ExceptionMessages.resx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,4 +910,8 @@
910910
<data name="KeyAgreementReceiptMissmatch" xml:space="preserve">
911911
<value>Key agreement receipts do not match</value>
912912
</data>
913+
914+
<data name="CommandApduTooLarge" xml:space="preserve">
915+
<value>Command APDU size ({0} bytes) exceeds maximum size ({1} bytes) supported by the YubiKey</value>
916+
</data>
913917
</root>

Yubico.YubiKey/src/Yubico/YubiKey/ConnectionFactory.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ public ConnectionFactory(
6262
[Obsolete("Obsolete")]
6363
internal IScp03YubiKeyConnection CreateScpConnection(YubiKeyApplication application, Scp03.StaticKeys scp03Keys)
6464
{
65-
6665
if (_smartCardDevice is null)
6766
{
68-
throw new InvalidOperationException("No smart card interface present. Unable to establish SCP connection to YubiKey.");
67+
throw new InvalidOperationException(
68+
"No smart card interface present. Unable to establish SCP connection to YubiKey.");
6969
}
7070

7171
_log.LogDebug("Connecting via the SmartCard interface using SCP03.");
@@ -79,38 +79,44 @@ internal IScp03YubiKeyConnection CreateScpConnection(YubiKeyApplication applicat
7979
/// </summary>
8080
/// <param name="application">The YubiKey application to connect to.</param>
8181
/// <param name="keyParameters">The security parameters for establishing the SCP connection.</param>
82+
/// <param name="firmwareVersion">The firmware version of the YubiKey.q</param>
8283
/// <returns>A secure connection to the specified YubiKey application.</returns>
8384
/// <exception cref="InvalidOperationException">Thrown when the SmartCard interface is not available on the YubiKey.</exception>
8485
/// <remarks>
8586
/// This method establishes a secure channel to the YubiKey using the SmartCard interface. The connection
8687
/// is protected using the Secure Channel Protocol (SCP) with the provided key parameters.
8788
/// </remarks>
88-
public IScpYubiKeyConnection CreateScpConnection(YubiKeyApplication application, ScpKeyParameters keyParameters)
89+
public IScpYubiKeyConnection CreateScpConnection(
90+
YubiKeyApplication application,
91+
ScpKeyParameters keyParameters,
92+
FirmwareVersion firmwareVersion)
8993
{
9094
LogConnectionAttempt(application, keyParameters);
9195

9296
if (_smartCardDevice is null)
9397
{
94-
throw new InvalidOperationException("No smart card interface present. Unable to establish SCP connection to YubiKey.");
98+
throw new InvalidOperationException(
99+
"No smart card interface present. Unable to establish SCP connection to YubiKey.");
95100
}
96101

97102
_log.LogDebug("Connecting via the SmartCard interface using SCP.");
98103
WaitForReclaimTimeout(Transport.SmartCard);
99104

100-
return new ScpConnection(_smartCardDevice, application, keyParameters);
105+
return new ScpConnection(_smartCardDevice, application, keyParameters, firmwareVersion);
101106
}
102107

103108
/// <summary>
104109
/// Creates a standard (non-SCP) connection to a specific YubiKey application.
105110
/// </summary>
106111
/// <param name="application">The YubiKey application to connect to.</param>
112+
/// <param name="firmwareVersion">The firmware version of the YubiKey.</param>
107113
/// <returns>A connection to the specified YubiKey application.</returns>
108114
/// <exception cref="InvalidOperationException">Thrown when no suitable interface is available for the requested application.</exception>
109115
/// <remarks>
110116
/// This method creates a connection using the most appropriate interface available for the specified application.
111117
/// It first attempts to use the SmartCard interface, then falls back to HID interfaces if necessary.
112118
/// </remarks>
113-
public IYubiKeyConnection CreateConnection(YubiKeyApplication application)
119+
public IYubiKeyConnection CreateConnection(YubiKeyApplication application, FirmwareVersion firmwareVersion)
114120
{
115121
if (_hidKeyboardDevice != null && application == YubiKeyApplication.Otp)
116122
{
@@ -120,7 +126,9 @@ public IYubiKeyConnection CreateConnection(YubiKeyApplication application)
120126
return new KeyboardConnection(_hidKeyboardDevice);
121127
}
122128

123-
bool isFidoApplication = application == YubiKeyApplication.Fido2 || application == YubiKeyApplication.FidoU2f;
129+
bool isFidoApplication =
130+
application == YubiKeyApplication.Fido2 || application == YubiKeyApplication.FidoU2f;
131+
124132
if (_hidFidoDevice != null && isFidoApplication)
125133
{
126134
_log.LogDebug("Connecting via the FIDO interface.");
@@ -134,10 +142,11 @@ public IYubiKeyConnection CreateConnection(YubiKeyApplication application)
134142
_log.LogDebug("Connecting via the SmartCard interface.");
135143

136144
WaitForReclaimTimeout(Transport.SmartCard);
137-
return new SmartCardConnection(_smartCardDevice, application);
145+
return new SmartCardConnection(_smartCardDevice, application, firmwareVersion);
138146
}
139147

140-
throw new InvalidOperationException("No suitable interface present. Unable to establish connection to YubiKey.");
148+
throw new InvalidOperationException(
149+
"No suitable interface present. Unable to establish connection to YubiKey.");
141150
}
142151

143152
// This function handles waiting for the reclaim timeout on the YubiKey to elapse. The reclaim timeout requires

Yubico.YubiKey/src/Yubico/YubiKey/ConnectionManager.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,13 @@ namespace Yubico.YubiKey
4040
/// Connecting to different YubiKeys at once is fine, just not to a single key.
4141
/// </para>
4242
/// </remarks>
43+
4344
// JUSTIFICATION: This class is a singleton, which means its lifetime will span the process lifetime. It contains
4445
// a lock which is disposable, so we must call its Dispose method at some point. The only reasonable time to do that
4546
// is in this class's finalizer. This analyzer doesn't seem to see this and still warns.
46-
#pragma warning disable CA1001
47+
#pragma warning disable CA1001
4748
internal class ConnectionManager
48-
#pragma warning restore CA1001
49+
#pragma warning restore CA1001
4950
{
5051
// Easy thread-safe singleton pattern using Lazy<>
5152
private static readonly Lazy<ConnectionManager> _instance =
@@ -62,15 +63,18 @@ internal class ConnectionManager
6263
/// <param name="device">A concrete device object from Yubico.Core.</param>
6364
/// <param name="application">An application of the YubiKey.</param>
6465
/// <returns>`true` if the device object supports the application, `false` otherwise.</returns>
66+
6567
// This function uses C# 8.0 pattern matching to build a concise table.
6668
public static bool DeviceSupportsApplication(IDevice device, YubiKeyApplication application) =>
6769
(device, application) switch
6870
{
6971
// FIDO interface
7072
(IHidDevice { UsagePage: HidUsagePage.Fido }, YubiKeyApplication.FidoU2f) => true,
7173
(IHidDevice { UsagePage: HidUsagePage.Fido }, YubiKeyApplication.Fido2) => true,
74+
7275
// Keyboard interface
7376
(IHidDevice { UsagePage: HidUsagePage.Keyboard }, YubiKeyApplication.Otp) => true,
77+
7478
// All Smart Card based interfaces
7579
(ISmartCardDevice _, YubiKeyApplication.Management) => true,
7680
(ISmartCardDevice _, YubiKeyApplication.Oath) => true,
@@ -79,10 +83,12 @@ public static bool DeviceSupportsApplication(IDevice device, YubiKeyApplication
7983
(ISmartCardDevice _, YubiKeyApplication.InterIndustry) => true,
8084
(ISmartCardDevice _, YubiKeyApplication.SecurityDomain) => true,
8185
(ISmartCardDevice _, YubiKeyApplication.YubiHsmAuth) => true,
86+
8287
// NB: Certain past models of YK NEO and YK 4 supported these applications over CCID
8388
(ISmartCardDevice _, YubiKeyApplication.FidoU2f) => true,
8489
(ISmartCardDevice _, YubiKeyApplication.Fido2) => true,
8590
(ISmartCardDevice _, YubiKeyApplication.Otp) => true,
91+
8692
// NFC interface
8793
(ISmartCardDevice { Kind: SmartCardConnectionKind.Nfc }, YubiKeyApplication.OtpNdef) => true,
8894
_ => false
@@ -133,8 +139,7 @@ public bool TryCreateConnection(
133139
IYubiKeyDevice yubiKeyDevice,
134140
IDevice device,
135141
YubiKeyApplication application,
136-
[MaybeNullWhen(returnValue: false)]
137-
out IYubiKeyConnection connection)
142+
[MaybeNullWhen(returnValue: false)] out IYubiKeyConnection connection)
138143
{
139144
if (!DeviceSupportsApplication(device, application))
140145
{
@@ -180,7 +185,7 @@ public bool TryCreateConnection(
180185
{
181186
IHidDevice { UsagePage: HidUsagePage.Fido } d => new FidoConnection(d),
182187
IHidDevice { UsagePage: HidUsagePage.Keyboard } d => new KeyboardConnection(d),
183-
ISmartCardDevice d => new SmartCardConnection(d, application),
188+
ISmartCardDevice d => new SmartCardConnection(d, application, yubiKeyDevice.FirmwareVersion),
184189
_ => throw new NotSupportedException(ExceptionMessages.DeviceTypeNotRecognized)
185190
};
186191

@@ -222,12 +227,11 @@ public bool TryCreateConnection(
222227
IYubiKeyDevice yubiKeyDevice,
223228
IDevice device,
224229
byte[] applicationId,
225-
[MaybeNullWhen(returnValue: false)]
226-
out IYubiKeyConnection connection)
230+
[MaybeNullWhen(returnValue: false)] out IYubiKeyConnection connection)
227231
{
228232
var smartCardDevice = device as ISmartCardDevice ?? throw new ArgumentException(
229-
ExceptionMessages.DeviceDoesNotSupportApplication,
230-
nameof(applicationId));
233+
ExceptionMessages.DeviceDoesNotSupportApplication,
234+
nameof(applicationId));
231235

232236
// Since taking a write lock is potentially very expensive, let's try and make a best effort to see if the
233237
// YubiKey is already present. This way we can fail fast.
@@ -262,7 +266,7 @@ public bool TryCreateConnection(
262266
return false;
263267
}
264268

265-
connection = new SmartCardConnection(smartCardDevice, applicationId);
269+
connection = new SmartCardConnection(smartCardDevice, applicationId, yubiKeyDevice.FirmwareVersion);
266270
_ = _openConnections.Add(yubiKeyDevice);
267271
}
268272
finally

Yubico.YubiKey/src/Yubico/YubiKey/Pipelines/SmartCardTransform.cs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using System;
16+
using System.Globalization;
1617
using Yubico.Core.Devices.SmartCard;
1718
using Yubico.Core.Iso7816;
1819

@@ -24,28 +25,59 @@ namespace Yubico.YubiKey.Pipelines
2425
/// </summary>
2526
internal class SmartCardTransform : IApduTransform
2627
{
27-
readonly ISmartCardConnection _smartCardConnection;
28+
private readonly ISmartCardConnection _smartCardConnection;
29+
private readonly int _maxApduSizeBytes;
2830

29-
public SmartCardTransform(ISmartCardConnection smartCardConnection)
31+
public SmartCardTransform(
32+
ISmartCardConnection smartCardConnection,
33+
FirmwareVersion? firmwareVersion = null)
3034
{
31-
if (smartCardConnection is null)
32-
{
33-
throw new ArgumentNullException(nameof(smartCardConnection));
34-
}
35-
36-
_smartCardConnection = smartCardConnection;
35+
_smartCardConnection = smartCardConnection ?? throw new ArgumentNullException(nameof(smartCardConnection));
36+
_maxApduSizeBytes = GetMaxApduSize(firmwareVersion);
3737
}
3838

3939
public void Cleanup()
4040
{
41-
41+
// No cleanup needed
4242
}
4343

44-
public ResponseApdu Invoke(CommandApdu command, Type commandType, Type responseType) => _smartCardConnection.Transmit(command);
44+
public ResponseApdu Invoke(CommandApdu command, Type commandType, Type responseType)
45+
{
46+
// Verify APDU size is within limits before sending
47+
byte[] apduBytes = command.AsByteArray();
48+
if (apduBytes.Length > _maxApduSizeBytes)
49+
{
50+
throw new InvalidOperationException(
51+
string.Format(
52+
CultureInfo.CurrentCulture,
53+
ExceptionMessages.CommandApduTooLarge,
54+
apduBytes.Length,
55+
_maxApduSizeBytes)
56+
);
57+
}
58+
59+
return _smartCardConnection.Transmit(command);
60+
}
4561

4662
public void Setup()
4763
{
48-
64+
// No setup needed
65+
}
66+
67+
private static int GetMaxApduSize(FirmwareVersion? fwVersion)
68+
{
69+
if (fwVersion is null)
70+
{
71+
return (int)SmartCardMaxApduSizes.NEO;
72+
}
73+
74+
var maxApduSize = fwVersion >= FirmwareVersion.V4_3_0
75+
? SmartCardMaxApduSizes.YK4_3
76+
: fwVersion >= FirmwareVersion.V4_0_0
77+
? SmartCardMaxApduSizes.YK4
78+
: SmartCardMaxApduSizes.NEO;
79+
80+
return (int)maxApduSize;
4981
}
5082
}
5183
}

Yubico.YubiKey/src/Yubico/YubiKey/Scp/ScpConnection.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ internal class ScpConnection : SmartCardConnection, IScpYubiKeyConnection
2929
public ScpConnection(
3030
ISmartCardDevice smartCardDevice,
3131
YubiKeyApplication application,
32-
ScpKeyParameters keyParameters)
33-
: base(smartCardDevice, application, null)
32+
ScpKeyParameters keyParameters,
33+
FirmwareVersion firmwareVersion
34+
)
35+
: base(smartCardDevice, application, firmwareVersion)
3436
{
3537
var scpPipeline = CreateScpPipeline(keyParameters);
3638
var withErrorHandling = CreateParentPipeline(scpPipeline, application);

Yubico.YubiKey/src/Yubico/YubiKey/Scp03/Scp03Connection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ public Scp03Connection(
3333
ISmartCardDevice smartCardDevice,
3434
YubiKeyApplication yubiKeyApplication,
3535
Scp03.StaticKeys scp03Keys)
36-
: base(smartCardDevice, yubiKeyApplication, null)
36+
: base(smartCardDevice, yubiKeyApplication)
3737
{
3838
_scp03ApduTransform = SetObject(yubiKeyApplication, scp03Keys);
3939
}
4040

4141
public Scp03Connection(ISmartCardDevice smartCardDevice, byte[] applicationId, Scp03.StaticKeys scp03Keys)
42-
: base(smartCardDevice, YubiKeyApplication.Unknown, applicationId)
42+
: base(smartCardDevice, YubiKeyApplication.Unknown, applicationId, null)
4343
{
4444
var setError = YubiKeyApplication.Unknown;
4545
if (applicationId.SequenceEqual(YubiKeyApplication.Fido2.GetIso7816ApplicationId()))

0 commit comments

Comments
 (0)