mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Add support for FIDO U2F (#3971)
* Add support for U2F Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add vendor library Add missing translations Signed-off-by: Jonas Franz <info@jonasfranz.software> * Minor improvements Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add U2F support for Firefox, Chrome (Android) by introducing a custom JS library Add U2F error handling Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add U2F login page to OAuth Signed-off-by: Jonas Franz <info@jonasfranz.software> * Move U2F user settings to a separate file Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add unit tests for u2f model Renamed u2f table name Signed-off-by: Jonas Franz <info@jonasfranz.software> * Fix problems caused by refactoring Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add U2F documentation Signed-off-by: Jonas Franz <info@jonasfranz.software> * Remove not needed console.log-s Signed-off-by: Jonas Franz <info@jonasfranz.software> * Add default values to app.ini.sample Add FIDO U2F to comparison Signed-off-by: Jonas Franz <info@jonasfranz.software>
This commit is contained in:
		
							
								
								
									
										21
									
								
								vendor/github.com/tstranex/u2f/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/tstranex/u2f/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2015 The Go FIDO U2F Library Authors
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
							
								
								
									
										97
									
								
								vendor/github.com/tstranex/u2f/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/github.com/tstranex/u2f/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
# Go FIDO U2F Library
 | 
			
		||||
 | 
			
		||||
This Go package implements the parts of the FIDO U2F specification required on
 | 
			
		||||
the server side of an application.
 | 
			
		||||
 | 
			
		||||
[](https://travis-ci.org/tstranex/u2f)
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
- Native Go implementation
 | 
			
		||||
- No dependancies other than the Go standard library
 | 
			
		||||
- Token attestation certificate verification
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
Please visit http://godoc.org/github.com/tstranex/u2f for the full
 | 
			
		||||
documentation.
 | 
			
		||||
 | 
			
		||||
### How to enrol a new token
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
app_id := "http://localhost"
 | 
			
		||||
 | 
			
		||||
// Send registration request to the browser.
 | 
			
		||||
c, _ := NewChallenge(app_id, []string{app_id})
 | 
			
		||||
req, _ := c.RegisterRequest()
 | 
			
		||||
 | 
			
		||||
// Read response from the browser.
 | 
			
		||||
var resp RegisterResponse
 | 
			
		||||
reg, err := Register(resp, c, nil)
 | 
			
		||||
if err != nil {
 | 
			
		||||
    // Registration failed.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Store registration in the database.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### How to perform an authentication
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// Fetch registration and counter from the database.
 | 
			
		||||
var reg Registration
 | 
			
		||||
var counter uint32
 | 
			
		||||
 | 
			
		||||
// Send authentication request to the browser.
 | 
			
		||||
c, _ := NewChallenge(app_id, []string{app_id})
 | 
			
		||||
req, _ := c.SignRequest(reg)
 | 
			
		||||
 | 
			
		||||
// Read response from the browser.
 | 
			
		||||
var resp SignResponse
 | 
			
		||||
newCounter, err := reg.Authenticate(resp, c, counter)
 | 
			
		||||
if err != nil {
 | 
			
		||||
    // Authentication failed.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Store updated counter in the database.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ go get github.com/tstranex/u2f
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Example
 | 
			
		||||
 | 
			
		||||
See u2fdemo/main.go for an full example server. To run it:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ go install github.com/tstranex/u2f/u2fdemo
 | 
			
		||||
$ ./bin/u2fdemo
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Open https://localhost:3483 in Chrome.
 | 
			
		||||
Ignore the SSL warning (due to the self-signed certificate for localhost).
 | 
			
		||||
You can then test registering and authenticating using your token.
 | 
			
		||||
 | 
			
		||||
## Changelog
 | 
			
		||||
 | 
			
		||||
- 2016-12-18: The package has been updated to work with the new
 | 
			
		||||
  U2F Javascript 1.1 API specification. This causes some breaking changes.
 | 
			
		||||
 | 
			
		||||
  `SignRequest` has been replaced by `WebSignRequest` which now includes
 | 
			
		||||
  multiple registrations. This is useful when the user has multiple devices
 | 
			
		||||
  registered since you can now authenticate against any of them with a single
 | 
			
		||||
  request.
 | 
			
		||||
 | 
			
		||||
  `WebRegisterRequest` has been introduced, which should generally be used
 | 
			
		||||
  instead of using `RegisterRequest` directly. It includes the list of existing
 | 
			
		||||
  registrations with the new registration request. If the user's device already
 | 
			
		||||
  matches one of the existing registrations, it will refuse to re-register.
 | 
			
		||||
 | 
			
		||||
  `Challenge.RegisterRequest` has been replaced by `NewWebRegisterRequest`.
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
The Go FIDO U2F Library is licensed under the MIT License.
 | 
			
		||||
							
								
								
									
										136
									
								
								vendor/github.com/tstranex/u2f/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/tstranex/u2f/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								vendor/github.com/tstranex/u2f/certs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								vendor/github.com/tstranex/u2f/certs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
// 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/x509"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const plugUpCert = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIBrjCCAVSgAwIBAgIJAMGSvUZlGSGVMAoGCCqGSM49BAMCMDIxMDAuBgNVBAMM
 | 
			
		||||
J1BsdWctdXAgRklETyBJbnRlcm5hbCBBdHRlc3RhdGlvbiBDQSAjMTAeFw0xNDA5
 | 
			
		||||
MjMxNjM3NTFaFw0zNDA5MjMxNjM3NTFaMDIxMDAuBgNVBAMMJ1BsdWctdXAgRklE
 | 
			
		||||
TyBJbnRlcm5hbCBBdHRlc3RhdGlvbiBDQSAjMTBZMBMGByqGSM49AgEGCCqGSM49
 | 
			
		||||
AwEHA0IABH9mscDgEHo4AUh7J8JHqRxsSVxbvsbe6Pxy5cUFKfQlWNjxRrZcbhOb
 | 
			
		||||
UY3WsAwmKuUdOcghbpTILhdp8LG9z5GjUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYD
 | 
			
		||||
VR0OBBYEFM+nRPKhYlDwOemShePaUOd9sDqoMB8GA1UdIwQYMBaAFM+nRPKhYlDw
 | 
			
		||||
OemShePaUOd9sDqoMAoGCCqGSM49BAMCA0gAMEUCIQDVzqnX1rgvyJaZ7WZUm1ED
 | 
			
		||||
hJKSsDxRXEnH+/voqpq/zgIgH4RUR6vr9YNrkzuCq5R07gF7P4qhtg/4jy+dhl7o
 | 
			
		||||
NAU=
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const neowaveCert = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIICJDCCAcugAwIBAgIJAIo+0R9DGvSBMAoGCCqGSM49BAMCMG8xCzAJBgNVBAYT
 | 
			
		||||
AkZSMQ8wDQYDVQQIDAZGcmFuY2UxETAPBgNVBAcMCEdhcmRhbm5lMRAwDgYDVQQK
 | 
			
		||||
DAdOZW93YXZlMSowKAYDVQQDDCFOZW93YXZlIEtFWURPIEZJRE8gVTJGIENBIEJh
 | 
			
		||||
dGNoIDEwHhcNMTUwMTI4MTA1ODM1WhcNMjUwMTI1MTA1ODM1WjBvMQswCQYDVQQG
 | 
			
		||||
EwJGUjEPMA0GA1UECAwGRnJhbmNlMREwDwYDVQQHDAhHYXJkYW5uZTEQMA4GA1UE
 | 
			
		||||
CgwHTmVvd2F2ZTEqMCgGA1UEAwwhTmVvd2F2ZSBLRVlETyBGSURPIFUyRiBDQSBC
 | 
			
		||||
YXRjaCAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBlUmE1BRE/M/CE/ZCN+x
 | 
			
		||||
eutfnVsThMwIDN+4DL9gqXoKCeRMiDQ1zwm/yQS80BYSEz7Du9RU+2mlnyhwhu+f
 | 
			
		||||
BqNQME4wHQYDVR0OBBYEFF42te8/iq5HGom4sIhgkJWLq5jkMB8GA1UdIwQYMBaA
 | 
			
		||||
FF42te8/iq5HGom4sIhgkJWLq5jkMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwID
 | 
			
		||||
RwAwRAIgVTxBFb2Hclq5Yi5gQp6WoZAcHETfKASvTQVOE88REGQCIA5DcwGVLsZB
 | 
			
		||||
QTb94Xgtb/WUieCvmwukFl/gEO15f3uA
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const yubicoRootCert = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIDHjCCAgagAwIBAgIEG0BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ
 | 
			
		||||
dWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw
 | 
			
		||||
MDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290
 | 
			
		||||
IENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
 | 
			
		||||
AoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk
 | 
			
		||||
5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep
 | 
			
		||||
8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw
 | 
			
		||||
nebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT
 | 
			
		||||
9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw
 | 
			
		||||
LvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ
 | 
			
		||||
hjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN
 | 
			
		||||
BgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4
 | 
			
		||||
MYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt
 | 
			
		||||
hX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k
 | 
			
		||||
LVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U
 | 
			
		||||
sG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc
 | 
			
		||||
U9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const entersektCert = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIICHjCCAcOgAwIBAgIBADAKBggqhkjOPQQDAjBvMQswCQYDVQQGEwJaQTEVMBMG
 | 
			
		||||
A1UECAwMV2VzdGVybiBDYXBlMRUwEwYDVQQHDAxTdGVsbGVuYm9zY2gxEjAQBgNV
 | 
			
		||||
BAoMCUVudGVyc2VrdDELMAkGA1UECwwCSVQxETAPBgNVBAMMCFRyYW5zYWt0MB4X
 | 
			
		||||
DTE0MTEwMTExMjczNFoXDTE1MTEwMTExMjczNFowbzELMAkGA1UEBhMCWkExFTAT
 | 
			
		||||
BgNVBAgMDFdlc3Rlcm4gQ2FwZTEVMBMGA1UEBwwMU3RlbGxlbmJvc2NoMRIwEAYD
 | 
			
		||||
VQQKDAlFbnRlcnNla3QxCzAJBgNVBAsMAklUMREwDwYDVQQDDAhUcmFuc2FrdDBZ
 | 
			
		||||
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBh10blFheMZy3k2iqW9TzLhS1DbJ/Xf
 | 
			
		||||
DxqQJJkpqTLq7vI+K3O4C20YtN0jsVrj7UylWoSRlPL5F7IkbeQ6aZ6jUDBOMB0G
 | 
			
		||||
A1UdDgQWBBQWRFF7mVAipWTdfBWk2B8Dv4Ab4jAfBgNVHSMEGDAWgBQWRFF7mVAi
 | 
			
		||||
pWTdfBWk2B8Dv4Ab4jAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQCo
 | 
			
		||||
bMURXOxv6pqz6ECBh0zgL2vVhEfTOZJOW0PACGalWgIhAME0LHGi6ZS7z9yzHNqi
 | 
			
		||||
cnRb+okM+PIy/hBcBuqTWCbw
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func mustLoadPool(pemCerts []byte) *x509.CertPool {
 | 
			
		||||
	p := x509.NewCertPool()
 | 
			
		||||
	if !p.AppendCertsFromPEM(pemCerts) {
 | 
			
		||||
		log.Fatal("u2f: Error loading root cert pool.")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var roots = mustLoadPool([]byte(yubicoRootCert + entersektCert + neowaveCert + plugUpCert))
 | 
			
		||||
							
								
								
									
										87
									
								
								vendor/github.com/tstranex/u2f/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/tstranex/u2f/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
// 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 (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// JwkKey represents a public key used by a browser for the Channel ID TLS
 | 
			
		||||
// extension.
 | 
			
		||||
type JwkKey struct {
 | 
			
		||||
	KTy string `json:"kty"`
 | 
			
		||||
	Crv string `json:"crv"`
 | 
			
		||||
	X   string `json:"x"`
 | 
			
		||||
	Y   string `json:"y"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClientData as defined by the FIDO U2F Raw Message Formats specification.
 | 
			
		||||
type ClientData struct {
 | 
			
		||||
	Typ       string          `json:"typ"`
 | 
			
		||||
	Challenge string          `json:"challenge"`
 | 
			
		||||
	Origin    string          `json:"origin"`
 | 
			
		||||
	CIDPubKey json.RawMessage `json:"cid_pubkey"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterRequest as defined by the FIDO U2F Javascript API 1.1.
 | 
			
		||||
type RegisterRequest struct {
 | 
			
		||||
	Version   string `json:"version"`
 | 
			
		||||
	Challenge string `json:"challenge"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WebRegisterRequest contains the parameters needed for the u2f.register()
 | 
			
		||||
// high-level Javascript API function as defined by the
 | 
			
		||||
// FIDO U2F Javascript API 1.1.
 | 
			
		||||
type WebRegisterRequest struct {
 | 
			
		||||
	AppID            string            `json:"appId"`
 | 
			
		||||
	RegisterRequests []RegisterRequest `json:"registerRequests"`
 | 
			
		||||
	RegisteredKeys   []RegisteredKey   `json:"registeredKeys"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterResponse as defined by the FIDO U2F Javascript API 1.1.
 | 
			
		||||
type RegisterResponse struct {
 | 
			
		||||
	Version          string `json:"version"`
 | 
			
		||||
	RegistrationData string `json:"registrationData"`
 | 
			
		||||
	ClientData       string `json:"clientData"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisteredKey as defined by the FIDO U2F Javascript API 1.1.
 | 
			
		||||
type RegisteredKey struct {
 | 
			
		||||
	Version   string `json:"version"`
 | 
			
		||||
	KeyHandle string `json:"keyHandle"`
 | 
			
		||||
	AppID     string `json:"appId"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WebSignRequest contains the parameters needed for the u2f.sign()
 | 
			
		||||
// high-level Javascript API function as defined by the
 | 
			
		||||
// FIDO U2F Javascript API 1.1.
 | 
			
		||||
type WebSignRequest struct {
 | 
			
		||||
	AppID          string          `json:"appId"`
 | 
			
		||||
	Challenge      string          `json:"challenge"`
 | 
			
		||||
	RegisteredKeys []RegisteredKey `json:"registeredKeys"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignResponse as defined by the FIDO U2F Javascript API 1.1.
 | 
			
		||||
type SignResponse struct {
 | 
			
		||||
	KeyHandle     string `json:"keyHandle"`
 | 
			
		||||
	SignatureData string `json:"signatureData"`
 | 
			
		||||
	ClientData    string `json:"clientData"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TrustedFacets as defined by the FIDO AppID and Facet Specification.
 | 
			
		||||
type TrustedFacets struct {
 | 
			
		||||
	Version struct {
 | 
			
		||||
		Major int `json:"major"`
 | 
			
		||||
		Minor int `json:"minor"`
 | 
			
		||||
	} `json:"version"`
 | 
			
		||||
	Ids []string `json:"ids"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TrustedFacetsEndpoint is a container of TrustedFacets.
 | 
			
		||||
// It is used as the response for an appId URL endpoint.
 | 
			
		||||
type TrustedFacetsEndpoint struct {
 | 
			
		||||
	TrustedFacets []TrustedFacets `json:"trustedFacets"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										230
									
								
								vendor/github.com/tstranex/u2f/register.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								vendor/github.com/tstranex/u2f/register.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								vendor/github.com/tstranex/u2f/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								vendor/github.com/tstranex/u2f/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,125 @@
 | 
			
		||||
// 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 implements the server-side parts of the
 | 
			
		||||
FIDO Universal 2nd Factor (U2F) specification.
 | 
			
		||||
 | 
			
		||||
Applications will usually persist Challenge and Registration objects in a
 | 
			
		||||
database.
 | 
			
		||||
 | 
			
		||||
To enrol a new token:
 | 
			
		||||
 | 
			
		||||
    app_id := "http://localhost"
 | 
			
		||||
    c, _ := NewChallenge(app_id, []string{app_id})
 | 
			
		||||
    req, _ := u2f.NewWebRegisterRequest(c, existingTokens)
 | 
			
		||||
    // Send the request to the browser.
 | 
			
		||||
    var resp RegisterResponse
 | 
			
		||||
    // Read resp from the browser.
 | 
			
		||||
    reg, err := Register(resp, c)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
         // Registration failed.
 | 
			
		||||
    }
 | 
			
		||||
    // Store reg in the database.
 | 
			
		||||
 | 
			
		||||
To perform an authentication:
 | 
			
		||||
 | 
			
		||||
    var regs []Registration
 | 
			
		||||
    // Fetch regs from the database.
 | 
			
		||||
    c, _ := NewChallenge(app_id, []string{app_id})
 | 
			
		||||
    req, _ := c.SignRequest(regs)
 | 
			
		||||
    // Send the request to the browser.
 | 
			
		||||
    var resp SignResponse
 | 
			
		||||
    // Read resp from the browser.
 | 
			
		||||
    new_counter, err := reg.Authenticate(resp, c)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        // Authentication failed.
 | 
			
		||||
    }
 | 
			
		||||
    reg.Counter = new_counter
 | 
			
		||||
    // Store updated Registration in the database.
 | 
			
		||||
 | 
			
		||||
The FIDO U2F specification can be found here:
 | 
			
		||||
https://fidoalliance.org/specifications/download
 | 
			
		||||
*/
 | 
			
		||||
package u2f
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/subtle"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const u2fVersion = "U2F_V2"
 | 
			
		||||
const timeout = 5 * time.Minute
 | 
			
		||||
 | 
			
		||||
func decodeBase64(s string) ([]byte, error) {
 | 
			
		||||
	for i := 0; i < len(s)%4; i++ {
 | 
			
		||||
		s += "="
 | 
			
		||||
	}
 | 
			
		||||
	return base64.URLEncoding.DecodeString(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func encodeBase64(buf []byte) string {
 | 
			
		||||
	s := base64.URLEncoding.EncodeToString(buf)
 | 
			
		||||
	return strings.TrimRight(s, "=")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Challenge represents a single transaction between the server and
 | 
			
		||||
// authenticator. This data will typically be stored in a database.
 | 
			
		||||
type Challenge struct {
 | 
			
		||||
	Challenge     []byte
 | 
			
		||||
	Timestamp     time.Time
 | 
			
		||||
	AppID         string
 | 
			
		||||
	TrustedFacets []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewChallenge generates a challenge for the given application.
 | 
			
		||||
func NewChallenge(appID string, trustedFacets []string) (*Challenge, error) {
 | 
			
		||||
	challenge := make([]byte, 32)
 | 
			
		||||
	n, err := rand.Read(challenge)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if n != 32 {
 | 
			
		||||
		return nil, errors.New("u2f: unable to generate random bytes")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var c Challenge
 | 
			
		||||
	c.Challenge = challenge
 | 
			
		||||
	c.Timestamp = time.Now()
 | 
			
		||||
	c.AppID = appID
 | 
			
		||||
	c.TrustedFacets = trustedFacets
 | 
			
		||||
	return &c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func verifyClientData(clientData []byte, challenge Challenge) error {
 | 
			
		||||
	var cd ClientData
 | 
			
		||||
	if err := json.Unmarshal(clientData, &cd); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	foundFacetID := false
 | 
			
		||||
	for _, facetID := range challenge.TrustedFacets {
 | 
			
		||||
		if facetID == cd.Origin {
 | 
			
		||||
			foundFacetID = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !foundFacetID {
 | 
			
		||||
		return errors.New("u2f: untrusted facet id")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := encodeBase64(challenge.Challenge)
 | 
			
		||||
	if len(c) != len(cd.Challenge) ||
 | 
			
		||||
		subtle.ConstantTimeCompare([]byte(c), []byte(cd.Challenge)) != 1 {
 | 
			
		||||
		return errors.New("u2f: challenge does not match")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user