From 643d0a374a8021e6f05f258295e12ffe110fd00e Mon Sep 17 00:00:00 2001 From: Dennis Dyallo Date: Thu, 7 Nov 2024 01:25:51 +0100 Subject: [PATCH 01/12] Update auth-decrypt.md --- .../docs/users-manual/application-piv/apdu/auth-decrypt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Yubico.YubiKey/docs/users-manual/application-piv/apdu/auth-decrypt.md b/Yubico.YubiKey/docs/users-manual/application-piv/apdu/auth-decrypt.md index 6d0e2bc33..571ed69ff 100644 --- a/Yubico.YubiKey/docs/users-manual/application-piv/apdu/auth-decrypt.md +++ b/Yubico.YubiKey/docs/users-manual/application-piv/apdu/auth-decrypt.md @@ -20,7 +20,7 @@ limitations under the License. --> |:---:|:---:|:-----------:|:-------------:|:----------:|:-------------------------:|:--------:| | 00 | 87 | *algorithm* | *slot number* | *data len* | *encoded data to decrypt* | (absent) | -The *algorithm* is either `06` (RSA-1048), `07` (RSA-2048), `05` (RSA 3072), or `16` (RSA 4096). Note that it is not possible +The *algorithm* is either `06` (RSA-1024), `07` (RSA-2048), `05` (RSA 3072), or `16` (RSA 4096). Note that it is not possible to decrypt using ECC. The *slot number* can be the number of any slot that holds a private key, other than `F9`. From 5b0650fc8dc8f4c7c64c158d380cfe068692bb9d Mon Sep 17 00:00:00 2001 From: Dennis Dyallo Date: Thu, 7 Nov 2024 01:26:21 +0100 Subject: [PATCH 02/12] Update auth-sign.md --- .../docs/users-manual/application-piv/apdu/auth-sign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Yubico.YubiKey/docs/users-manual/application-piv/apdu/auth-sign.md b/Yubico.YubiKey/docs/users-manual/application-piv/apdu/auth-sign.md index 2851caa9d..d13e61a58 100644 --- a/Yubico.YubiKey/docs/users-manual/application-piv/apdu/auth-sign.md +++ b/Yubico.YubiKey/docs/users-manual/application-piv/apdu/auth-sign.md @@ -20,7 +20,7 @@ limitations under the License. --> |:---:|:---:|:-----------:|:-------------:|:----------:|:--------------------------------:|:--------:| | 00 | 87 | *algorithm* | *slot number* | *data len* | *encoded digest of data to sign* | (absent) | -The *algorithm* is either `06` (RSA-1048), `07` (RSA-2048), `05` (RSA 3072), `16` (RSA 4096), `11` (ECC-P256), or `14` +The *algorithm* is either `06` (RSA-1024), `07` (RSA-2048), `05` (RSA 3072), `16` (RSA 4096), `11` (ECC-P256), or `14` (ECC-P384). The *slot number* can be the number of any slot that holds a private key, other than `F9`. From 57d8c3ff99581b42ac75e2de693885a11733babb Mon Sep 17 00:00:00 2001 From: Dennis Dyallo Date: Thu, 7 Nov 2024 01:41:54 +0100 Subject: [PATCH 03/12] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 45bcab0ab..26e657309 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ This is a cross-platform, all encompassing SDK for the YubiKey aimed at large to customers. This version is written against .NET Core, and will eventually include bindings to languages outside the direct .NET ecosystem. +## SDK Support +The SDK is targetting net47, netstandard2.0 and netstandard2.1. This means the SDK can be loaded in NET Framework, NET6 and upwards. + ## Documentation The public documentation for this project is located From f70d97c1fa159abd4ca3e6de386f81ae5fb4f2d6 Mon Sep 17 00:00:00 2001 From: Aaron Fortner Date: Wed, 6 Nov 2024 17:29:12 -0800 Subject: [PATCH 04/12] Set IsSkySeries on the combined info object in YubiKeyDeviceInfo.Merge --- Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs b/Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs index 2ed341faf..a09979ba0 100644 --- a/Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs +++ b/Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs @@ -299,6 +299,7 @@ internal YubiKeyDeviceInfo Merge(YubiKeyDeviceInfo? second) ResetBlocked = ResetBlocked | second.ResetBlocked, SerialNumber = SerialNumber ?? second.SerialNumber, IsFipsSeries = IsFipsSeries || second.IsFipsSeries, + IsSkySeries = IsSkySeries || second.IsSkySeries, FormFactor = FormFactor != FormFactor.Unknown ? FormFactor From b6d9cbf91d07a1435c19f404a9d7a23abc569288 Mon Sep 17 00:00:00 2001 From: Aaron Fortner Date: Thu, 7 Nov 2024 10:03:02 -0800 Subject: [PATCH 05/12] Add YubiKeyDeviceListener test for IsSkySeries merge fix --- .../YubiKey/YubiKeyDeviceListenerTests.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Yubico.YubiKey/tests/integration/Yubico/YubiKey/YubiKeyDeviceListenerTests.cs diff --git a/Yubico.YubiKey/tests/integration/Yubico/YubiKey/YubiKeyDeviceListenerTests.cs b/Yubico.YubiKey/tests/integration/Yubico/YubiKey/YubiKeyDeviceListenerTests.cs new file mode 100644 index 000000000..6e5c049f7 --- /dev/null +++ b/Yubico.YubiKey/tests/integration/Yubico/YubiKey/YubiKeyDeviceListenerTests.cs @@ -0,0 +1,58 @@ +// Copyright 2024 Yubico AB +// +// Licensed under the Apache License, Version 2.0 (the "License"). +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading; +using Xunit; +using Yubico.PlatformInterop; +using Yubico.YubiKey.TestUtilities; + +namespace Yubico.YubiKey +{ + [Trait(TraitTypes.Category, TestCategories.Elevated)] + public class YubiKeyDeviceListenerTests + { + private IYubiKeyDevice WaitForDevice() + { + IYubiKeyDevice? device = null; + + AutoResetEvent reset = new AutoResetEvent(false); + EventHandler handler = (sender, args) => + { + device = args.Device; + reset.Set(); + }; + + YubiKeyDeviceListener.Instance.Arrived += handler; + reset.WaitOne(); + YubiKeyDeviceListener.Instance.Arrived -= handler; + + Assert.NotNull(device); + return device; + } + + [Fact] + public void KeyArrived_SkyEe_IsSkySeriesIsTrue() + { + // Needs to run elevated so the listener finds and enumerates any hidFido + // devices else no Merge will happen, and it won't be a valid test + // See https://github.com/Yubico/Yubico.NET.SDK/issues/156 + Assert.True(SdkPlatformInfo.IsElevated); + + IYubiKeyDevice device = WaitForDevice(); + + Assert.True(device.IsSkySeries); + } + } +} From c3230312fe44dceffe4e2add9c497139133ce001 Mon Sep 17 00:00:00 2001 From: Dennis Dyall Date: Thu, 14 Nov 2024 16:20:24 +0100 Subject: [PATCH 06/12] Fixes #160. Refactor PivSession and update tests for management key algorithm handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update management key algorithm refresh • Add FIPS-specific test cases • Enhance test coverage for key changes • Adjust for firmware version differences --- .../YubiKey/Piv/PivSession.ManagementKey.cs | 141 ++++++++++-------- .../src/Yubico/YubiKey/Piv/PivSession.cs | 27 +--- .../Yubico/YubiKey/Piv/ManagementKeyTests.cs | 88 ++++++++--- 3 files changed, 151 insertions(+), 105 deletions(-) diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs b/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs index 12a7a2071..cf2e29d95 100644 --- a/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs +++ b/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs @@ -71,6 +71,12 @@ public sealed partial class PivSession : IDisposable /// public AuthenticateManagementKeyResult ManagementKeyAuthenticationResult { get; private set; } + private PivAlgorithm DefaultManagementKeyAlgorithm => + _yubiKeyDevice.HasFeature(YubiKeyFeature.PivAesManagementKey) && + _yubiKeyDevice.FirmwareVersion > FirmwareVersion.V5_7_0 + ? PivAlgorithm.Aes192 + : PivAlgorithm.TripleDes; + /// /// Try to authenticate the management key. /// @@ -531,7 +537,7 @@ public bool TryAuthenticateManagementKey(ReadOnlyMemory managementKey, boo /// authenticated. /// public bool TryChangeManagementKey(PivTouchPolicy touchPolicy = PivTouchPolicy.Default) => - TryChangeManagementKey(touchPolicy, PivAlgorithm.TripleDes); + TryChangeManagementKey(touchPolicy, DefaultManagementKeyAlgorithm); /// /// Try to change the management key. The new key will be the specified @@ -651,7 +657,8 @@ public bool TryChangeManagementKey(PivTouchPolicy touchPolicy = PivTouchPolicy.D /// public bool TryChangeManagementKey(PivTouchPolicy touchPolicy, PivAlgorithm newKeyAlgorithm) { - _log.LogInformation("Try to change the management key, touch policy = {TouchPolicy}, algorithm = {PivALgorithm}.", + _log.LogInformation( + "Try to change the management key, touch policy = {TouchPolicy}, algorithm = {PivALgorithm}.", touchPolicy.ToString(), newKeyAlgorithm.ToString()); CheckManagementKeyAlgorithm(newKeyAlgorithm, true); @@ -726,7 +733,7 @@ public bool TryChangeManagementKey(PivTouchPolicy touchPolicy, PivAlgorithm newK /// authenticated. /// public void ChangeManagementKey(PivTouchPolicy touchPolicy = PivTouchPolicy.Default) => - ChangeManagementKey(touchPolicy, PivAlgorithm.TripleDes); + ChangeManagementKey(touchPolicy, DefaultManagementKeyAlgorithm); /// /// Change the management key, throw an exception if the user cancels. @@ -761,7 +768,8 @@ public void ChangeManagementKey(PivTouchPolicy touchPolicy = PivTouchPolicy.Defa /// public void ChangeManagementKey(PivTouchPolicy touchPolicy, PivAlgorithm newKeyAlgorithm) { - _log.LogInformation("Change the management key, touch policy = {TouchPolicy}, algorithm = {PivAlgorithm}.", + _log.LogInformation( + "Change the management key, touch policy = {TouchPolicy}, algorithm = {PivAlgorithm}.", touchPolicy.ToString(), newKeyAlgorithm.ToString()); if (TryChangeManagementKey(touchPolicy, newKeyAlgorithm) == false) @@ -827,7 +835,7 @@ public void ChangeManagementKey(PivTouchPolicy touchPolicy, PivAlgorithm newKeyA public bool TryChangeManagementKey(ReadOnlyMemory currentKey, ReadOnlyMemory newKey, PivTouchPolicy touchPolicy = PivTouchPolicy.Default) => - TryChangeManagementKey(currentKey, newKey, touchPolicy, PivAlgorithm.TripleDes); + TryChangeManagementKey(currentKey, newKey, touchPolicy, DefaultManagementKeyAlgorithm); /// /// Try to change the management key. This method will use the @@ -915,68 +923,11 @@ private bool TryForcedChangeManagementKey(ReadOnlyMemory currentKey, } _log.LogInformation($"Failed to set management key. Message: {response.StatusMessage}"); - } return false; } - // Verify that and that the given algorithm is allowed. - // If checkMode is true, also check that the PIN-only mode is None. - // This is called by methods that set PIN-only mode or change the mgmt - // key. - // The algorithm can only be 3DES or AES, and it can only be AES if the - // YubiKey is 5.4.2 or later. - // It is not allowed to change the mgmt key if it is PIN-only, so those - // methods that change, will check the mode as well (they will pass true - // as the checkMode arg). - // If setting PIN-only, then the mode is not an issue, so don't check - // (pass false as the checkMode arg). - // If everything is fine, return, otherwise throw an exception. - private void CheckManagementKeyAlgorithm(PivAlgorithm algorithm, bool checkMode) - { - if (checkMode) - { - var pinOnlyMode = GetPinOnlyMode(); - if (pinOnlyMode.HasFlag(PivPinOnlyMode.PinProtected) || - pinOnlyMode.HasFlag(PivPinOnlyMode.PinDerived)) - { - throw new InvalidOperationException( - string.Format( - CultureInfo.CurrentCulture, - ExceptionMessages.MgmtKeyCannotBeChanged)); - } - } - - bool isValid = false; - - switch (algorithm) - { - case PivAlgorithm.TripleDes: - isValid = true; - - break; - - case PivAlgorithm.Aes128: - case PivAlgorithm.Aes192: - case PivAlgorithm.Aes256: - isValid = _yubiKeyDevice.HasFeature(YubiKeyFeature.PivAesManagementKey); - - break; - - default: - break; - } - - if (!isValid) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - ExceptionMessages.UnsupportedAlgorithm)); - } - } - // This is the actual Try code, shared by both TryAuth and TryChange. // The caller provides a KeyEntryData object set with the appropriate // request: @@ -1046,7 +997,8 @@ private bool TryAuthenticateManagementKey(bool mutualAuthentication, // off-card app authenticated, but the YubiKey itself did // not. // If case (3), throw an exception. - if (ManagementKeyAuthenticationResult == AuthenticateManagementKeyResult.MutualYubiKeyAuthenticationFailed) + if (ManagementKeyAuthenticationResult == + AuthenticateManagementKeyResult.MutualYubiKeyAuthenticationFailed) { throw new SecurityException( string.Format( @@ -1061,5 +1013,68 @@ private bool TryAuthenticateManagementKey(bool mutualAuthentication, return ManagementKeyAuthenticated; } + + private void RefreshManagementKeyAlgorithm() => ManagementKeyAlgorithm = GetManagementKeyAlgorithm(); + + private PivAlgorithm GetManagementKeyAlgorithm() + { + var response = Connection.SendCommand(new GetMetadataCommand(PivSlot.Management)); + if (response.Status != ResponseStatus.Success) + { + throw new InvalidOperationException(response.StatusMessage); + } + + var metadata = response.GetData(); + return metadata.Algorithm; + } + + // Verify that and that the given algorithm is allowed. + // If checkMode is true, also check that the PIN-only mode is None. + // This is called by methods that set PIN-only mode or change the mgmt + // key. + // The algorithm can only be 3DES or AES, and it can only be AES if the + // YubiKey is 5.4.2 or later. + // It is not allowed to change the mgmt key if it is PIN-only, so those + // methods that change, will check the mode as well (they will pass true + // as the checkMode arg). + // If setting PIN-only, then the mode is not an issue, so don't check + // (pass false as the checkMode arg). + // If everything is fine, return, otherwise throw an exception. + private void CheckManagementKeyAlgorithm(PivAlgorithm algorithm, bool checkMode) + { + if (checkMode) + { + var pinOnlyMode = GetPinOnlyMode(); + if (pinOnlyMode.HasFlag(PivPinOnlyMode.PinProtected) || + pinOnlyMode.HasFlag(PivPinOnlyMode.PinDerived)) + { + throw new InvalidOperationException( + string.Format( + CultureInfo.CurrentCulture, + ExceptionMessages.MgmtKeyCannotBeChanged)); + } + } + + bool isValid = IsValid(algorithm); + if (!isValid) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentCulture, + ExceptionMessages.UnsupportedAlgorithm)); + } + + return; + + bool IsValid(PivAlgorithm pa) => + pa switch + { + PivAlgorithm.TripleDes => true, // Default for keys below fw version 5.7 + PivAlgorithm.Aes128 => _yubiKeyDevice.HasFeature(YubiKeyFeature.PivAesManagementKey), + PivAlgorithm.Aes192 => _yubiKeyDevice.HasFeature(YubiKeyFeature.PivAesManagementKey), + PivAlgorithm.Aes256 => _yubiKeyDevice.HasFeature(YubiKeyFeature.PivAesManagementKey), + _ => false + }; + } } } diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs b/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs index 43c664c2e..1f7fa198c 100644 --- a/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs +++ b/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs @@ -249,7 +249,7 @@ private PivSession(StaticKeys? scp03Keys, IYubiKeyDevice yubiKey) : yubiKey.ConnectScp03(YubiKeyApplication.Piv, scp03Keys); ResetAuthenticationStatus(); - UpdateManagementKey(yubiKey); + RefreshManagementKeyAlgorithm(); _yubiKeyDevice = yubiKey; _disposed = false; @@ -313,14 +313,14 @@ public void Dispose() { _ = Connection.SendCommand(new SelectApplicationCommand(YubiKeyApplication.Management)); } -#pragma warning disable CA1031 + #pragma warning disable CA1031 catch (Exception e) -#pragma warning restore CA1031 + #pragma warning restore CA1031 { string message = string.Format( CultureInfo.CurrentCulture, ExceptionMessages.PivSessionDisposeUnknownError, e.GetType(), e.Message); - + // Example: // Exception caught when disposing PivSession: Yubico.PlatformInterop.SCardException, // Unable to begin a transaction with the given smart card. SCARD_E_SERVICE_STOPPED: The smart card resource manager has shut down. @@ -510,7 +510,7 @@ public void ResetApplication() // As resetting the PIV application resets the management key, // the management key must be updated to account for the case when the previous management key type // was not the default key type. - UpdateManagementKey(_yubiKeyDevice); + RefreshManagementKeyAlgorithm(); } /// @@ -671,22 +671,5 @@ private void TryBlock(byte slot) CultureInfo.CurrentCulture, ExceptionMessages.ApplicationResetFailure)); } - - private void UpdateManagementKey(IYubiKeyDevice yubiKey) => - ManagementKeyAlgorithm = yubiKey.HasFeature(YubiKeyFeature.PivAesManagementKey) - ? GetManagementKeyAlgorithm() - : PivAlgorithm.TripleDes; // Default for keys with firmware version < 5.7 - - private PivAlgorithm GetManagementKeyAlgorithm() - { - var response = Connection.SendCommand(new GetMetadataCommand(PivSlot.Management)); - if (response.Status != ResponseStatus.Success) - { - throw new InvalidOperationException(response.StatusMessage); - } - - var metadata = response.GetData(); - return metadata.Algorithm; - } } } diff --git a/Yubico.YubiKey/tests/integration/Yubico/YubiKey/Piv/ManagementKeyTests.cs b/Yubico.YubiKey/tests/integration/Yubico/YubiKey/Piv/ManagementKeyTests.cs index 6e7e5d716..6ecd5afa3 100644 --- a/Yubico.YubiKey/tests/integration/Yubico/YubiKey/Piv/ManagementKeyTests.cs +++ b/Yubico.YubiKey/tests/integration/Yubico/YubiKey/Piv/ManagementKeyTests.cs @@ -44,10 +44,12 @@ public ManagementKeyTests(ITestOutputHelper output) }; } - [Fact] - public void HasFeature_ReturnsCorrect() + [Theory] + [InlineData(StandardTestDevice.Fw5)] + [InlineData(StandardTestDevice.Fw5Fips)] + public void HasFeature_ReturnsCorrect(StandardTestDevice device) { - var testDevice = IntegrationTestDeviceEnumeration.GetTestDevice(StandardTestDevice.Fw5); + var testDevice = IntegrationTestDeviceEnumeration.GetTestDevice(device); var expectedResult = testDevice.FirmwareVersion >= new FirmwareVersion(major: 5, minor: 4, patch: 2); @@ -56,47 +58,93 @@ public void HasFeature_ReturnsCorrect() Assert.Equal(hasFeature, expectedResult); } - [Fact] - public void GetManagementAlgorithm_WhenReset_ReturnsCorrectType() + [Theory] + [InlineData(StandardTestDevice.Fw5)] + [InlineData(StandardTestDevice.Fw5Fips)] + public void GetManagementAlgorithm_WhenReset_ReturnsCorrectType(StandardTestDevice device) { - var testDevice = IntegrationTestDeviceEnumeration.GetTestDevice(StandardTestDevice.Fw5); - var shouldBeTripleDes = testDevice.FirmwareVersion < FirmwareVersion.V5_7_0; - var defaultManagementKeyType = shouldBeTripleDes - ? PivAlgorithm.TripleDes - : PivAlgorithm.Aes192; + var testDevice = IntegrationTestDeviceEnumeration.GetTestDevice(device); + var shouldBeAes = testDevice.FirmwareVersion >= FirmwareVersion.V5_7_0; + var mustBeAes = shouldBeAes && testDevice.IsFipsSeries; + var defaultManagementKeyType = shouldBeAes + ? PivAlgorithm.Aes192 + : PivAlgorithm.TripleDes; - var alternativeManagementKeyType = !shouldBeTripleDes - ? PivAlgorithm.TripleDes - : PivAlgorithm.Aes192; + var alternativeManagementKeyType = !shouldBeAes + ? PivAlgorithm.Aes192 + : PivAlgorithm.TripleDes; using var session = new PivSession(testDevice); session.KeyCollector = TestKeyCollectorDelegate; session.ResetApplication(); - session.ChangeManagementKey(PivTouchPolicy.None, alternativeManagementKeyType); - var firstCheckKeyType = session.ManagementKeyAlgorithm; + + // This must throw for FIPS devices. + if (mustBeAes) + { + Assert.Throws( + () => session.ChangeManagementKey(PivTouchPolicy.None, PivAlgorithm.TripleDes)); + } + else + { + session.ChangeManagementKey(PivTouchPolicy.None, alternativeManagementKeyType); + Assert.Equal(alternativeManagementKeyType, session.ManagementKeyAlgorithm); + + session.AuthenticateManagementKey(); + session.ResetApplication(); - Assert.Equal(alternativeManagementKeyType, firstCheckKeyType); + Assert.Equal(defaultManagementKeyType, session.ManagementKeyAlgorithm); + } + } - session.AuthenticateManagementKey(); + [Theory] + [InlineData(StandardTestDevice.Fw5)] + [InlineData(StandardTestDevice.Fw5Fips)] + public void ChangeManagementKey_WithDefaultParameters_UsesCorrectTypeForRespectiveVersion(StandardTestDevice device) + { + var testDevice = IntegrationTestDeviceEnumeration.GetTestDevice(device); + + var shouldBeAes = testDevice.FirmwareVersion >= FirmwareVersion.V5_7_0; + var mustBeAes = shouldBeAes && testDevice.IsFipsSeries; + var defaultManagementKeyType = shouldBeAes || mustBeAes + ? PivAlgorithm.Aes192 + : PivAlgorithm.TripleDes; + + using var session = new PivSession(testDevice); + session.KeyCollector = TestKeyCollectorDelegate; session.ResetApplication(); - var secondCheckKeyType = session.ManagementKeyAlgorithm; - Assert.Equal(defaultManagementKeyType, secondCheckKeyType); + // This must not throw. 5.7 FIPS requires management key to be AES192. + session.ChangeManagementKey(); + Assert.Equal(defaultManagementKeyType, session.ManagementKeyAlgorithm); + + // This must throw for FIPS devices. + if (mustBeAes) + { + Assert.Throws( + () => session.ChangeManagementKey(PivTouchPolicy.None, PivAlgorithm.TripleDes)); + } } [Theory] [InlineData(StandardTestDevice.Fw5)] + [InlineData(StandardTestDevice.Fw5Fips)] public void RandomKey_Authenticates(StandardTestDevice testDeviceType) { var testDevice = IntegrationTestDeviceEnumeration.GetTestDevice(testDeviceType); + var shouldBeAes = testDevice.FirmwareVersion >= FirmwareVersion.V5_7_0; + var mustBeAes = shouldBeAes && testDevice.IsFipsSeries; + var defaultManagementKeyType = shouldBeAes || mustBeAes + ? PivAlgorithm.Aes192 + : PivAlgorithm.TripleDes; + var isValid = false; var count = 10; for (var index = 0; index < count; index++) { GetRandomMgmtKey(); - isValid = ChangeMgmtKey(testDevice, PivAlgorithm.TripleDes); + isValid = ChangeMgmtKey(testDevice, defaultManagementKeyType); if (!isValid) { break; From 64a11b9cb77e657a7c01483182384c946af27e77 Mon Sep 17 00:00:00 2001 From: Dennis Dyallo Date: Mon, 18 Nov 2024 10:49:20 +0100 Subject: [PATCH 07/12] Create devcontainer.json --- .devcontainer/devcontainer.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..96e9533b7 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + "ghcr.io/devcontainers/features/dotnet:2": { + "version":"6.0" + } + } +} From 236c05d4cef07e31a66493c1145deb64a5e7c77c Mon Sep 17 00:00:00 2001 From: Dennis Dyallo Date: Mon, 18 Nov 2024 11:36:17 +0100 Subject: [PATCH 08/12] Create tasks.json --- .vscode/tasks.json | 145 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..9eea20adf --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,145 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build project: Yubico.Yubikey", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Yubico.YubiKey/src/Yubico.YubiKey.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "run tests: unit", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/Yubico.YubiKey/tests/unit/Yubico.YubiKey.UnitTests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "type": "dotnet", + "task": "build", + "group": "build", + "problemMatcher": [], + "label": "dotnet: build" + }, + { + "label": "run tests: integration (RequiresTouch)", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/Yubico.YubiKey/tests/integration/Yubico.YubiKey.IntegrationTests.csproj", + "--filter", + "Category=RequiresTouch", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": "test" + }, + { + "label": "run tests: integration (Simple)", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/Yubico.YubiKey/tests/integration/Yubico.YubiKey.IntegrationTests.csproj", + "--filter", + "Category=Simple", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": "test" + }, + { + "label": "run tests: integration (Elevated)", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/Yubico.YubiKey/tests/integration/Yubico.YubiKey.IntegrationTests.csproj", + "--filter", + "Category=Elevated", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": "test" + }, + { + "label": "run tests: integration (RequiresBio)", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/Yubico.YubiKey/tests/integration/Yubico.YubiKey.IntegrationTests.csproj", + "--filter", + "Category=RequiresBio", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": "test" + }, + { + "label": "run tests: integration (RequiresSetup)", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/Yubico.YubiKey/tests/integration/Yubico.YubiKey.IntegrationTests.csproj", + "--filter", + "Category=RequiresSetup", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": "test" + }, + { + "label": "run tests: integration (RequiresStepDebug)", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/Yubico.YubiKey/tests/integration/Yubico.YubiKey.IntegrationTests.csproj", + "--filter", + "Category=RequiresStepDebug", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": "test" + }, + { + "label": "run tests: integration (RequiresFips)", + "command": "dotnet", + "type": "process", + "args": [ + "test", + "${workspaceFolder}/Yubico.YubiKey/tests/integration/Yubico.YubiKey.IntegrationTests.csproj", + "--filter", + "Category=RequiresFips", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile", + "group": "test" + } + ] +} From ce4ade9d98747534f6133bd283444d7234f79672 Mon Sep 17 00:00:00 2001 From: Dennis Dyallo Date: Mon, 18 Nov 2024 11:54:08 +0100 Subject: [PATCH 09/12] Update devcontainer.json --- .devcontainer/devcontainer.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 96e9533b7..b9f3d5a01 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,5 +4,13 @@ "ghcr.io/devcontainers/features/dotnet:2": { "version":"6.0" } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-dotnettools.vscode-dotnet-runtime", + "ms-dotnettools.csharp" + ] + } } } From 4bb5448d2f67b59625a9bc6ee49b52caca783157 Mon Sep 17 00:00:00 2001 From: Dennis Dyallo Date: Mon, 18 Nov 2024 11:22:50 +0000 Subject: [PATCH 10/12] Assume default management algorithm --- .../src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs b/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs index cf2e29d95..34d3bab64 100644 --- a/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs +++ b/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.ManagementKey.cs @@ -73,7 +73,7 @@ public sealed partial class PivSession : IDisposable private PivAlgorithm DefaultManagementKeyAlgorithm => _yubiKeyDevice.HasFeature(YubiKeyFeature.PivAesManagementKey) && - _yubiKeyDevice.FirmwareVersion > FirmwareVersion.V5_7_0 + _yubiKeyDevice.FirmwareVersion >= FirmwareVersion.V5_7_0 ? PivAlgorithm.Aes192 : PivAlgorithm.TripleDes; @@ -1018,6 +1018,13 @@ private bool TryAuthenticateManagementKey(bool mutualAuthentication, private PivAlgorithm GetManagementKeyAlgorithm() { + if (!_yubiKeyDevice.HasFeature(YubiKeyFeature.PivMetadata)) + { + // Assume default for version + return DefaultManagementKeyAlgorithm; + } + + // Get current ManagementKeyAlgorithm from Yubikey metadata var response = Connection.SendCommand(new GetMetadataCommand(PivSlot.Management)); if (response.Status != ResponseStatus.Success) { From 9de07502eb44c5cba3ff52873c1dce33415642e0 Mon Sep 17 00:00:00 2001 From: Dennis Dyall Date: Mon, 18 Nov 2024 13:01:37 +0100 Subject: [PATCH 11/12] Fixed tests, assume default Tdes version is lower than 5.7 --- Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs | 9 ++++----- .../unit/Yubico/YubiKey/Piv/PivMetadataTests.cs | 13 +++---------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs b/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs index 1f7fa198c..dc62ba6fe 100644 --- a/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs +++ b/Yubico.YubiKey/src/Yubico/YubiKey/Piv/PivSession.cs @@ -244,15 +244,14 @@ private PivSession(StaticKeys? scp03Keys, IYubiKeyDevice yubiKey) throw new ArgumentNullException(nameof(yubiKey)); } + _yubiKeyDevice = yubiKey; + Connection = scp03Keys is null - ? yubiKey.Connect(YubiKeyApplication.Piv) - : yubiKey.ConnectScp03(YubiKeyApplication.Piv, scp03Keys); + ? _yubiKeyDevice.Connect(YubiKeyApplication.Piv) + : _yubiKeyDevice.ConnectScp03(YubiKeyApplication.Piv, scp03Keys); ResetAuthenticationStatus(); RefreshManagementKeyAlgorithm(); - - _yubiKeyDevice = yubiKey; - _disposed = false; } /// diff --git a/Yubico.YubiKey/tests/unit/Yubico/YubiKey/Piv/PivMetadataTests.cs b/Yubico.YubiKey/tests/unit/Yubico/YubiKey/Piv/PivMetadataTests.cs index 22a9114ff..8bf7da925 100644 --- a/Yubico.YubiKey/tests/unit/Yubico/YubiKey/Piv/PivMetadataTests.cs +++ b/Yubico.YubiKey/tests/unit/Yubico/YubiKey/Piv/PivMetadataTests.cs @@ -14,6 +14,7 @@ using System; using Xunit; +using Yubico.YubiKey.Piv.Commands; using Yubico.YubiKey.TestUtilities; namespace Yubico.YubiKey.Piv @@ -21,17 +22,9 @@ namespace Yubico.YubiKey.Piv public class PivMetadataTests { [Fact] - public void SessionMetadata_BadSlot_CorrectException() + public void GetMetadataCommand_BadSlot_CorrectException() { - var yubiKey = new HollowYubiKeyDevice(); - yubiKey.FirmwareVersion.Major = 5; - yubiKey.FirmwareVersion.Minor = 3; - yubiKey.AvailableUsbCapabilities = YubiKeyCapabilities.Piv; - - using (var pivSession = new PivSession(yubiKey)) - { - _ = Assert.Throws(() => pivSession.GetMetadata(0xff)); - } + Assert.Throws(() => new GetMetadataCommand(0xff)); } [Fact] From 1b5578a320da9b47b5de273c1ef323f235b06cb1 Mon Sep 17 00:00:00 2001 From: Dennis Dyallo Date: Wed, 27 Nov 2024 19:08:08 +0100 Subject: [PATCH 12/12] Update pull_request_template.md --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5cd739a89..6140cf0ad 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -22,7 +22,7 @@ Please describe the tests that you ran to verify your changes. Provide instructi ## Checklist: -- [ ] My code follows the [style guidelines](https://raw.githubusercontent.com/Yubico/Yubico.NET.SDK/043119ad1d19e0e6e66556c970a81d0c1aba36c8/CONTRIBUTING.md) of this project +- [ ] My code follows the [style guidelines](https://raw.githubusercontent.com/Yubico/Yubico.NET.SDK/CONTRIBUTING.md) of this project - [ ] I have performed a self-review of my own code - [ ] I have run `dotnet format` to format my code - [ ] I have commented my code, particularly in hard-to-understand areas