1// Go FIDO U2F Library 2// Copyright 2015 The Go FIDO U2F Library Authors. All rights reserved. 3// Use of this source code is governed by the MIT 4// license that can be found in the LICENSE file. 5 6package u2f 7 8import ( 9 "crypto/ecdsa" 10 "crypto/sha256" 11 "encoding/asn1" 12 "errors" 13 "math/big" 14 "time" 15) 16 17// SignRequest creates a request to initiate an authentication. 18func (c *Challenge) SignRequest(regs []Registration) *WebSignRequest { 19 var sr WebSignRequest 20 sr.AppID = c.AppID 21 sr.Challenge = encodeBase64(c.Challenge) 22 for _, r := range regs { 23 rk := getRegisteredKey(c.AppID, r) 24 sr.RegisteredKeys = append(sr.RegisteredKeys, rk) 25 } 26 return &sr 27} 28 29// ErrCounterTooLow is raised when the counter value received from the device is 30// lower than last stored counter value. This may indicate that the device has 31// been cloned (or is malfunctioning). The application may choose to disable 32// the particular device as precaution. 33var ErrCounterTooLow = errors.New("u2f: counter too low") 34 35// Authenticate validates a SignResponse authentication response. 36// An error is returned if any part of the response fails to validate. 37// The counter should be the counter associated with appropriate device 38// (i.e. resp.KeyHandle). 39// The latest counter value is returned, which the caller should store. 40func (reg *Registration) Authenticate(resp SignResponse, c Challenge, counter uint32) (newCounter uint32, err error) { 41 if time.Now().Sub(c.Timestamp) > timeout { 42 return 0, errors.New("u2f: challenge has expired") 43 } 44 if resp.KeyHandle != encodeBase64(reg.KeyHandle) { 45 return 0, errors.New("u2f: wrong key handle") 46 } 47 48 sigData, err := decodeBase64(resp.SignatureData) 49 if err != nil { 50 return 0, err 51 } 52 53 clientData, err := decodeBase64(resp.ClientData) 54 if err != nil { 55 return 0, err 56 } 57 58 ar, err := parseSignResponse(sigData) 59 if err != nil { 60 return 0, err 61 } 62 63 if ar.Counter < counter { 64 return 0, ErrCounterTooLow 65 } 66 67 if err := verifyClientData(clientData, c); err != nil { 68 return 0, err 69 } 70 71 if err := verifyAuthSignature(*ar, ®.PubKey, c.AppID, clientData); err != nil { 72 return 0, err 73 } 74 75 if !ar.UserPresenceVerified { 76 return 0, errors.New("u2f: user was not present") 77 } 78 79 return ar.Counter, nil 80} 81 82type ecdsaSig struct { 83 R, S *big.Int 84} 85 86type authResp struct { 87 UserPresenceVerified bool 88 Counter uint32 89 sig ecdsaSig 90 raw []byte 91} 92 93func parseSignResponse(sd []byte) (*authResp, error) { 94 if len(sd) < 5 { 95 return nil, errors.New("u2f: data is too short") 96 } 97 98 var ar authResp 99 100 userPresence := sd[0] 101 if userPresence|1 != 1 { 102 return nil, errors.New("u2f: invalid user presence byte") 103 } 104 ar.UserPresenceVerified = userPresence == 1 105 106 ar.Counter = uint32(sd[1])<<24 | uint32(sd[2])<<16 | uint32(sd[3])<<8 | uint32(sd[4]) 107 108 ar.raw = sd[:5] 109 110 rest, err := asn1.Unmarshal(sd[5:], &ar.sig) 111 if err != nil { 112 return nil, err 113 } 114 if len(rest) != 0 { 115 return nil, errors.New("u2f: trailing data") 116 } 117 118 return &ar, nil 119} 120 121func verifyAuthSignature(ar authResp, pubKey *ecdsa.PublicKey, appID string, clientData []byte) error { 122 appParam := sha256.Sum256([]byte(appID)) 123 challenge := sha256.Sum256(clientData) 124 125 var buf []byte 126 buf = append(buf, appParam[:]...) 127 buf = append(buf, ar.raw...) 128 buf = append(buf, challenge[:]...) 129 hash := sha256.Sum256(buf) 130 131 if !ecdsa.Verify(pubKey, hash[:], ar.sig.R, ar.sig.S) { 132 return errors.New("u2f: invalid signature") 133 } 134 135 return nil 136} 137