Skip to content

Commit 2ef5ff3

Browse files
authored
Improve Code Quality (#405)
* Make UafVersion a readonly struct * Fix UndesiredMetadataStatusFido2VerificationException class name * Make RgbPaletteEntry a readonly struct * Make AuthenticationExtensionsDevicePublicKeyOutputs immutable * Make PublicKeyCredentialDescriptor immutable * Fix typo * Remove unused namespace * Use new IBufferWriter.WriteGuidBigEndian helper * Add AsnHelper.GetAaguidBlob * Fix tests
1 parent dc8916b commit 2ef5ff3

15 files changed

+173
-181
lines changed

Src/Fido2.Ctap2/Cbor/CborHelper.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,23 @@ internal sealed class CborHelper
77
{
88
public static PublicKeyCredentialDescriptor DecodePublicKeyCredentialDescriptor(CborMap map)
99
{
10-
var result = new PublicKeyCredentialDescriptor();
10+
byte[]? id = null;
11+
PublicKeyCredentialType type = default;
1112

1213
foreach (var (key, value) in map)
1314
{
1415
switch ((string)key)
1516
{
1617
case "id":
17-
result.Id = (byte[])value;
18+
id = (byte[])value;
1819
break;
1920
case "type" when (value is CborTextString { Value: "public-key" }):
20-
result.Type = PublicKeyCredentialType.PublicKey;
21+
type = PublicKeyCredentialType.PublicKey;
2122
break;
2223
}
2324
}
2425

25-
return result;
26+
return new PublicKeyCredentialDescriptor(type, id!, null);
2627
}
2728

2829
public static PublicKeyCredentialUserEntity DecodePublicKeyCredentialUserEntity(CborMap map)
Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.ComponentModel.DataAnnotations;
1+
using System;
22
using System.Text.Json.Serialization;
33

44
namespace Fido2NetLib;
@@ -9,23 +9,58 @@ namespace Fido2NetLib;
99
/// <remarks>
1010
/// <see href="https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-metadata-statement-v2.0-rd-20180702.html#rgbpaletteentry-dictionary"/>
1111
/// </remarks>
12-
public class RgbPaletteEntry
12+
public readonly struct RgbPaletteEntry : IEquatable<RgbPaletteEntry>
1313
{
14+
[JsonConstructor]
15+
public RgbPaletteEntry(ushort r, ushort g, ushort b)
16+
{
17+
R = r;
18+
G = g;
19+
B = b;
20+
}
21+
1422
/// <summary>
1523
/// Gets or sets the red channel sample value.
1624
/// </summary>
17-
[JsonPropertyName("r"), Required]
18-
public ushort R { get; set; }
25+
[JsonPropertyName("r")]
26+
public ushort R { get; }
1927

2028
/// <summary>
2129
/// Gets or sets the green channel sample value.
2230
/// </summary>
23-
[JsonPropertyName("g"), Required]
24-
public ushort G { get; set; }
31+
[JsonPropertyName("g")]
32+
public ushort G { get; }
2533

2634
/// <summary>
2735
/// Gets or sets the blue channel sample value.
2836
/// </summary>
29-
[JsonPropertyName("b"), Required]
30-
public ushort B { get; set; }
37+
[JsonPropertyName("b")]
38+
public ushort B { get; }
39+
40+
public bool Equals(RgbPaletteEntry other)
41+
{
42+
return R == other.R
43+
&& G == other.G
44+
&& B == other.B;
45+
}
46+
47+
public override bool Equals(object obj)
48+
{
49+
return obj is RgbPaletteEntry other && Equals(other);
50+
}
51+
52+
public static bool operator ==(RgbPaletteEntry left, RgbPaletteEntry right)
53+
{
54+
return left.Equals(right);
55+
}
56+
57+
public static bool operator !=(RgbPaletteEntry left, RgbPaletteEntry right)
58+
{
59+
return !left.Equals(right);
60+
}
61+
62+
public override int GetHashCode()
63+
{
64+
return HashCode.Combine(R, G, B);
65+
}
3166
}
Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Text.Json.Serialization;
1+
using System;
2+
using System.Text.Json.Serialization;
23

34
namespace Fido2NetLib;
45

@@ -8,16 +9,50 @@ namespace Fido2NetLib;
89
/// <remarks>
910
/// https://fidoalliance.org/specs/fido-uaf-v1.2-rd-20171128/fido-uaf-protocol-v1.2-rd-20171128.html#version-interface
1011
/// </remarks>
11-
public class UafVersion
12+
public readonly struct UafVersion : IEquatable<UafVersion>
1213
{
14+
[JsonConstructor]
15+
public UafVersion(ushort major, ushort minor)
16+
{
17+
Major = major;
18+
Minor = minor;
19+
}
20+
1321
/// <summary>
1422
/// Major version
1523
/// </summary>
1624
[JsonPropertyName("major")]
17-
public ushort Major { get; set; }
25+
public ushort Major { get; }
26+
1827
/// <summary>
1928
/// Minor version
2029
/// </summary>
2130
[JsonPropertyName("minor")]
22-
public ushort Minor { get; set; }
31+
public ushort Minor { get; }
32+
33+
public bool Equals(UafVersion other)
34+
{
35+
return Major == other.Major
36+
&& Minor == other.Minor;
37+
}
38+
39+
public override bool Equals(object obj)
40+
{
41+
return obj is UafVersion other && Equals(other);
42+
}
43+
44+
public override int GetHashCode()
45+
{
46+
return HashCode.Combine(Major, Minor);
47+
}
48+
49+
public static bool operator ==(UafVersion left, UafVersion right)
50+
{
51+
return left.Equals(right);
52+
}
53+
54+
public static bool operator !=(UafVersion left, UafVersion right)
55+
{
56+
return !left.Equals(right);
57+
}
2358
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
namespace Fido2NetLib.Objects;
1+
#nullable enable
2+
3+
namespace Fido2NetLib.Objects;
24

35
using System.Text.Json.Serialization;
46

57
public sealed class AuthenticationExtensionsDevicePublicKeyOutputs
68
{
9+
[JsonConstructor]
10+
public AuthenticationExtensionsDevicePublicKeyOutputs(byte[] authenticatorOutput, byte[] signature)
11+
{
12+
AuthenticatorOutput = authenticatorOutput;
13+
Signature = signature;
14+
}
15+
716
[JsonConverter(typeof(Base64UrlConverter))]
817
[JsonPropertyName("authenticatorOutput")]
9-
public byte[] AuthenticatorOutput { get; set; }
18+
public byte[] AuthenticatorOutput { get; }
1019

1120
[JsonConverter(typeof(Base64UrlConverter))]
1221
[JsonPropertyName("signature")]
13-
public byte[] Signature { get; set; }
22+
public byte[] Signature { get; }
1423
}
Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using System.Text.Json.Serialization;
1+
#nullable enable
2+
3+
using System;
4+
using System.Text.Json.Serialization;
25

36
namespace Fido2NetLib.Objects;
47

@@ -7,37 +10,38 @@ namespace Fido2NetLib.Objects;
710
/// Lazy implementation of https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialdescriptor
811
/// todo: Should add validation of values as specified in spec
912
/// </summary>
10-
public class PublicKeyCredentialDescriptor
13+
public sealed class PublicKeyCredentialDescriptor
1114
{
12-
public PublicKeyCredentialDescriptor(byte[] credentialId)
13-
{
14-
Id = credentialId;
15-
}
15+
public PublicKeyCredentialDescriptor(byte[] id)
16+
: this(PublicKeyCredentialType.PublicKey, id, null) { }
1617

17-
public PublicKeyCredentialDescriptor()
18+
[JsonConstructor]
19+
public PublicKeyCredentialDescriptor(PublicKeyCredentialType type, byte[] id, AuthenticatorTransport[]? transports = null)
1820
{
21+
ArgumentNullException.ThrowIfNull(id);
1922

23+
Type = type;
24+
Id = id;
25+
Transports = transports;
2026
}
2127

2228
/// <summary>
2329
/// This member contains the type of the public key credential the caller is referring to.
2430
/// </summary>
2531
[JsonPropertyName("type")]
26-
public PublicKeyCredentialType? Type { get; set; } = PublicKeyCredentialType.PublicKey;
32+
public PublicKeyCredentialType Type { get; }
2733

2834
/// <summary>
2935
/// This member contains the credential ID of the public key credential the caller is referring to.
3036
/// </summary>
3137
[JsonConverter(typeof(Base64UrlConverter))]
3238
[JsonPropertyName("id")]
33-
public byte[] Id { get; set; }
34-
35-
#nullable enable
39+
public byte[] Id { get; }
3640

3741
/// <summary>
3842
/// This OPTIONAL member contains a hint as to how the client might communicate with the managing authenticator of the public key credential the caller is referring to.
3943
/// </summary>
4044
[JsonPropertyName("transports")]
4145
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
42-
public AuthenticatorTransport[]? Transports { get; set; }
46+
public AuthenticatorTransport[]? Transports { get; }
4347
};

Src/Fido2.Models/UndesiredMetdatataStatusFido2VerificationException.cs renamed to Src/Fido2.Models/UndesiredMetadataStatusFido2VerificationException.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ namespace Fido2NetLib;
77
/// Exception thrown when a new attestation comes from an authenticator with a current reported security issue.
88
/// </summary>
99
[Serializable]
10-
public class UndesiredMetdatataStatusFido2VerificationException : Fido2VerificationException
10+
public class UndesiredMetadataStatusFido2VerificationException : Fido2VerificationException
1111
{
12-
public UndesiredMetdatataStatusFido2VerificationException(StatusReport statusReport) : base($"Authenticator found with undesirable status. Was {statusReport.Status}")
12+
public UndesiredMetadataStatusFido2VerificationException(StatusReport statusReport) : base($"Authenticator found with undesirable status. Was {statusReport.Status}")
1313
{
1414
StatusReport = statusReport;
1515
}
1616

17-
protected UndesiredMetdatataStatusFido2VerificationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
17+
protected UndesiredMetadataStatusFido2VerificationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
1818

1919
/// <summary>
2020
/// Status report from the authenticator that caused the attestation to be rejected.

Src/Fido2/AuthenticatorAssertionResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public static AuthenticatorAssertionResponse Parse(AuthenticatorAssertionRawResp
5050
}
5151

5252
/// <summary>
53-
/// Implements alghoritm from https://www.w3.org/TR/webauthn/#verifying-assertion
53+
/// Implements algorithm from https://www.w3.org/TR/webauthn/#verifying-assertion
5454
/// </summary>
5555
/// <param name="options">The assertionoptions that was sent to the client</param>
5656
/// <param name="fullyQualifiedExpectedOrigins">

Src/Fido2/AuthenticatorAttestationResponse.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,11 @@ public async Task<AttestationVerificationSuccess> VerifyAsync(
176176
// If ECDAA was used, verify that the identifier of the ECDAA-Issuer public key used is included in the set of acceptable trust anchors obtained in step 15.
177177
// Otherwise, use the X.509 certificates returned by the verification procedure to verify that the attestation public key correctly chains up to an acceptable root certificate.
178178

179-
// Check status resports for authenticator with undesirable status
179+
// Check status reports for authenticator with undesirable status
180180
var latestStatusReport = metadataEntry?.GetLatestStatusReport();
181181
if (latestStatusReport != null && config.UndesiredAuthenticatorMetadataStatuses.Contains(latestStatusReport.Status))
182182
{
183-
throw new UndesiredMetdatataStatusFido2VerificationException(latestStatusReport);
183+
throw new UndesiredMetadataStatusFido2VerificationException(latestStatusReport);
184184
}
185185

186186
// 23. Verify that the credentialId is ≤ 1023 bytes.
@@ -274,7 +274,7 @@ byte[] hash
274274
var latestStatusReport = metadataEntry?.GetLatestStatusReport();
275275
if (latestStatusReport != null && _config.UndesiredAuthenticatorMetadataStatuses.Contains(latestStatusReport.Status))
276276
{
277-
throw new UndesiredMetdatataStatusFido2VerificationException(latestStatusReport);
277+
throw new UndesiredMetadataStatusFido2VerificationException(latestStatusReport);
278278
}
279279

280280
return devicePublicKeyAuthenticatorOutput.GetBytes();

Src/Fido2/Objects/AttestedCredentialData.cs

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -123,21 +123,6 @@ public static Guid FromBigEndian(byte[] aaGuid)
123123
return new Guid(aaGuid);
124124
}
125125

126-
/// <summary>
127-
/// AAGUID is sent as big endian byte array, this converter is for little endian systems.
128-
/// </summary>
129-
public static byte[] AaGuidToBigEndian(Guid aaGuid)
130-
{
131-
var aaguid = aaGuid.ToByteArray();
132-
133-
SwapBytes(aaguid, 0, 3);
134-
SwapBytes(aaguid, 1, 2);
135-
SwapBytes(aaguid, 4, 5);
136-
SwapBytes(aaguid, 6, 7);
137-
138-
return aaguid;
139-
}
140-
141126
public override string ToString()
142127
{
143128
return $"AAGUID: {AaGuid}, CredentialID: {Convert.ToHexString(CredentialID)}, CredentialPublicKey: {CredentialPublicKey}";
@@ -154,16 +139,7 @@ public byte[] ToByteArray()
154139

155140
public void WriteTo(IBufferWriter<byte> writer)
156141
{
157-
// Write the aaguid as big endian bytes
158-
if (BitConverter.IsLittleEndian)
159-
{
160-
writer.Write(AaGuidToBigEndian(AaGuid));
161-
}
162-
else
163-
{
164-
_ = AaGuid.TryWriteBytes(writer.GetSpan(16));
165-
writer.Advance(16);
166-
}
142+
writer.WriteGuidBigEndian(AaGuid);
167143

168144
// Write the length of credential ID, as big endian bytes of a 16-bit unsigned integer
169145
writer.WriteUInt16BigEndian((ushort)CredentialID.Length);

Src/Fido2/Objects/DevicePublicKeyAuthenticatorOutput.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
namespace Fido2NetLib.Objects;
33

44
using System;
5+
56
using Fido2NetLib.Cbor;
6-
using NSec.Cryptography;
77

88

99
public sealed class DevicePublicKeyAuthenticatorOutput

0 commit comments

Comments
 (0)