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();
}