本站源代码
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.

126 lines
3.0KB

  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. /*
  6. Package u2f implements the server-side parts of the
  7. FIDO Universal 2nd Factor (U2F) specification.
  8. Applications will usually persist Challenge and Registration objects in a
  9. database.
  10. To enrol a new token:
  11. app_id := "http://localhost"
  12. c, _ := NewChallenge(app_id, []string{app_id})
  13. req, _ := u2f.NewWebRegisterRequest(c, existingTokens)
  14. // Send the request to the browser.
  15. var resp RegisterResponse
  16. // Read resp from the browser.
  17. reg, err := Register(resp, c)
  18. if err != nil {
  19. // Registration failed.
  20. }
  21. // Store reg in the database.
  22. To perform an authentication:
  23. var regs []Registration
  24. // Fetch regs from the database.
  25. c, _ := NewChallenge(app_id, []string{app_id})
  26. req, _ := c.SignRequest(regs)
  27. // Send the request to the browser.
  28. var resp SignResponse
  29. // Read resp from the browser.
  30. new_counter, err := reg.Authenticate(resp, c)
  31. if err != nil {
  32. // Authentication failed.
  33. }
  34. reg.Counter = new_counter
  35. // Store updated Registration in the database.
  36. The FIDO U2F specification can be found here:
  37. https://fidoalliance.org/specifications/download
  38. */
  39. package u2f
  40. import (
  41. "crypto/rand"
  42. "crypto/subtle"
  43. "encoding/base64"
  44. "encoding/json"
  45. "errors"
  46. "strings"
  47. "time"
  48. )
  49. const u2fVersion = "U2F_V2"
  50. const timeout = 5 * time.Minute
  51. func decodeBase64(s string) ([]byte, error) {
  52. for i := 0; i < len(s)%4; i++ {
  53. s += "="
  54. }
  55. return base64.URLEncoding.DecodeString(s)
  56. }
  57. func encodeBase64(buf []byte) string {
  58. s := base64.URLEncoding.EncodeToString(buf)
  59. return strings.TrimRight(s, "=")
  60. }
  61. // Challenge represents a single transaction between the server and
  62. // authenticator. This data will typically be stored in a database.
  63. type Challenge struct {
  64. Challenge []byte
  65. Timestamp time.Time
  66. AppID string
  67. TrustedFacets []string
  68. }
  69. // NewChallenge generates a challenge for the given application.
  70. func NewChallenge(appID string, trustedFacets []string) (*Challenge, error) {
  71. challenge := make([]byte, 32)
  72. n, err := rand.Read(challenge)
  73. if err != nil {
  74. return nil, err
  75. }
  76. if n != 32 {
  77. return nil, errors.New("u2f: unable to generate random bytes")
  78. }
  79. var c Challenge
  80. c.Challenge = challenge
  81. c.Timestamp = time.Now()
  82. c.AppID = appID
  83. c.TrustedFacets = trustedFacets
  84. return &c, nil
  85. }
  86. func verifyClientData(clientData []byte, challenge Challenge) error {
  87. var cd ClientData
  88. if err := json.Unmarshal(clientData, &cd); err != nil {
  89. return err
  90. }
  91. foundFacetID := false
  92. for _, facetID := range challenge.TrustedFacets {
  93. if facetID == cd.Origin {
  94. foundFacetID = true
  95. break
  96. }
  97. }
  98. if !foundFacetID {
  99. return errors.New("u2f: untrusted facet id")
  100. }
  101. c := encodeBase64(challenge.Challenge)
  102. if len(c) != len(cd.Challenge) ||
  103. subtle.ConstantTimeCompare([]byte(c), []byte(cd.Challenge)) != 1 {
  104. return errors.New("u2f: challenge does not match")
  105. }
  106. return nil
  107. }
上海开阖软件有限公司 沪ICP备12045867号-1