-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathAuthenticatorData.cs
More file actions
122 lines (94 loc) · 3.47 KB
/
Copy pathAuthenticatorData.cs
File metadata and controls
122 lines (94 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
using System.Buffers.Binary;
namespace NpgsqlRestClient.Fido2;
public class AuthenticatorData
{
public byte[] RpIdHash { get; set; } = [];
public bool UserPresent { get; set; }
public bool UserVerified { get; set; }
public bool BackupEligible { get; set; }
public bool BackedUp { get; set; }
public bool HasAttestedCredentialData { get; set; }
public bool HasExtensions { get; set; }
public uint SignCount { get; set; }
public AttestedCredentialData? AttestedCredentialData { get; set; }
public byte Flags { get; set; }
public static AuthenticatorData? Parse(byte[] data)
{
if (data == null || data.Length < 37)
return null;
try
{
var rpIdHash = data[..32];
var flags = data[32];
var signCount = BinaryPrimitives.ReadUInt32BigEndian(data.AsSpan(33, 4));
var authData = new AuthenticatorData
{
RpIdHash = rpIdHash,
Flags = flags,
UserPresent = (flags & AuthenticatorDataFlags.UP) != 0,
UserVerified = (flags & AuthenticatorDataFlags.UV) != 0,
BackupEligible = (flags & AuthenticatorDataFlags.BE) != 0,
BackedUp = (flags & AuthenticatorDataFlags.BS) != 0,
HasAttestedCredentialData = (flags & AuthenticatorDataFlags.AT) != 0,
HasExtensions = (flags & AuthenticatorDataFlags.ED) != 0,
SignCount = signCount
};
// Parse attested credential data if present
if (authData.HasAttestedCredentialData && data.Length > 37)
{
authData.AttestedCredentialData = AttestedCredentialData.Parse(data.AsSpan(37));
}
return authData;
}
catch
{
return null;
}
}
}
public static class AuthenticatorDataFlags
{
public const byte UP = 0x01;
public const byte UV = 0x04;
public const byte BE = 0x08;
public const byte BS = 0x10;
public const byte AT = 0x40;
public const byte ED = 0x80;
}
public class AttestedCredentialData
{
public byte[] Aaguid { get; set; } = [];
public byte[] CredentialId { get; set; } = [];
public byte[] PublicKeyBytes { get; set; } = [];
public CredentialPublicKey? PublicKey { get; set; }
public int Algorithm => PublicKey?.AlgorithmInt ?? 0;
public static AttestedCredentialData? Parse(ReadOnlySpan<byte> data)
{
// Minimum: 16 (aaguid) + 2 (length) = 18 bytes
if (data.Length < 18)
return null;
try
{
var aaguid = data[..16].ToArray();
var credentialIdLength = BinaryPrimitives.ReadUInt16BigEndian(data.Slice(16, 2));
if (data.Length < 18 + credentialIdLength)
return null;
var credentialId = data.Slice(18, credentialIdLength).ToArray();
// The rest is the COSE public key
var publicKeyData = data[(18 + credentialIdLength)..];
var publicKey = CredentialPublicKey.Decode(publicKeyData.ToArray(), out int bytesRead);
var publicKeyBytes = publicKeyData[..bytesRead].ToArray();
return new AttestedCredentialData
{
Aaguid = aaguid,
CredentialId = credentialId,
PublicKeyBytes = publicKeyBytes,
PublicKey = publicKey
};
}
catch
{
return null;
}
}
}