/
u2f.go
211 lines (186 loc) · 5.8 KB
/
u2f.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package u2f
import (
"bytes"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"github.com/Symantec/Dominator/lib/log"
"github.com/flynn/u2f/u2fhid"
"github.com/flynn/u2f/u2ftoken"
"github.com/tstranex/u2f"
"io"
"io/ioutil"
"net/http"
"time"
)
const clientDataAuthenticationTypeValue = "navigator.id.getAssertion"
func checkU2FDevices(logger log.Logger) {
// TODO: move this to initialization code, ans pass the device list to this function?
// or maybe pass the token?...
devices, err := u2fhid.Devices()
if err != nil {
logger.Fatal(err)
}
if len(devices) == 0 {
logger.Fatal("no U2F tokens found")
}
// TODO: transform this into an iteration over all found devices
for _, d := range devices {
//d := devices[0]
logger.Printf("manufacturer = %q, product = %q, vid = 0x%04x, pid = 0x%04x", d.Manufacturer, d.Product, d.ProductID, d.VendorID)
dev, err := u2fhid.Open(d)
if err != nil {
logger.Fatal(err)
}
defer dev.Close()
}
}
func doU2FAuthenticate(
client *http.Client,
authCookies []*http.Cookie,
baseURL string,
logger log.DebugLogger) error {
logger.Printf("top of doU2fAuthenticate")
url := baseURL + "/u2f/SignRequest"
signRequest, err := http.NewRequest("GET", url, nil)
if err != nil {
logger.Fatal(err)
}
// Add the login cookies
for _, cookie := range authCookies {
signRequest.AddCookie(cookie)
}
logger.Debugf(0, "Authcookies: %+v", authCookies)
signRequestResp, err := client.Do(signRequest) // Client.Get(targetUrl)
if err != nil {
logger.Printf("Failure to sign request req %s", err)
return err
}
logger.Debugf(0, "Get url request did not failed %+v", signRequestResp)
// Dont defer the body response Close ... as we need to close it explicitly
// in the body of the function so that we can reuse the connection
if signRequestResp.StatusCode != 200 {
signRequestResp.Body.Close()
logger.Printf("got error from call %s, url='%s'\n", signRequestResp.Status, url)
err = errors.New("failed respose from sign request")
return err
}
var webSignRequest u2f.WebSignRequest
if err := json.NewDecoder(signRequestResp.Body).Decode(&webSignRequest); err != nil {
//http.Error(w, "invalid response: "+err.Error(), http.StatusBadRequest)
// return
logger.Fatal(err)
}
io.Copy(ioutil.Discard, signRequestResp.Body)
signRequestResp.Body.Close()
// TODO: move this to initialization code, ans pass the device list to this function?
// or maybe pass the token?...
devices, err := u2fhid.Devices()
if err != nil {
logger.Fatal(err)
return err
}
if len(devices) == 0 {
err = errors.New("no U2F tokens found")
logger.Println(err)
return err
}
// TODO: transform this into an iteration over all found devices
d := devices[0]
logger.Printf("manufacturer = %q, product = %q, vid = 0x%04x, pid = 0x%04x", d.Manufacturer, d.Product, d.ProductID, d.VendorID)
dev, err := u2fhid.Open(d)
if err != nil {
logger.Fatal(err)
}
defer dev.Close()
t := u2ftoken.NewToken(dev)
version, err := t.Version()
if err != nil {
logger.Fatal(err)
}
// TODO: Maybe use Debugf()?
logger.Println("version:", version)
///////
tokenAuthenticationClientData := u2f.ClientData{Typ: clientDataAuthenticationTypeValue, Challenge: webSignRequest.Challenge, Origin: webSignRequest.AppID}
tokenAuthenticationBuf := new(bytes.Buffer)
err = json.NewEncoder(tokenAuthenticationBuf).Encode(tokenAuthenticationClientData)
if err != nil {
logger.Fatal(err)
}
reqSignChallenge := sha256.Sum256(tokenAuthenticationBuf.Bytes())
challenge := make([]byte, 32)
app := make([]byte, 32)
challenge = reqSignChallenge[:]
reqSingApp := sha256.Sum256([]byte(webSignRequest.AppID))
app = reqSingApp[:]
// We find out what key is associated to the currently inserted device.
keyIsKnown := false
var req u2ftoken.AuthenticateRequest
var keyHandle []byte
for _, registeredKey := range webSignRequest.RegisteredKeys {
decodedHandle, err := base64.RawURLEncoding.DecodeString(registeredKey.KeyHandle)
if err != nil {
logger.Fatal(err)
}
keyHandle = decodedHandle
req = u2ftoken.AuthenticateRequest{
Challenge: challenge,
Application: app,
KeyHandle: keyHandle,
}
//logger.Printf("%+v", req)
if err := t.CheckAuthenticate(req); err == nil {
keyIsKnown = true
break
}
}
if !keyIsKnown {
err = errors.New("key is not known")
return err
}
// Now we ask the token to sign/authenticate
logger.Println("authenticating, provide user presence")
var rawBytes []byte
for {
res, err := t.Authenticate(req)
if err == u2ftoken.ErrPresenceRequired {
time.Sleep(200 * time.Millisecond)
continue
} else if err != nil {
logger.Fatal(err)
}
rawBytes = res.RawResponse
logger.Printf("counter = %d, signature = %x", res.Counter, res.Signature)
break
}
// now we do the last request
var signRequestResponse u2f.SignResponse
signRequestResponse.KeyHandle = base64.RawURLEncoding.EncodeToString(keyHandle)
signRequestResponse.SignatureData = base64.RawURLEncoding.EncodeToString(rawBytes)
signRequestResponse.ClientData = base64.RawURLEncoding.EncodeToString(tokenAuthenticationBuf.Bytes())
//
webSignRequestBuf := &bytes.Buffer{}
err = json.NewEncoder(webSignRequestBuf).Encode(signRequestResponse)
if err != nil {
logger.Fatal(err)
}
url = baseURL + "/u2f/SignResponse"
webSignRequest2, err := http.NewRequest("POST", url, webSignRequestBuf)
// Add the login cookies
for _, cookie := range authCookies {
webSignRequest2.AddCookie(cookie)
}
signRequestResp2, err := client.Do(webSignRequest2) // Client.Get(targetUrl)
if err != nil {
logger.Printf("Failure to sign request req %s", err)
return err
}
defer signRequestResp2.Body.Close()
if signRequestResp2.StatusCode != 200 {
logger.Printf("got error from call %s, url='%s'\n", signRequestResp2.Status, url)
return err
}
io.Copy(ioutil.Discard, signRequestResp2.Body)
return nil
}