diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/FirmwareVersion.cs b/Yubico.YubiKey/src/Yubico/YubiKey/FirmwareVersion.cs index 2e71a5ddd..9ce781c4e 100644 --- a/Yubico.YubiKey/src/Yubico/YubiKey/FirmwareVersion.cs +++ b/Yubico.YubiKey/src/Yubico/YubiKey/FirmwareVersion.cs @@ -61,7 +61,7 @@ public FirmwareVersion(byte major, byte minor = 0, byte patch = 0) Minor = minor; Patch = patch; } - + /// /// Parse a string of the form "major.minor.patch" /// @@ -75,23 +75,44 @@ public static FirmwareVersion Parse(string versionString) { throw new ArgumentNullException(nameof(versionString)); } - + string[] parts = versionString.Split('.'); if (parts.Length != 3) { throw new ArgumentException("Must include major.minor.patch", nameof(versionString)); } - + if (!byte.TryParse(parts[0], out byte major) || - !byte.TryParse(parts[1], out byte minor) || + !byte.TryParse(parts[1], out byte minor) || !byte.TryParse(parts[2], out byte patch)) { throw new ArgumentException("Major, minor and patch must be valid numbers", nameof(versionString)); } - + return new FirmwareVersion(major, minor, patch); } + /// + /// Creates a from a byte array. + /// The byte array must contain exactly three bytes, representing the major, minor, and patch versions. + /// + /// A byte array containing the version information. + /// A instance. + /// Thrown if the byte array does not contain exactly three bytes. + /// + /// The first byte represents the major version, the second byte represents the minor version, + /// and the third byte represents the patch version. + /// + public static FirmwareVersion FromBytes(ReadOnlySpan bytes) + { + if (bytes.Length != 3) + { + throw new ArgumentException("Invalid length of data"); + } + + return new FirmwareVersion(bytes[0], bytes[1], bytes[2]); + } + public static bool operator >(FirmwareVersion left, FirmwareVersion right) { // CA1065, these operators shouldn't throw exceptions. diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/VersionQualifier.cs b/Yubico.YubiKey/src/Yubico/YubiKey/VersionQualifier.cs new file mode 100644 index 000000000..c28f32c69 --- /dev/null +++ b/Yubico.YubiKey/src/Yubico/YubiKey/VersionQualifier.cs @@ -0,0 +1,94 @@ +// Copyright 2025 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; + +namespace Yubico.YubiKey; + +/// +/// Represents the type of version qualifier for a firmware version. +/// The version qualifier type indicates whether the version is an Alpha, Beta, or Final release. +/// +internal enum VersionQualifierType : byte +{ + Alpha = 0x00, + Beta = 0x01, + Final = 0x02 +} + +/// +/// Represents a version qualifier for a firmware version. +/// A version qualifier typically includes the firmware version, a type (such as Alpha, Beta, or Final), +/// and an iteration number. +/// +internal class VersionQualifier +{ + /// + /// Represents the firmware version associated with this qualifier. + /// + public FirmwareVersion FirmwareVersion { get; } + /// + /// Represents the type of version qualifier, such as Alpha, Beta, or Final. + /// + public VersionQualifierType Type { get; } + + /// + /// Represents the iteration number of the version qualifier. + /// + public long Iteration { get; } + + /// + /// Initializes a new instance of the class. + /// This constructor allows you to specify the firmware version, type, and iteration. + /// The iteration must be a non-negative value and less than or equal to int.MaxValue. + /// If the firmware version is null, an will be thrown. + /// If the iteration is negative or greater than int.MaxValue, an will be thrown. + /// + /// The firmware version associated with this qualifier. + /// The type of version qualifier (Alpha, Beta, Final). + /// The iteration number of the version qualifier, must be a non-negative value and less than or equal to int.MaxValue. + /// + /// + public VersionQualifier(FirmwareVersion firmwareVersion, VersionQualifierType type, long iteration) + { + if (iteration < 0 || iteration > uint.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(iteration), + $"Iteration must be between 0 and {uint.MaxValue}."); + } + + FirmwareVersion = firmwareVersion ?? throw new ArgumentNullException(nameof(firmwareVersion)); + Type = type; + Iteration = iteration; + } + + /// + /// Initializes a new instance of the class with default values. + /// The default firmware version is set to a new instance of , + /// the type is set to , and the iteration is set to 0. + /// + public VersionQualifier() + { + FirmwareVersion = new FirmwareVersion(); + Type = VersionQualifierType.Final; + Iteration = 0; + } + + public override string ToString() => $"{FirmwareVersion}.{Type.ToString().ToLowerInvariant()}.{Iteration}"; + public override bool Equals(object obj) => obj is VersionQualifier other && + FirmwareVersion.Equals(other.FirmwareVersion) && + Type == other.Type && + Iteration == other.Iteration; + public override int GetHashCode() => HashCode.Combine(FirmwareVersion, Type, Iteration); +} diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs b/Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs index 90f3846da..e816c9d1b 100644 --- a/Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs +++ b/Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs @@ -18,6 +18,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; +using Microsoft.Extensions.Logging; +using Yubico.Core.Tlv; namespace Yubico.YubiKey { @@ -73,9 +75,16 @@ public class YubiKeyDeviceInfo : IYubiKeyDeviceInfo /// public FormFactor FormFactor { get; set; } + public string VersionName => VersionQualifier.Type == VersionQualifierType.Final + ? FirmwareVersion.ToString() + : VersionQualifier.ToString(); + /// public FirmwareVersion FirmwareVersion { get; set; } + /// + internal VersionQualifier VersionQualifier { get; set; } + /// public TemplateStorageVersion? TemplateStorageVersion { get; set; } @@ -109,6 +118,7 @@ public class YubiKeyDeviceInfo : IYubiKeyDeviceInfo public YubiKeyDeviceInfo() { FirmwareVersion = new FirmwareVersion(); + VersionQualifier = new VersionQualifier(FirmwareVersion, VersionQualifierType.Final, 0); } /// @@ -191,6 +201,10 @@ internal static YubiKeyDeviceInfo CreateFromResponseData(Dictionary(); + Logger.LogDebug("Overriding behavioral version with {FirmwareString}", deviceInfo.VersionQualifier.FirmwareVersion); + } + + var computedVersion = isFinalVersion ? deviceInfo.FirmwareVersion : deviceInfo.VersionQualifier.FirmwareVersion; + deviceInfo.FirmwareVersion = computedVersion; return deviceInfo; } @@ -309,6 +372,10 @@ internal YubiKeyDeviceInfo Merge(YubiKeyDeviceInfo? second) ? FirmwareVersion : second.FirmwareVersion, + VersionQualifier = VersionQualifier != new VersionQualifier() + ? VersionQualifier + : second.VersionQualifier, + AutoEjectTimeout = DeviceFlags.HasFlag(DeviceFlags.TouchEject) ? AutoEjectTimeout : second.DeviceFlags.HasFlag(DeviceFlags.TouchEject) diff --git a/Yubico.YubiKey/src/Yubico/YubiKey/YubikeyDeviceManagementTags.cs b/Yubico.YubiKey/src/Yubico/YubiKey/YubikeyDeviceManagementTags.cs index 46e42500c..65e1d587b 100644 --- a/Yubico.YubiKey/src/Yubico/YubiKey/YubikeyDeviceManagementTags.cs +++ b/Yubico.YubiKey/src/Yubico/YubiKey/YubikeyDeviceManagementTags.cs @@ -42,6 +42,7 @@ internal static class YubikeyDeviceManagementTags internal const byte PinComplexityTag = 0x16; internal const byte NfcRestrictedTag = 0x17; internal const byte ResetBlockedTag = 0x18; + internal const byte VersionQualifierTag = 0x19; internal const byte TemplateStorageVersionTag = 0x20; // FPS version tag internal const byte ImageProcessorVersionTag = 0x21; // STM version tag internal const byte TempTouchThresholdTag = 0x85; diff --git a/Yubico.YubiKey/tests/unit/Yubico/YubiKey/VersionQualifierTests.cs b/Yubico.YubiKey/tests/unit/Yubico/YubiKey/VersionQualifierTests.cs new file mode 100644 index 000000000..70bd218d6 --- /dev/null +++ b/Yubico.YubiKey/tests/unit/Yubico/YubiKey/VersionQualifierTests.cs @@ -0,0 +1,92 @@ +// 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 Xunit; + +namespace Yubico.YubiKey +{ + public class VersionQualifierTests + { + [Fact] + public void TestVersion() + { + var version = new FirmwareVersion(5, 7, 2); + Assert.Equal( + version, new VersionQualifier(version, VersionQualifierType.Alpha, 1).FirmwareVersion); + } + + [Fact] + public void TestType() + { + Assert.Equal( + VersionQualifierType.Alpha, + new VersionQualifier(new FirmwareVersion(5, 7, 2), VersionQualifierType.Alpha, 1).Type); + Assert.Equal( + VersionQualifierType.Beta, + new VersionQualifier(new FirmwareVersion(5, 7, 2), VersionQualifierType.Beta, 1).Type); + Assert.Equal( + VersionQualifierType.Final, + new VersionQualifier(new FirmwareVersion(5, 7, 2), VersionQualifierType.Final, 1).Type); + } + + [Fact] + public void TestIteration() + { + var version = new FirmwareVersion(5, 7, 2); + var type = VersionQualifierType.Alpha; + Assert.Equal(0, new VersionQualifier(version, type, 0).Iteration); + Assert.Equal(128, new VersionQualifier(version, type, 128).Iteration); + Assert.Equal(255, new VersionQualifier(version, type, 255).Iteration); + } + + [Fact] + public void TestToString() + { + Assert.Equal( + "5.7.2.alpha.0", + new VersionQualifier(new FirmwareVersion(5, 7, 2), VersionQualifierType.Alpha, 0).ToString()); + Assert.Equal( + "5.6.6.beta.16384", + new VersionQualifier(new FirmwareVersion(5, 6, 6), VersionQualifierType.Beta, 16384).ToString()); + Assert.Equal( + "3.4.0.final.2147483648", + new VersionQualifier(new FirmwareVersion(3, 4, 0), VersionQualifierType.Final, 0x80000000).ToString()); + Assert.Equal( + "3.4.0.final.2147483647", + new VersionQualifier(new FirmwareVersion(3, 4, 0), VersionQualifierType.Final, 0x7fffffff).ToString()); + } + + [Fact] + public void TestEqualsAndHashCode() + { + var version1 = new FirmwareVersion(1, 0, 0); + var version2 = new FirmwareVersion(1, 0, 0); + var qualifier1 = new VersionQualifier(version1, VersionQualifierType.Alpha, 1); + var qualifier2 = new VersionQualifier(version2, VersionQualifierType.Alpha, 1); + var qualifier3 = new VersionQualifier(version1, VersionQualifierType.Beta, 2); + + Assert.Equal(qualifier1, qualifier2); + Assert.Equal(qualifier1.GetHashCode(), qualifier2.GetHashCode()); + Assert.NotEqual(qualifier1, qualifier3); + } + + [Fact] + public void TestTypeFromValue() + { + Assert.Equal(VersionQualifierType.Alpha, (VersionQualifierType)0); + Assert.Equal(VersionQualifierType.Beta, (VersionQualifierType)1); + Assert.Equal(VersionQualifierType.Final, (VersionQualifierType)2); + } + } +} diff --git a/Yubico.YubiKey/tests/unit/Yubico/YubiKey/YubikeyDeviceInfoTests.cs b/Yubico.YubiKey/tests/unit/Yubico/YubiKey/YubikeyDeviceInfoTests.cs index 65803f1fd..6a0b6cf7b 100644 --- a/Yubico.YubiKey/tests/unit/Yubico/YubiKey/YubikeyDeviceInfoTests.cs +++ b/Yubico.YubiKey/tests/unit/Yubico/YubiKey/YubikeyDeviceInfoTests.cs @@ -55,7 +55,7 @@ public void CreateFromResponseData_Returns_ExpectedFipsApproved( public void CreateFromResponseData_Returns_ExpectedSerialNumber() { const int serialNumberTag = 0x02; - Assert.Null(DeviceInfoFor(serialNumberTag).SerialNumber); + Assert.Null(DefaultInfo().SerialNumber); Assert.Equal(123456789, DeviceInfoFor(serialNumberTag, FromHex("075BCD15")).SerialNumber); } @@ -88,7 +88,7 @@ public void CreateFromResponseData_WithDifferentFormFactor_Returns_ExpectedFormF public void CreateFromResponseData_Returns_ExpectedConfigurationLocked() { const int configurationLockedTag = 0x0a; - Assert.False(DeviceInfoFor(configurationLockedTag).ConfigurationLocked); + Assert.False(DefaultInfo().ConfigurationLocked); Assert.True(DeviceInfoFor(configurationLockedTag, FromHex("01")).ConfigurationLocked); Assert.False(DeviceInfoFor(configurationLockedTag, FromHex("00")).ConfigurationLocked); } @@ -97,7 +97,7 @@ public void CreateFromResponseData_Returns_ExpectedConfigurationLocked() public void CreateFromResponseData_Returns_ExpectedFipsSeries() { const int formFactorTag = 0x04; - Assert.False(DeviceInfoFor(formFactorTag).IsFipsSeries); + Assert.False(DefaultInfo().IsFipsSeries); Assert.True(DeviceInfoFor(formFactorTag, FromHex("80"), FirmwareVersion.V5_4_2).IsFipsSeries); Assert.True(DeviceInfoFor(formFactorTag, FromHex("C0"), FirmwareVersion.V5_4_2).IsFipsSeries); Assert.False(DeviceInfoFor(formFactorTag, FromHex("40"), FirmwareVersion.V5_4_2).IsFipsSeries); @@ -107,7 +107,7 @@ public void CreateFromResponseData_Returns_ExpectedFipsSeries() public void CreateFromResponseData_Returns_ExpectedIsSkySeries() { const int formFactorTag = 0x04; - Assert.False(DeviceInfoFor(formFactorTag).IsSkySeries); + Assert.False(DefaultInfo().IsSkySeries); Assert.True(DeviceInfoFor(formFactorTag, FromHex("40")).IsSkySeries); Assert.True(DeviceInfoFor(formFactorTag, FromHex("C0")).IsSkySeries); Assert.False(DeviceInfoFor(formFactorTag, FromHex("80")).IsSkySeries); @@ -119,7 +119,7 @@ public void CreateFromResponseData_Returns_ExpectedPartNumber() const int partNumberTag = 0x13; // Valid UTF-8 - Assert.Null(DeviceInfoFor(partNumberTag).PartNumber); + Assert.Null(DefaultInfo().PartNumber); Assert.Equal("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=+-", DeviceInfoFor(partNumberTag, FromHex("6162636465666768696A6B6C6D6E6F707172737475767778797A4142434445464748494A4B4C4D4E4F505152535455565758595A303132333435363738395F3D2B2D")).PartNumber); Assert.Equal("ÖÄÅöäåěščřžýáíúůĚŠČŘŽÝÁÍÚŮ", DeviceInfoFor(partNumberTag, FromHex("C396C384C385C3B6C3A4C3A5C49BC5A1C48DC599C5BEC3BDC3A1C3ADC3BAC5AFC49AC5A0C48CC598C5BDC39DC381C38DC39AC5AE")).PartNumber); Assert.Equal("😀", DeviceInfoFor(partNumberTag, FromHex("F09F9880")).PartNumber); @@ -139,7 +139,7 @@ public void CreateFromResponseData_Returns_ExpectedPartNumber() public void CreateFromResponseData_Returns_ExpectedPinComplexity() { const int pinComplexityTag = 0x16; - Assert.False(DeviceInfoFor(pinComplexityTag).IsPinComplexityEnabled); + Assert.False(DefaultInfo().IsPinComplexityEnabled); Assert.False(DeviceInfoFor(pinComplexityTag, FromHex("00")).IsPinComplexityEnabled); Assert.True(DeviceInfoFor(pinComplexityTag, FromHex("01")).IsPinComplexityEnabled); } @@ -148,7 +148,7 @@ public void CreateFromResponseData_Returns_ExpectedPinComplexity() public void CreateFromResponseData_Returns_ExpectedResetBlocked() { const int resetBlockedTag = 0x18; - Assert.Equal(YubiKeyCapabilities.None, DeviceInfoFor(resetBlockedTag).ResetBlocked); + Assert.Equal(YubiKeyCapabilities.None, DefaultInfo().ResetBlocked); Assert.Equal(YubiKeyCapabilities.Oath | YubiKeyCapabilities.Fido2, DeviceInfoFor(resetBlockedTag, FromHex("0220")).ResetBlocked); } @@ -157,7 +157,7 @@ public void CreateFromResponseData_Returns_ExpectedResetBlocked() public void CreateFromResponseData_Returns_ExpectedTemplateStorageVersion() { const int templateStorageVersionTag = 0x20; - Assert.Null(DeviceInfoFor(templateStorageVersionTag).TemplateStorageVersion); + Assert.Null(DefaultInfo().TemplateStorageVersion); Assert.Equal(new TemplateStorageVersion(5, 6, 6), DeviceInfoFor(templateStorageVersionTag, FromHex("050606")).TemplateStorageVersion); } @@ -166,36 +166,136 @@ public void CreateFromResponseData_Returns_ExpectedTemplateStorageVersion() public void CreateFromResponseData_Returns_ExpectedImageProcessorVersion() { const int imageProcessorVersionTag = 0x21; - Assert.Null(DeviceInfoFor(imageProcessorVersionTag).ImageProcessorVersion); + Assert.Null(DefaultInfo().ImageProcessorVersion); Assert.Equal(new ImageProcessorVersion(7, 0, 5), DeviceInfoFor(imageProcessorVersionTag, FromHex("070005")).ImageProcessorVersion); } - private static YubiKeyDeviceInfo DeviceInfoFor(int tag, FirmwareVersion? version = null) => - DeviceInfoFor(tag, Array.Empty()); + [Fact] + public void CreateFromResponseData_WithEmptyData_SetsQualifierCorrectly() + { + var deviceInfo = DefaultInfo(); + Assert.Equal(DefaultVersionQualifier, deviceInfo.VersionQualifier); + + deviceInfo.VersionQualifier = new VersionQualifier(FirmwareVersion.V5_7_2, VersionQualifierType.Alpha, 15); + deviceInfo.FirmwareVersion = FirmwareVersion.V3_1_0; - private static YubiKeyDeviceInfo DeviceInfoFor(int tag, byte[] data, FirmwareVersion? version = null) + Assert.Equal("5.7.2.alpha.15", deviceInfo.VersionQualifier.ToString()); + Assert.Equal("3.1.0", deviceInfo.FirmwareVersion.ToString()); + } + + [Fact] + public void ParseVersionQualifier_VariousScenarios_ParsesCorrectly() { - byte[] versionAsBytes = version is { } + // Default version and qualifier + var info = InfoOfVersion(null, null); + Assert.Equal(DefaultVersion, info.FirmwareVersion); + Assert.Equal(new VersionQualifier(DefaultVersion, VersionQualifierType.Final, 0), info.VersionQualifier); + Assert.Equal("2.2.2", info.VersionName); + + // No qualifier provided + info = InfoOfVersion(FromHex("030403"), null); + Assert.Equal(new FirmwareVersion(3, 4, 3), info.FirmwareVersion); + Assert.Equal(new VersionQualifier(new FirmwareVersion(3, 4, 3), VersionQualifierType.Final, 0), info.VersionQualifier); + Assert.Equal("3.4.3", info.VersionName); + + // ALPHA version qualifier + info = InfoOfVersion(FromHex("000001"), FromHex("0103050403020100030400000000")); + Assert.Equal(new FirmwareVersion(5, 4, 3), info.FirmwareVersion); + Assert.Equal("5.4.3.alpha.0", info.VersionQualifier.ToString()); + Assert.Equal("5.4.3.alpha.0", info.VersionName); + + // BETA version qualifier + info = InfoOfVersion(FromHex("000001"), FromHex("01030507080201010304000000e9")); + Assert.Equal(new FirmwareVersion(5, 7, 8), info.FirmwareVersion); + Assert.Equal("5.7.8.beta.233", info.VersionQualifier.ToString()); + Assert.Equal("5.7.8.beta.233", info.VersionName); + + // FINAL version qualifier + info = InfoOfVersion(FromHex("050404"), FromHex("0103050404020102030400000005")); + Assert.Equal(new FirmwareVersion(5, 4, 4), info.FirmwareVersion); + Assert.Equal("5.4.4.final.5", info.VersionQualifier.ToString()); + Assert.Equal("5.4.4", info.VersionName); + + info = InfoOfVersion(FromHex("050709"), FromHex("01030507090201020304FFFFFFFF")); + Assert.Equal(new FirmwareVersion(5, 7, 9), info.FirmwareVersion); + Assert.Equal("5.7.9.final.4294967295", info.VersionQualifier.ToString()); + Assert.Equal("5.7.9", info.VersionName); + + info = InfoOfVersion(FromHex("05070A"), FromHex("010305070A020102030480000000")); + Assert.Equal(new FirmwareVersion(5, 7, 10), info.FirmwareVersion); + Assert.Equal("5.7.10.final.2147483648", info.VersionQualifier.ToString()); + Assert.Equal("5.7.10", info.VersionName); + + info = InfoOfVersion(FromHex("05070B"), FromHex("010305070B02010203047FFFFFFF")); + Assert.Equal(new FirmwareVersion(5, 7, 11), info.FirmwareVersion); + Assert.Equal("5.7.11.final.2147483647", info.VersionQualifier.ToString()); + Assert.Equal("5.7.11", info.VersionName); + } + + private static readonly FirmwareVersion DefaultVersion = new(2, 2, 2); + private static readonly VersionQualifier DefaultVersionQualifier = new(new FirmwareVersion(), VersionQualifierType.Final, 0); + + private static YubiKeyDeviceInfo InfoOfVersion(byte[]? versionBytes, byte[]? qualifierBytes) + { + var tlvs = new Dictionary>(); + + if (versionBytes != null) + { + tlvs.Add(0x05, versionBytes); + } + else + { + tlvs.Add(0x05, new byte[] { 2, 2, 2 }); // Default version + } + + if (qualifierBytes != null) + { + tlvs.Add(0x19, qualifierBytes); + } + + + return YubiKeyDeviceInfo.CreateFromResponseData(tlvs); + } + + private static YubiKeyDeviceInfo DefaultInfo() => YubiKeyDeviceInfo.CreateFromResponseData([]); + + private static YubiKeyDeviceInfo DeviceInfoFor(int tag, byte[] deviceInfoData, FirmwareVersion? version = null) + { + if (deviceInfoData.Length == 0) + { + return YubiKeyDeviceInfo.CreateFromResponseData([]); + } + + var tlvs = new Dictionary> { { tag, deviceInfoData } }; + const byte versionTag = 0x5; + if (tag == versionTag) + { + // No need to set version info as its already set + } + else + { + SetVersionTag(tag, version, tlvs); + } + + + return YubiKeyDeviceInfo.CreateFromResponseData(tlvs); //We're testing this method + } + + private static void SetVersionTag(int tag, FirmwareVersion? version, Dictionary> tlvs) + { + byte[] versionAsBytes = version is not null ? VersionToBytes(version) : VersionToBytes(FirmwareVersion.V2_2_0); - var tlvs = new Dictionary> { { tag, data } }; - const int versionTag = 0x5; + const byte versionTag = 0x5; if (tag != versionTag) { tlvs.Add(versionTag, versionAsBytes); } - - YubiKeyDeviceInfo info = data.Length == 0 - ? new YubiKeyDeviceInfo() - : YubiKeyDeviceInfo.CreateFromResponseData(tlvs); //We're testing this method - - return info; } - private static byte[] VersionToBytes(FirmwareVersion version) => - new[] { version.Major, version.Minor, version.Patch }; + private static byte[] VersionToBytes(FirmwareVersion version) => [version.Major, version.Minor, version.Patch]; private static byte[] FromHex(string? hex) => hex != null ? Base16.DecodeText(hex) : Array.Empty(); }