diff options
Diffstat (limited to 'vendor/github.com/tstranex/u2f/auth.go')
-rw-r--r-- | vendor/github.com/tstranex/u2f/auth.go | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/vendor/github.com/tstranex/u2f/auth.go b/vendor/github.com/tstranex/u2f/auth.go new file mode 100644 index 0000000000..05c25f5731 --- /dev/null +++ b/vendor/github.com/tstranex/u2f/auth.go @@ -0,0 +1,136 @@ +// 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/sha256" + "encoding/asn1" + "errors" + "math/big" + "time" +) + +// SignRequest creates a request to initiate an authentication. +func (c *Challenge) SignRequest(regs []Registration) *WebSignRequest { + var sr WebSignRequest + sr.AppID = c.AppID + sr.Challenge = encodeBase64(c.Challenge) + for _, r := range regs { + rk := getRegisteredKey(c.AppID, r) + sr.RegisteredKeys = append(sr.RegisteredKeys, rk) + } + return &sr +} + +// ErrCounterTooLow is raised when the counter value received from the device is +// lower than last stored counter value. This may indicate that the device has +// been cloned (or is malfunctioning). The application may choose to disable +// the particular device as precaution. +var ErrCounterTooLow = errors.New("u2f: counter too low") + +// Authenticate validates a SignResponse authentication response. +// An error is returned if any part of the response fails to validate. +// The counter should be the counter associated with appropriate device +// (i.e. resp.KeyHandle). +// The latest counter value is returned, which the caller should store. +func (reg *Registration) Authenticate(resp SignResponse, c Challenge, counter uint32) (newCounter uint32, err error) { + if time.Now().Sub(c.Timestamp) > timeout { + return 0, errors.New("u2f: challenge has expired") + } + if resp.KeyHandle != encodeBase64(reg.KeyHandle) { + return 0, errors.New("u2f: wrong key handle") + } + + sigData, err := decodeBase64(resp.SignatureData) + if err != nil { + return 0, err + } + + clientData, err := decodeBase64(resp.ClientData) + if err != nil { + return 0, err + } + + ar, err := parseSignResponse(sigData) + if err != nil { + return 0, err + } + + if ar.Counter < counter { + return 0, ErrCounterTooLow + } + + if err := verifyClientData(clientData, c); err != nil { + return 0, err + } + + if err := verifyAuthSignature(*ar, ®.PubKey, c.AppID, clientData); err != nil { + return 0, err + } + + if !ar.UserPresenceVerified { + return 0, errors.New("u2f: user was not present") + } + + return ar.Counter, nil +} + +type ecdsaSig struct { + R, S *big.Int +} + +type authResp struct { + UserPresenceVerified bool + Counter uint32 + sig ecdsaSig + raw []byte +} + +func parseSignResponse(sd []byte) (*authResp, error) { + if len(sd) < 5 { + return nil, errors.New("u2f: data is too short") + } + + var ar authResp + + userPresence := sd[0] + if userPresence|1 != 1 { + return nil, errors.New("u2f: invalid user presence byte") + } + ar.UserPresenceVerified = userPresence == 1 + + ar.Counter = uint32(sd[1])<<24 | uint32(sd[2])<<16 | uint32(sd[3])<<8 | uint32(sd[4]) + + ar.raw = sd[:5] + + rest, err := asn1.Unmarshal(sd[5:], &ar.sig) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, errors.New("u2f: trailing data") + } + + return &ar, nil +} + +func verifyAuthSignature(ar authResp, pubKey *ecdsa.PublicKey, appID string, clientData []byte) error { + appParam := sha256.Sum256([]byte(appID)) + challenge := sha256.Sum256(clientData) + + var buf []byte + buf = append(buf, appParam[:]...) + buf = append(buf, ar.raw...) + buf = append(buf, challenge[:]...) + hash := sha256.Sum256(buf) + + if !ecdsa.Verify(pubKey, hash[:], ar.sig.R, ar.sig.S) { + return errors.New("u2f: invalid signature") + } + + return nil +} |