-
Notifications
You must be signed in to change notification settings - Fork 35
/
register.go
230 lines (191 loc) · 6.12 KB
/
register.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// Go FIDO U2F Library
// Copyright 2015 The Go FIDO U2F Library Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.
package u2f
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/hex"
"errors"
"time"
)
// Registration represents a single enrolment or pairing between an
// application and a token. This data will typically be stored in a database.
type Registration struct {
// Raw serialized registration data as received from the token.
Raw []byte
KeyHandle []byte
PubKey ecdsa.PublicKey
// AttestationCert can be nil for Authenticate requests.
AttestationCert *x509.Certificate
}
// Config contains configurable options for the package.
type Config struct {
// SkipAttestationVerify controls whether the token attestation
// certificate should be verified on registration. Ideally it should
// always be verified. However, there is currently no public list of
// trusted attestation root certificates so it may be necessary to skip.
SkipAttestationVerify bool
// RootAttestationCertPool overrides the default root certificates used
// to verify client attestations. If nil, this defaults to the roots that are
// bundled in this library.
RootAttestationCertPool *x509.CertPool
}
// Register validates a RegisterResponse message to enrol a new token.
// An error is returned if any part of the response fails to validate.
// The returned Registration should be stored by the caller.
func Register(resp RegisterResponse, c Challenge, config *Config) (*Registration, error) {
if config == nil {
config = &Config{}
}
if time.Now().Sub(c.Timestamp) > timeout {
return nil, errors.New("u2f: challenge has expired")
}
regData, err := decodeBase64(resp.RegistrationData)
if err != nil {
return nil, err
}
clientData, err := decodeBase64(resp.ClientData)
if err != nil {
return nil, err
}
reg, sig, err := parseRegistration(regData)
if err != nil {
return nil, err
}
if err := verifyClientData(clientData, c); err != nil {
return nil, err
}
if err := verifyAttestationCert(*reg, config); err != nil {
return nil, err
}
if err := verifyRegistrationSignature(*reg, sig, c.AppID, clientData); err != nil {
return nil, err
}
return reg, nil
}
func parseRegistration(buf []byte) (*Registration, []byte, error) {
if len(buf) < 1+65+1+1+1 {
return nil, nil, errors.New("u2f: data is too short")
}
var r Registration
r.Raw = buf
if buf[0] != 0x05 {
return nil, nil, errors.New("u2f: invalid reserved byte")
}
buf = buf[1:]
x, y := elliptic.Unmarshal(elliptic.P256(), buf[:65])
if x == nil {
return nil, nil, errors.New("u2f: invalid public key")
}
r.PubKey.Curve = elliptic.P256()
r.PubKey.X = x
r.PubKey.Y = y
buf = buf[65:]
khLen := int(buf[0])
buf = buf[1:]
if len(buf) < khLen {
return nil, nil, errors.New("u2f: invalid key handle")
}
r.KeyHandle = buf[:khLen]
buf = buf[khLen:]
// The length of the x509 cert isn't specified so it has to be inferred
// by parsing. We can't use x509.ParseCertificate yet because it returns
// an error if there are any trailing bytes. So parse raw asn1 as a
// workaround to get the length.
sig, err := asn1.Unmarshal(buf, &asn1.RawValue{})
if err != nil {
return nil, nil, err
}
buf = buf[:len(buf)-len(sig)]
fixCertIfNeed(buf)
cert, err := x509.ParseCertificate(buf)
if err != nil {
return nil, nil, err
}
r.AttestationCert = cert
return &r, sig, nil
}
// UnmarshalBinary implements encoding.BinaryMarshaler.
func (r *Registration) UnmarshalBinary(data []byte) error {
reg, _, err := parseRegistration(data)
if err != nil {
return err
}
*r = *reg
return nil
}
// MarshalBinary implements encoding.BinaryUnmarshaler.
func (r *Registration) MarshalBinary() ([]byte, error) {
return r.Raw, nil
}
func verifyAttestationCert(r Registration, config *Config) error {
if config.SkipAttestationVerify {
return nil
}
rootCertPool := roots
if config.RootAttestationCertPool != nil {
rootCertPool = config.RootAttestationCertPool
}
opts := x509.VerifyOptions{Roots: rootCertPool}
_, err := r.AttestationCert.Verify(opts)
return err
}
func verifyRegistrationSignature(
r Registration, signature []byte, appid string, clientData []byte) error {
appParam := sha256.Sum256([]byte(appid))
challenge := sha256.Sum256(clientData)
buf := []byte{0}
buf = append(buf, appParam[:]...)
buf = append(buf, challenge[:]...)
buf = append(buf, r.KeyHandle...)
pk := elliptic.Marshal(r.PubKey.Curve, r.PubKey.X, r.PubKey.Y)
buf = append(buf, pk...)
return r.AttestationCert.CheckSignature(
x509.ECDSAWithSHA256, buf, signature)
}
func getRegisteredKey(appID string, r Registration) RegisteredKey {
return RegisteredKey{
Version: u2fVersion,
KeyHandle: encodeBase64(r.KeyHandle),
AppID: appID,
}
}
// fixCertIfNeed fixes broken certificates described in
// https://github.com/Yubico/php-u2flib-server/blob/master/src/u2flib_server/U2F.php#L84
func fixCertIfNeed(cert []byte) {
h := sha256.Sum256(cert)
switch hex.EncodeToString(h[:]) {
case
"349bca1031f8c82c4ceca38b9cebf1a69df9fb3b94eed99eb3fb9aa3822d26e8",
"dd574527df608e47ae45fbba75a2afdd5c20fd94a02419381813cd55a2a3398f",
"1d8764f0f7cd1352df6150045c8f638e517270e8b5dda1c63ade9c2280240cae",
"d0edc9a91a1677435a953390865d208c55b3183c6759c9b5a7ff494c322558eb",
"6073c436dcd064a48127ddbf6032ac1a66fd59a0c24434f070d4e564c124c897",
"ca993121846c464d666096d35f13bf44c1b05af205f9b4a1e00cf6cc10c5e511":
// clear the offending byte.
cert[len(cert)-257] = 0
}
}
// NewWebRegisterRequest creates a request to enrol a new token.
// regs is the list of the user's existing registration. The browser will
// refuse to re-register a device if it has an existing registration.
func NewWebRegisterRequest(c *Challenge, regs []Registration) *WebRegisterRequest {
req := RegisterRequest{
Version: u2fVersion,
Challenge: encodeBase64(c.Challenge),
}
rr := WebRegisterRequest{
AppID: c.AppID,
RegisterRequests: []RegisterRequest{req},
}
for _, r := range regs {
rk := getRegisteredKey(c.AppID, r)
rr.RegisteredKeys = append(rr.RegisteredKeys, rk)
}
return &rr
}