You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

auth.go 3.4KB

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