Skip to content
31 changes: 29 additions & 2 deletions Yubico.YubiKey/src/Yubico/YubiKey/Fido2/Fido2Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Microsoft.Extensions.Logging;
using Yubico.Core.Logging;
using Yubico.YubiKey.Fido2.Commands;
using Yubico.YubiKey.Scp;

namespace Yubico.YubiKey.Fido2
{
Expand Down Expand Up @@ -218,6 +219,7 @@ public ReadOnlyMemory<byte>? AuthenticatorCredStoreState
/// YubiKey.
/// </summary>
/// <remarks>
/// <para>
/// Because this class implements <c>IDisposable</c>, use the <c>using</c> keyword. For example,
/// <code language="csharp">
/// IYubiKeyDevice yubiKeyToUse = SelectYubiKey();
Expand All @@ -226,17 +228,42 @@ public ReadOnlyMemory<byte>? AuthenticatorCredStoreState
/// /* Perform FIDO2 operations. */
/// }
/// </code>
/// </para>
/// <para>
/// To establish an SCP-protected FIDO2 session:
/// <code language="csharp">
/// using (var fido2 = new Fido2Session(yubiKeyToUse, Scp03KeyParameters.DefaultKey))
Comment thread
DennisDyallo marked this conversation as resolved.
Outdated
/// {
/// /* All FIDO2 commands are encrypted via SCP. */
/// }
/// </code>
/// </para>
/// <para>
/// <b>Important:</b> FIDO2 over SCP requires an NFC connection. Over USB, FIDO2 communicates
/// via the HID interface, which does not support SCP (a SmartCard-layer protocol). Over NFC,
/// all communication uses the SmartCard protocol, so both FIDO2 and SCP are available on the
/// same interface.
/// </para>
/// </remarks>
/// <param name="yubiKey">
/// The object that represents the actual YubiKey on which the FIDO2 operations should be performed.
/// </param>
/// <param name="keyParameters">
/// Optional parameters for establishing a Secure Channel Protocol (SCP) connection.
/// When provided, all communication with the YubiKey will be encrypted and authenticated
/// using the specified SCP protocol (e.g., SCP03 or SCP11). Requires an NFC connection —
/// FIDO2 over SCP is not supported over USB.
/// </param>
/// <param name="persistentPinUvAuthToken">If supplied, will be used for credential management read-only operations
/// </param>
/// <exception cref="ArgumentNullException">
/// The <paramref name="yubiKey"/> argument is <c>null</c>.
/// </exception>
public Fido2Session(IYubiKeyDevice yubiKey, ReadOnlyMemory<byte>? persistentPinUvAuthToken = null)
: base(Log.GetLogger<Fido2Session>(), yubiKey, YubiKeyApplication.Fido2, keyParameters: null)
public Fido2Session(
IYubiKeyDevice yubiKey,
Comment thread
DennisDyallo marked this conversation as resolved.
ScpKeyParameters? keyParameters = null,
ReadOnlyMemory<byte>? persistentPinUvAuthToken = null)
: base(Log.GetLogger<Fido2Session>(), yubiKey, YubiKeyApplication.Fido2, keyParameters)
{
Guard.IsNotNull(yubiKey, nameof(yubiKey));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ public static bool HasFeature(this IYubiKeyDevice yubiKeyDevice, YubiKeyFeature
|| HasApplication(yubiKeyDevice, YubiKeyCapabilities.Oath)
|| HasApplication(yubiKeyDevice, YubiKeyCapabilities.OpenPgp)
|| HasApplication(yubiKeyDevice, YubiKeyCapabilities.Otp)
|| HasApplication(yubiKeyDevice, YubiKeyCapabilities.YubiHsmAuth)),
|| HasApplication(yubiKeyDevice, YubiKeyCapabilities.YubiHsmAuth)
|| HasApplication(yubiKeyDevice, YubiKeyCapabilities.Fido2)),
Comment thread
DennisDyallo marked this conversation as resolved.

YubiKeyFeature.Scp03Oath =>
yubiKeyDevice.FirmwareVersion >= FirmwareVersion.V5_6_3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ protected Fido2Session GetSession(
{
session = persistentPinUvAuthToken is null
? new Fido2Session(testDevice)
: new Fido2Session(testDevice, persistentPinUvAuthToken);
: new Fido2Session(testDevice, persistentPinUvAuthToken: persistentPinUvAuthToken);

session.KeyCollector = KeyCollector.HandleRequest;

Expand Down
27 changes: 27 additions & 0 deletions Yubico.YubiKey/tests/integration/Yubico/YubiKey/Scp/Scp03Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Xunit;
using Yubico.Core.Tlv;
using Yubico.YubiKey.Cryptography;
using Yubico.YubiKey.Fido2;
using Yubico.YubiKey.Piv;
using Yubico.YubiKey.Piv.Commands;
using Yubico.YubiKey.Scp03;
Expand Down Expand Up @@ -404,6 +405,32 @@ public void Scp03_PivSession_TryVerifyPinAndGetMetaData_Succeeds(
}


[SkippableTheory(typeof(DeviceNotFoundException))]
[InlineData(StandardTestDevice.Fw5, Transport.NfcSmartCard)]
[InlineData(StandardTestDevice.Fw5Fips, Transport.NfcSmartCard)]
Comment thread
DennisDyallo marked this conversation as resolved.
Outdated
public void Scp03_Fido2Session_GetAuthenticatorInfo_Succeeds(
StandardTestDevice desiredDeviceType,
Transport transport)
{
var testDevice = GetDevice(desiredDeviceType, transport);
Assert.True(testDevice.FirmwareVersion >= FirmwareVersion.V5_3_0);
Assert.True(testDevice.HasFeature(YubiKeyFeature.Scp03));

// FIDO2 over SCP requires NFC (SmartCard protocol). Over USB, FIDO2 uses HID
// and the FIDO2 AID is not selectable over CCID on most YubiKey devices.
// This matches the Android SDK behavior where FIDO2+SCP tests run over NFC.
Skip.IfNot(
testDevice.AvailableUsbCapabilities.HasFlag(YubiKeyCapabilities.Fido2)
|| transport == Transport.NfcSmartCard,
Comment thread
DennisDyallo marked this conversation as resolved.
Outdated
"FIDO2 is not available over SmartCard on this device");

using var fido2Session = new Fido2Session(testDevice, Scp03KeyParameters.DefaultKey);

var info = fido2Session.AuthenticatorInfo;
Assert.NotNull(info);
Assert.NotEmpty(info.Versions);
}

[SkippableTheory(typeof(DeviceNotFoundException))]
[InlineData(StandardTestDevice.Fw5, Transport.UsbSmartCard)]
[InlineData(StandardTestDevice.Fw5Fips, Transport.UsbSmartCard)]
Expand Down
25 changes: 25 additions & 0 deletions Yubico.YubiKey/tests/integration/Yubico/YubiKey/Scp/Scp11Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
using Yubico.Core.Devices.Hid;
using Yubico.Core.Tlv;
using Yubico.YubiKey.Cryptography;
using Yubico.YubiKey.Fido2;
using Yubico.YubiKey.Oath;
using Yubico.YubiKey.Otp;
using Yubico.YubiKey.Piv;
Expand Down Expand Up @@ -131,6 +132,30 @@ public void Scp11b_App_OtpSession_Operations_Succeeds(
configObj.Execute();
}

[SkippableTheory(typeof(DeviceNotFoundException))]
[InlineData(StandardTestDevice.Fw5)]
[InlineData(StandardTestDevice.Fw5Fips)]
public void Scp11b_App_Fido2Session_GetAuthenticatorInfo_Succeeds(
StandardTestDevice desiredDeviceType)
{
var testDevice = GetDevice(desiredDeviceType);

// FIDO2 over SCP requires SmartCard protocol (NFC). Over USB, FIDO2 uses HID
// and the FIDO2 AID is not selectable over CCID on most YubiKey devices.
Skip.IfNot(
testDevice.AvailableUsbCapabilities.HasFlag(YubiKeyCapabilities.Fido2),
"FIDO2 is not available over SmartCard on this device");

var keyReference = new KeyReference(ScpKeyIds.Scp11B, 0x1);
var keyParams = Get_Scp11b_SecureConnection_Parameters(testDevice, keyReference);

using var session = new Fido2Session(testDevice, keyParams);
Comment thread
DennisDyallo marked this conversation as resolved.
Outdated

var info = session.AuthenticatorInfo;
Assert.NotNull(info);
Assert.NotEmpty(info.Versions);
}

[SkippableTheory(typeof(DeviceNotFoundException))]
[InlineData(StandardTestDevice.Fw5)]
[InlineData(StandardTestDevice.Fw5Fips)]
Expand Down
Loading