-
Notifications
You must be signed in to change notification settings - Fork 2
/
authenticator_data.go
122 lines (108 loc) · 3.85 KB
/
authenticator_data.go
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
package webauthn
import (
"bytes"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
)
// RPIDHashSize is the number of bytes in a SHA256 Hash of the RP ID.
const RPIDHashSize = sha256.Size
// RPIDHash is the SHA-256 hash of the RP ID.
type RPIDHash [RPIDHashSize]byte
// MarshalJSON marshals the RPIDHash for JSON.
func (rpidhash RPIDHash) MarshalJSON() ([]byte, error) {
return json.Marshal(base64.RawURLEncoding.EncodeToString(rpidhash[:]))
}
// ErrInvalidAuthenticatorData indicates the authenticator data is invalid.
var ErrInvalidAuthenticatorData = errors.New("invalid authenticator data")
// The AuthenticatorData encodes contextual bindings made by the authenticator. These bindings are controlled by the
// authenticator itself, and derive their trust from the WebAuthn Relying Party's assessment of the security
// properties of the authenticator.
type AuthenticatorData struct {
// RPIDHash is the SHA-256 hash of the RP ID the credential is scoped to.
RPIDHash RPIDHash
// Flags are the authenticator flags.
Flags AuthenticatorFlags
// SignCount is the signature counter, 32-bit unsigned big-endian integer.
SignCount uint32
// AttestedCredentialData is the attested credential data (if present).
AttestedCredentialData *AttestedCredentialData
// Extensions are extension-defined authenticator data. This is a CBOR map with extension identifiers as keys,
// and authenticator extension outputs as values.
Extensions []byte
}
// UnmarshalAuthenticatorData unmarshals AuthenticatorData according to the data layout described in:
// https://www.w3.org/TR/webauthn-2/#authenticator-data.
//
// rpIdHash: 32 bytes
// flags: 1 byte, bitmask
// signCount: 4 bytes, 32-bit unsigned big-endian integer
// attestedCredentialData: variable
// extensions: variable, cbor map
func UnmarshalAuthenticatorData(raw []byte) (data *AuthenticatorData, remaining []byte, err error) {
data = new(AuthenticatorData)
// unmarshal rpIdHash
if len(raw) < RPIDHashSize {
return nil, nil, fmt.Errorf("%w: missing RPIDHash", ErrInvalidAuthenticatorData)
}
copy(data.RPIDHash[:], raw[:RPIDHashSize])
raw = raw[RPIDHashSize:]
// unmarshal flags
if len(raw) < AuthenticatorFlagsSize {
return nil, nil, fmt.Errorf("%w: missing flags", ErrInvalidAuthenticatorData)
}
data.Flags = AuthenticatorFlags(raw[0])
raw = raw[AuthenticatorFlagsSize:]
// unmarshal sign count
if len(raw) < 4 {
return nil, nil, fmt.Errorf("%w: missing sign count", ErrInvalidAuthenticatorData)
}
data.SignCount = binary.BigEndian.Uint32(raw[:4])
raw = raw[4:]
// unmarshal attested credential data
if data.Flags.AttestedCredentialDataIncluded() {
data.AttestedCredentialData, raw, err = UnmarshalAttestedCredentialData(raw)
if err != nil {
return nil, nil, err
}
}
// unmarshal extension data
if data.Flags.ExtensionDataIncluded() {
data.Extensions, raw, err = extractCBOR(raw)
if err != nil {
return nil, nil, fmt.Errorf("%w: missing extensions", ErrInvalidAuthenticatorData)
}
}
return data, raw, nil
}
// Marshal marshals authenticator data according to the format defined in Unmarshal.
func (authenticatorData *AuthenticatorData) Marshal() ([]byte, error) {
var buf bytes.Buffer
if err := write(&buf, authenticatorData.RPIDHash[:]...); err != nil {
return nil, err
}
if err := write(&buf, byte(authenticatorData.Flags)); err != nil {
return nil, err
}
if err := writeUint32(&buf, authenticatorData.SignCount); err != nil {
return nil, err
}
if authenticatorData.Flags.AttestedCredentialDataIncluded() {
bs, err := authenticatorData.AttestedCredentialData.Marshal()
if err != nil {
return nil, err
}
if err := write(&buf, bs...); err != nil {
return nil, err
}
}
if authenticatorData.Flags.ExtensionDataIncluded() {
if err := write(&buf, authenticatorData.Extensions...); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}