Skip to content

Commit b471633

Browse files
authored
Merge pull request #261 from Yubico/dennisdyallo/versionqualifier
refactor: Update access modifier of VersionQualifier
2 parents 9b6e69c + ad2af7e commit b471633

File tree

7 files changed

+106
-55
lines changed

7 files changed

+106
-55
lines changed

Yubico.YubiKey/src/Yubico/YubiKey/IYubiKeyDeviceInfo.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@ public interface IYubiKeyDeviceInfo
9090
/// The version of the firmware currently running on the YubiKey.
9191
/// </summary>
9292
public FirmwareVersion FirmwareVersion { get; }
93+
94+
/// <summary>
95+
/// Represents a version qualifier for a firmware version.
96+
/// A version qualifier typically includes the firmware version, a type (such as Alpha, Beta, or Final),
97+
/// and an iteration number.
98+
/// </summary>
99+
public VersionQualifier VersionQualifier { get; }
100+
101+
/// <summary>
102+
/// A string representation of the firmware version currently running on the YubiKey
103+
/// which may include a version qualifier.
104+
/// </summary>
105+
public string VersionName { get; }
93106

94107
/// <summary>
95108
/// The version of the chip/firmware storing the fingerprints (the second

Yubico.YubiKey/src/Yubico/YubiKey/VersionQualifier.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Yubico.YubiKey;
2020
/// Represents the type of version qualifier for a firmware version.
2121
/// The version qualifier type indicates whether the version is an Alpha, Beta, or Final release.
2222
/// </summary>
23-
internal enum VersionQualifierType : byte
23+
public enum VersionQualifierType : byte
2424
{
2525
Alpha = 0x00,
2626
Beta = 0x01,
@@ -32,12 +32,13 @@ internal enum VersionQualifierType : byte
3232
/// A version qualifier typically includes the firmware version, a type (such as Alpha, Beta, or Final),
3333
/// and an iteration number.
3434
/// </summary>
35-
internal class VersionQualifier
35+
public class VersionQualifier
3636
{
3737
/// <summary>
3838
/// Represents the firmware version associated with this qualifier.
3939
/// </summary>
4040
public FirmwareVersion FirmwareVersion { get; }
41+
4142
/// <summary>
4243
/// Represents the type of version qualifier, such as Alpha, Beta, or Final.
4344
/// </summary>
@@ -62,9 +63,10 @@ internal class VersionQualifier
6263
/// <exception cref="ArgumentNullException"></exception>
6364
public VersionQualifier(FirmwareVersion firmwareVersion, VersionQualifierType type, long iteration)
6465
{
65-
if (iteration < 0 || iteration > uint.MaxValue)
66+
if (iteration is < 0 or > uint.MaxValue)
6667
{
67-
throw new ArgumentOutOfRangeException(nameof(iteration),
68+
throw new ArgumentOutOfRangeException(
69+
nameof(iteration),
6870
$"Iteration must be between 0 and {uint.MaxValue}.");
6971
}
7072

@@ -82,13 +84,18 @@ public VersionQualifier()
8284
{
8385
FirmwareVersion = new FirmwareVersion();
8486
Type = VersionQualifierType.Final;
85-
Iteration = 0;
8687
}
8788

89+
/// <summary>
90+
/// Returns a string that represents the current <see cref="VersionQualifier"/>.
91+
/// </summary>
8892
public override string ToString() => $"{FirmwareVersion}.{Type.ToString().ToLowerInvariant()}.{Iteration}";
89-
public override bool Equals(object obj) => obj is VersionQualifier other &&
90-
FirmwareVersion.Equals(other.FirmwareVersion) &&
91-
Type == other.Type &&
92-
Iteration == other.Iteration;
93+
94+
public override bool Equals(object? obj) =>
95+
obj is VersionQualifier other &&
96+
FirmwareVersion.Equals(other.FirmwareVersion) &&
97+
Type == other.Type &&
98+
Iteration == other.Iteration;
99+
93100
public override int GetHashCode() => HashCode.Combine(FirmwareVersion, Type, Iteration);
94101
}

Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDevice.Instance.cs

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,67 +31,73 @@ public partial class YubiKeyDevice : IYubiKeyDevice
3131
#region IYubiKeyDeviceInfo
3232

3333
/// <inheritdoc />
34-
public YubiKeyCapabilities AvailableUsbCapabilities => _yubiKeyInfo.AvailableUsbCapabilities;
34+
public YubiKeyCapabilities AvailableUsbCapabilities => _yubiKeyDeviceInfo.AvailableUsbCapabilities;
3535

3636
/// <inheritdoc />
37-
public YubiKeyCapabilities EnabledUsbCapabilities => _yubiKeyInfo.EnabledUsbCapabilities;
37+
public YubiKeyCapabilities EnabledUsbCapabilities => _yubiKeyDeviceInfo.EnabledUsbCapabilities;
3838

3939
/// <inheritdoc />
40-
public YubiKeyCapabilities AvailableNfcCapabilities => _yubiKeyInfo.AvailableNfcCapabilities;
40+
public YubiKeyCapabilities AvailableNfcCapabilities => _yubiKeyDeviceInfo.AvailableNfcCapabilities;
4141

4242
/// <inheritdoc />
43-
public YubiKeyCapabilities EnabledNfcCapabilities => _yubiKeyInfo.EnabledNfcCapabilities;
43+
public YubiKeyCapabilities EnabledNfcCapabilities => _yubiKeyDeviceInfo.EnabledNfcCapabilities;
4444

4545
/// <inheritdoc />
46-
public YubiKeyCapabilities FipsApproved => _yubiKeyInfo.FipsApproved;
46+
public YubiKeyCapabilities FipsApproved => _yubiKeyDeviceInfo.FipsApproved;
4747

4848
/// <inheritdoc />
49-
public YubiKeyCapabilities FipsCapable => _yubiKeyInfo.FipsCapable;
49+
public YubiKeyCapabilities FipsCapable => _yubiKeyDeviceInfo.FipsCapable;
5050

5151
/// <inheritdoc />
52-
public YubiKeyCapabilities ResetBlocked => _yubiKeyInfo.ResetBlocked;
52+
public YubiKeyCapabilities ResetBlocked => _yubiKeyDeviceInfo.ResetBlocked;
5353

5454
/// <inheritdoc />
55-
public bool IsNfcRestricted => _yubiKeyInfo.IsNfcRestricted;
55+
public bool IsNfcRestricted => _yubiKeyDeviceInfo.IsNfcRestricted;
5656

5757
/// <inheritdoc />
58-
public string? PartNumber => _yubiKeyInfo.PartNumber;
58+
public string? PartNumber => _yubiKeyDeviceInfo.PartNumber;
5959

6060
/// <inheritdoc />
61-
public bool IsPinComplexityEnabled => _yubiKeyInfo.IsPinComplexityEnabled;
61+
public bool IsPinComplexityEnabled => _yubiKeyDeviceInfo.IsPinComplexityEnabled;
6262

6363
/// <inheritdoc />
64-
public int? SerialNumber => _yubiKeyInfo.SerialNumber;
64+
public int? SerialNumber => _yubiKeyDeviceInfo.SerialNumber;
6565

6666
/// <inheritdoc />
67-
public bool IsFipsSeries => _yubiKeyInfo.IsFipsSeries;
67+
public bool IsFipsSeries => _yubiKeyDeviceInfo.IsFipsSeries;
6868

6969
/// <inheritdoc />
70-
public bool IsSkySeries => _yubiKeyInfo.IsSkySeries;
70+
public bool IsSkySeries => _yubiKeyDeviceInfo.IsSkySeries;
7171

7272
/// <inheritdoc />
73-
public FormFactor FormFactor => _yubiKeyInfo.FormFactor;
73+
public FormFactor FormFactor => _yubiKeyDeviceInfo.FormFactor;
7474

7575
/// <inheritdoc />
76-
public FirmwareVersion FirmwareVersion => _yubiKeyInfo.FirmwareVersion;
76+
public FirmwareVersion FirmwareVersion => _yubiKeyDeviceInfo.FirmwareVersion;
77+
78+
/// <inheritdoc />
79+
public VersionQualifier VersionQualifier => _yubiKeyDeviceInfo.VersionQualifier;
80+
81+
/// <inheritdoc />
82+
public string VersionName => _yubiKeyDeviceInfo.VersionName;
7783

7884
/// <inheritdoc />
79-
public TemplateStorageVersion? TemplateStorageVersion => _yubiKeyInfo.TemplateStorageVersion;
85+
public TemplateStorageVersion? TemplateStorageVersion => _yubiKeyDeviceInfo.TemplateStorageVersion;
8086

8187
/// <inheritdoc />
82-
public ImageProcessorVersion? ImageProcessorVersion => _yubiKeyInfo.ImageProcessorVersion;
88+
public ImageProcessorVersion? ImageProcessorVersion => _yubiKeyDeviceInfo.ImageProcessorVersion;
8389

8490
/// <inheritdoc />
85-
public int AutoEjectTimeout => _yubiKeyInfo.AutoEjectTimeout;
91+
public int AutoEjectTimeout => _yubiKeyDeviceInfo.AutoEjectTimeout;
8692

8793
/// <inheritdoc />
88-
public byte ChallengeResponseTimeout => _yubiKeyInfo.ChallengeResponseTimeout;
94+
public byte ChallengeResponseTimeout => _yubiKeyDeviceInfo.ChallengeResponseTimeout;
8995

9096
/// <inheritdoc />
91-
public DeviceFlags DeviceFlags => _yubiKeyInfo.DeviceFlags;
97+
public DeviceFlags DeviceFlags => _yubiKeyDeviceInfo.DeviceFlags;
9298

9399
/// <inheritdoc />
94-
public bool ConfigurationLocked => _yubiKeyInfo.ConfigurationLocked;
100+
public bool ConfigurationLocked => _yubiKeyDeviceInfo.ConfigurationLocked;
95101

96102
#endregion
97103

@@ -109,7 +115,7 @@ public partial class YubiKeyDevice : IYubiKeyDevice
109115
private ISmartCardDevice? _smartCardDevice;
110116
private IHidDevice? _hidFidoDevice;
111117
private IHidDevice? _hidKeyboardDevice;
112-
private IYubiKeyDeviceInfo _yubiKeyInfo;
118+
private IYubiKeyDeviceInfo _yubiKeyDeviceInfo;
113119

114120
private ConnectionFactory ConnectionFactory =>
115121
new ConnectionFactory(
@@ -149,9 +155,9 @@ public Transport AvailableTransports
149155
/// Constructs a <see cref="YubiKeyDevice"/> instance.
150156
/// </summary>
151157
/// <param name="device">A valid device; either a smart card, keyboard, or FIDO device.</param>
152-
/// <param name="info">The YubiKey device information that describes the device.</param>
158+
/// <param name="deviceInfo">The YubiKey device information that describes the device.</param>
153159
/// <exception cref="ArgumentException">An unrecognized device type was given.</exception>
154-
public YubiKeyDevice(IDevice device, IYubiKeyDeviceInfo info)
160+
public YubiKeyDevice(IDevice device, IYubiKeyDeviceInfo deviceInfo)
155161
{
156162
switch (device)
157163
{
@@ -170,7 +176,7 @@ public YubiKeyDevice(IDevice device, IYubiKeyDeviceInfo info)
170176

171177
_log.LogInformation("Created a YubiKeyDevice based on the {Transport} transport.", LastActiveTransport);
172178

173-
_yubiKeyInfo = info;
179+
_yubiKeyDeviceInfo = deviceInfo;
174180
IsNfcDevice = _smartCardDevice?.IsNfcTransport() ?? false;
175181
LastActiveTransport = GetTransportIfOnlyDevice();
176182
}
@@ -192,7 +198,7 @@ public YubiKeyDevice(
192198
_hidFidoDevice = hidFidoDevice;
193199
_hidKeyboardDevice = hidKeyboardDevice;
194200

195-
_yubiKeyInfo = yubiKeyDeviceInfo;
201+
_yubiKeyDeviceInfo = yubiKeyDeviceInfo;
196202
IsNfcDevice = smartCardDevice?.IsNfcTransport() ?? false;
197203
LastActiveTransport = GetTransportIfOnlyDevice(); // Must be after setting the three device fields.
198204
}
@@ -228,13 +234,13 @@ internal void Merge(IDevice device, IYubiKeyDeviceInfo info)
228234
MergeDevice(device);
229235

230236
// Then merge the YubiKey device information / metadata
231-
if (_yubiKeyInfo is YubiKeyDeviceInfo first && info is YubiKeyDeviceInfo second)
237+
if (_yubiKeyDeviceInfo is YubiKeyDeviceInfo first && info is YubiKeyDeviceInfo second)
232238
{
233-
_yubiKeyInfo = first.Merge(second);
239+
_yubiKeyDeviceInfo = first.Merge(second);
234240
}
235241
else
236242
{
237-
_yubiKeyInfo = info;
243+
_yubiKeyDeviceInfo = info;
238244
}
239245
}
240246

Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDeviceInfo.cs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Diagnostics.CodeAnalysis;
2020
using System.Text;
2121
using Microsoft.Extensions.Logging;
22+
using Yubico.Core.Logging;
2223
using Yubico.Core.Tlv;
2324

2425
namespace Yubico.YubiKey
@@ -75,6 +76,7 @@ public class YubiKeyDeviceInfo : IYubiKeyDeviceInfo
7576
/// <inheritdoc />
7677
public FormFactor FormFactor { get; set; }
7778

79+
/// <inheritdoc />
7880
public string VersionName => VersionQualifier.Type == VersionQualifierType.Final
7981
? FirmwareVersion.ToString()
8082
: VersionQualifier.ToString();
@@ -83,7 +85,7 @@ public class YubiKeyDeviceInfo : IYubiKeyDeviceInfo
8385
public FirmwareVersion FirmwareVersion { get; set; }
8486

8587
/// <inheritdoc />
86-
internal VersionQualifier VersionQualifier { get; set; }
88+
public VersionQualifier VersionQualifier { get; set; }
8789

8890
/// <inheritdoc />
8991
public TemplateStorageVersion? TemplateStorageVersion { get; set; }
@@ -269,12 +271,24 @@ internal static YubiKeyDeviceInfo CreateFromResponseData(Dictionary<int, ReadOnl
269271
}
270272
}
271273

274+
SetFipsSeries(deviceInfo, fipsSeriesFlag);
275+
SetSkySeries(deviceInfo, skySeriesFlag);
276+
SetFirmwareVersionAndQualifier(responseApduData, deviceInfo);
277+
278+
return deviceInfo;
279+
}
280+
281+
private static void SetSkySeries(YubiKeyDeviceInfo deviceInfo, bool skySeriesFlag) => deviceInfo.IsSkySeries |= skySeriesFlag;
282+
283+
private static void SetFipsSeries(YubiKeyDeviceInfo deviceInfo, bool fipsSeriesFlag)
284+
{
272285
deviceInfo.IsFipsSeries = deviceInfo.FirmwareVersion >= _fipsFlagInclusiveLowerBound
273286
? fipsSeriesFlag
274287
: deviceInfo.IsFipsVersion;
288+
}
275289

276-
deviceInfo.IsSkySeries |= skySeriesFlag;
277-
290+
private static void SetFirmwareVersionAndQualifier(Dictionary<int, ReadOnlyMemory<byte>> responseApduData, YubiKeyDeviceInfo deviceInfo)
291+
{
278292
if (!responseApduData.TryGetValue(YubikeyDeviceManagementTags.VersionQualifierTag, out var versionQualifierBytes))
279293
{
280294
deviceInfo.VersionQualifier = new VersionQualifier(deviceInfo.FirmwareVersion, VersionQualifierType.Final, 0);
@@ -286,21 +300,21 @@ internal static YubiKeyDeviceInfo CreateFromResponseData(Dictionary<int, ReadOnl
286300
throw new ArgumentException("Invalid data length.");
287301
}
288302

289-
const byte TAG_VERSION = 0x01;
290-
const byte TAG_TYPE = 0x02;
291-
const byte TAG_ITERATION = 0x03;
303+
const byte tagVersion = 0x01;
304+
const byte tagType = 0x02;
305+
const byte tagIteration = 0x03;
292306

293307
var data = TlvObjects.DecodeDictionary(versionQualifierBytes.Span);
294308

295-
if (!data.TryGetValue(TAG_VERSION, out var firmwareVersionBytes))
309+
if (!data.TryGetValue(tagVersion, out var firmwareVersionBytes))
296310
{
297311
throw new ArgumentException("Missing TLV field: TAG_VERSION.");
298312
}
299-
if (!data.TryGetValue(TAG_TYPE, out var versionTypeBytes))
313+
if (!data.TryGetValue(tagType, out var versionTypeBytes))
300314
{
301315
throw new ArgumentException("Missing TLV field: TAG_TYPE.");
302316
}
303-
if (!data.TryGetValue(TAG_ITERATION, out var iterationBytes))
317+
if (!data.TryGetValue(tagIteration, out var iterationBytes))
304318
{
305319
throw new ArgumentException("Missing TLV field: TAG_ITERATION.");
306320
}
@@ -318,13 +332,15 @@ internal static YubiKeyDeviceInfo CreateFromResponseData(Dictionary<int, ReadOnl
318332
bool isFinalVersion = deviceInfo.VersionQualifier.Type == VersionQualifierType.Final;
319333
if (!isFinalVersion)
320334
{
321-
var Logger = Core.Logging.Log.GetLogger<YubiKeyDeviceInfo>();
322-
Logger.LogDebug("Overriding behavioral version with {FirmwareString}", deviceInfo.VersionQualifier.FirmwareVersion);
335+
var logger = Log.GetLogger<YubiKeyDeviceInfo>();
336+
logger.LogDebug("Overriding behavioral version with {FirmwareString}", deviceInfo.VersionQualifier.FirmwareVersion);
323337
}
324338

325-
var computedVersion = isFinalVersion ? deviceInfo.FirmwareVersion : deviceInfo.VersionQualifier.FirmwareVersion;
326-
deviceInfo.FirmwareVersion = computedVersion;
327-
return deviceInfo;
339+
var finalVersion = isFinalVersion
340+
? deviceInfo.FirmwareVersion
341+
: deviceInfo.VersionQualifier.FirmwareVersion;
342+
343+
deviceInfo.FirmwareVersion = finalVersion;
328344
}
329345

330346
private static string? GetPartNumber(ReadOnlySpan<byte> valueSpan)
@@ -372,7 +388,7 @@ internal YubiKeyDeviceInfo Merge(YubiKeyDeviceInfo? second)
372388
? FirmwareVersion
373389
: second.FirmwareVersion,
374390

375-
VersionQualifier = VersionQualifier != new VersionQualifier()
391+
VersionQualifier = !Equals(VersionQualifier, new VersionQualifier())
376392
? VersionQualifier
377393
: second.VersionQualifier,
378394

Yubico.YubiKey/tests/integration/Yubico/YubiKey/Piv/AttestTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ public void Attest_Imported_ThrowsException(
5151
Session.CreateAttestationStatement(PivSlot.Retired1));
5252
}
5353

54-
[SkippableTheory(typeof(DeviceNotFoundException))]
54+
[SkippableTheory(typeof(DeviceNotFoundException), typeof(NotSupportedException))]
55+
[InlineData(KeyType.RSA1024, StandardTestDevice.Fw5)]
5556
[InlineData(KeyType.RSA2048, StandardTestDevice.Fw5)]
5657
[InlineData(KeyType.RSA3072, StandardTestDevice.Fw5)]
5758
[InlineData(KeyType.RSA4096, StandardTestDevice.Fw5)]
58-
[InlineData(KeyType.ECP256, StandardTestDevice.Fw5Fips)]
59+
[InlineData(KeyType.ECP256, StandardTestDevice.Fw5)]
5960
[InlineData(KeyType.Ed25519, StandardTestDevice.Fw5)]
6061
public void AttestGenerated(
6162
KeyType keyType,

Yubico.YubiKey/tests/integration/Yubico/YubiKey/Piv/RetryTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public void ChangeRetry_SetsToDefault(StandardTestDevice testDeviceType)
5959

6060
using (var pivSession = new PivSession(testDevice))
6161
{
62+
pivSession.ResetApplication();
6263
try
6364
{
6465
var collectorObj = new Simple39KeyCollector();

Yubico.YubiKey/tests/utilities/Yubico/YubiKey/TestUtilities/HollowYubiKeyDevice.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ public sealed class HollowYubiKeyDevice : IYubiKeyDevice
7878
/// <inheritdoc />
7979
public FirmwareVersion FirmwareVersion { get; set; }
8080

81+
public VersionQualifier VersionQualifier { get; }
82+
public string VersionName => VersionQualifier.Type == VersionQualifierType.Final
83+
? FirmwareVersion.ToString()
84+
: VersionQualifier.ToString();
85+
8186
/// <inheritdoc />
8287
public TemplateStorageVersion TemplateStorageVersion { get; set; }
8388

@@ -113,6 +118,8 @@ public HollowYubiKeyDevice(bool alwaysAuthenticatePiv = false)
113118
// We initialize this to zeros, but if you need a version,
114119
// the setter is public. HollowConnection takes a version
115120
// and feeds it back in the ReadStatusCommand.
121+
122+
VersionQualifier = new VersionQualifier();
116123
FirmwareVersion = new FirmwareVersion
117124
{
118125
Major = 0,

0 commit comments

Comments
 (0)