@reegaviljoen/eldlock 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +285 -0
- package/bin/eldlock +11 -0
- package/docs/architecture.md +164 -0
- package/docs/threat-model.md +47 -0
- package/eldlock-cli/README.md +56 -0
- package/eldlock-cli/bin/eldlock +3 -0
- package/eldlock-cli/package-lock.json +805 -0
- package/eldlock-cli/package.json +71 -0
- package/eldlock-cli/src/api.ts +250 -0
- package/eldlock-cli/src/cli.ts +490 -0
- package/eldlock-cli/src/main.ts +10 -0
- package/eldlock-cli/src/tui.ts +676 -0
- package/eldlock-cli/tsconfig.json +13 -0
- package/eldlock-cli/vendor/npm/ansi-regex-6.2.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/bun-ffi-structs-0.2.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/diff-9.0.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/emoji-regex-10.6.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-darwin-arm64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-darwin-x64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-linux-arm64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-linux-x64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/fsevents-2.3.3.tgz +0 -0
- package/eldlock-cli/vendor/npm/get-east-asian-width-1.6.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/marked-17.0.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-darwin-arm64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-darwin-x64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-linux-arm64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-linux-x64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/string-width-7.2.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/strip-ansi-7.1.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/tsx-4.22.4.tgz +0 -0
- package/eldlock-cli/vendor/npm/types-node-22.19.19.tgz +0 -0
- package/eldlock-cli/vendor/npm/typescript-5.9.3.tgz +0 -0
- package/eldlock-cli/vendor/npm/undici-types-6.21.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/web-tree-sitter-0.25.10.tgz +0 -0
- package/eldlock-cli/vendor/npm/yoga-layout-3.2.1.tgz +0 -0
- package/eldlock-server/cmd/eldlock-server/main.go +132 -0
- package/eldlock-server/go.mod +10 -0
- package/eldlock-server/go.sum +11 -0
- package/eldlock-server/internal/api/README.md +14 -0
- package/eldlock-server/internal/api/core.go +126 -0
- package/eldlock-server/internal/api/exec.go +97 -0
- package/eldlock-server/internal/api/secrets.go +358 -0
- package/eldlock-server/internal/api/server.go +72 -0
- package/eldlock-server/internal/api/service_test.go +416 -0
- package/eldlock-server/internal/api/types.go +48 -0
- package/eldlock-server/internal/api/vault.go +69 -0
- package/eldlock-server/internal/api/vendor.go +44 -0
- package/eldlock-server/internal/libfido2/LICENSE +21 -0
- package/eldlock-server/internal/libfido2/README.md +127 -0
- package/eldlock-server/internal/libfido2/examples_test.go +614 -0
- package/eldlock-server/internal/libfido2/fido2.go +1234 -0
- package/eldlock-server/internal/libfido2/fido2_darwin.go +7 -0
- package/eldlock-server/internal/libfido2/fido2_other.go +9 -0
- package/eldlock-server/internal/libfido2/fido2_test.go +101 -0
- package/eldlock-server/internal/libfido2/go.mod +10 -0
- package/eldlock-server/internal/libfido2/go.sum +16 -0
- package/eldlock-server/internal/libfido2/log.go +87 -0
- package/eldlock-server/internal/store/README.md +7 -0
- package/eldlock-server/internal/store/store.go +434 -0
- package/eldlock-server/internal/store/store_test.go +125 -0
- package/eldlock-server/internal/yubikey/README.md +25 -0
- package/eldlock-server/internal/yubikey/default_fido2.go +7 -0
- package/eldlock-server/internal/yubikey/default_stub.go +7 -0
- package/eldlock-server/internal/yubikey/fido2_disabled.go +9 -0
- package/eldlock-server/internal/yubikey/fido2_libfido2.go +225 -0
- package/eldlock-server/internal/yubikey/fido2_libfido2_test.go +66 -0
- package/eldlock-server/internal/yubikey/passkey.go +139 -0
- package/eldlock-server/internal/yubikey/passkey_test.go +36 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/LICENSE +21 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/README.md +127 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2.go +1234 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2_darwin.go +7 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2_other.go +9 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/log.go +87 -0
- package/eldlock-server/vendor/github.com/pkg/errors/.travis.yml +10 -0
- package/eldlock-server/vendor/github.com/pkg/errors/LICENSE +23 -0
- package/eldlock-server/vendor/github.com/pkg/errors/Makefile +44 -0
- package/eldlock-server/vendor/github.com/pkg/errors/README.md +59 -0
- package/eldlock-server/vendor/github.com/pkg/errors/appveyor.yml +32 -0
- package/eldlock-server/vendor/github.com/pkg/errors/errors.go +288 -0
- package/eldlock-server/vendor/github.com/pkg/errors/go113.go +38 -0
- package/eldlock-server/vendor/github.com/pkg/errors/stack.go +177 -0
- package/eldlock-server/vendor/modules.txt +7 -0
- package/examples/eldlock.toml +17 -0
- package/install.sh +66 -0
- package/package.json +66 -0
- package/scripts/build-production.mjs +177 -0
- package/scripts/postinstall-production.mjs +23 -0
|
@@ -0,0 +1,1234 @@
|
|
|
1
|
+
package libfido2
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
#include <fido.h>
|
|
5
|
+
#include <fido/bio.h>
|
|
6
|
+
#include <fido/credman.h>
|
|
7
|
+
#include <stdlib.h>
|
|
8
|
+
*/
|
|
9
|
+
import "C"
|
|
10
|
+
import (
|
|
11
|
+
"crypto/rand"
|
|
12
|
+
"encoding/hex"
|
|
13
|
+
"fmt"
|
|
14
|
+
"sync"
|
|
15
|
+
"unsafe"
|
|
16
|
+
|
|
17
|
+
"github.com/pkg/errors"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
// TODO: fido_assert_verify
|
|
21
|
+
|
|
22
|
+
func init() {
|
|
23
|
+
C.fido_init(0) // C.FIDO_DEBUG)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Device ...
|
|
27
|
+
type Device struct {
|
|
28
|
+
path string
|
|
29
|
+
|
|
30
|
+
// Device instance if open.
|
|
31
|
+
dev *C.fido_dev_t
|
|
32
|
+
sync.Mutex
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// DeviceLocation ...
|
|
36
|
+
type DeviceLocation struct {
|
|
37
|
+
Path string
|
|
38
|
+
ProductID int16
|
|
39
|
+
VendorID int16
|
|
40
|
+
Manufacturer string
|
|
41
|
+
Product string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// HIDInfo ...
|
|
45
|
+
type HIDInfo struct {
|
|
46
|
+
Protocol uint8
|
|
47
|
+
Major uint8
|
|
48
|
+
Minor uint8
|
|
49
|
+
Build uint8
|
|
50
|
+
Flags uint8
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Option ...
|
|
54
|
+
type Option struct {
|
|
55
|
+
Name string
|
|
56
|
+
Value OptionValue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// DeviceInfo ...
|
|
60
|
+
type DeviceInfo struct {
|
|
61
|
+
Versions []string
|
|
62
|
+
Extensions []string
|
|
63
|
+
AAGUID []byte
|
|
64
|
+
Options []Option
|
|
65
|
+
Protocols []byte
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// DeviceType is latest type the device supports.
|
|
69
|
+
type DeviceType string
|
|
70
|
+
|
|
71
|
+
const (
|
|
72
|
+
// UnknownDevice ...
|
|
73
|
+
UnknownDevice DeviceType = ""
|
|
74
|
+
// FIDO2 ...
|
|
75
|
+
FIDO2 DeviceType = "fido2"
|
|
76
|
+
// U2F ...
|
|
77
|
+
U2F DeviceType = "u2f"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
// RelyingParty ...
|
|
81
|
+
type RelyingParty struct {
|
|
82
|
+
ID string
|
|
83
|
+
Name string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// User ...
|
|
87
|
+
type User struct {
|
|
88
|
+
ID []byte
|
|
89
|
+
Name string
|
|
90
|
+
DisplayName string
|
|
91
|
+
Icon string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Attestation from MakeCredential ...
|
|
95
|
+
type Attestation struct {
|
|
96
|
+
ClientDataHash []byte
|
|
97
|
+
AuthData []byte
|
|
98
|
+
CredentialID []byte
|
|
99
|
+
CredentialType CredentialType
|
|
100
|
+
PubKey []byte
|
|
101
|
+
Cert []byte
|
|
102
|
+
Sig []byte
|
|
103
|
+
Format string
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Credential ...
|
|
107
|
+
type Credential struct {
|
|
108
|
+
ID []byte
|
|
109
|
+
Type CredentialType
|
|
110
|
+
User User
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// CredentialType ...
|
|
114
|
+
type CredentialType int
|
|
115
|
+
|
|
116
|
+
const (
|
|
117
|
+
// ES256 ...
|
|
118
|
+
ES256 CredentialType = -7
|
|
119
|
+
// EDDSA ...
|
|
120
|
+
EDDSA CredentialType = -8
|
|
121
|
+
|
|
122
|
+
// ECDHES256 COSEAlgorithm = -25
|
|
123
|
+
|
|
124
|
+
// RS256 ...
|
|
125
|
+
RS256 CredentialType = -257
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
func (c CredentialType) String() string {
|
|
129
|
+
switch c {
|
|
130
|
+
case ES256:
|
|
131
|
+
return "es256"
|
|
132
|
+
case EDDSA:
|
|
133
|
+
return "eddsa"
|
|
134
|
+
case RS256:
|
|
135
|
+
return "rs256"
|
|
136
|
+
default:
|
|
137
|
+
return fmt.Sprintf("COSE(%d)", c)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Extension ...
|
|
142
|
+
type Extension string
|
|
143
|
+
|
|
144
|
+
const (
|
|
145
|
+
// HMACSecretExtension for HMAC secret extension.
|
|
146
|
+
// https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#sctn-hmac-secret-extension
|
|
147
|
+
HMACSecretExtension Extension = "hmac-secret"
|
|
148
|
+
// CredProtectExtension for credProtect extension.
|
|
149
|
+
CredProtectExtension Extension = "credProtect"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
// Assertion ...
|
|
153
|
+
type Assertion struct {
|
|
154
|
+
// AuthDataCBOR is CBOR encoded authdata.
|
|
155
|
+
// TODO: Include "raw" authdata if that is added to libfido2.
|
|
156
|
+
AuthDataCBOR []byte
|
|
157
|
+
Sig []byte
|
|
158
|
+
HMACSecret []byte
|
|
159
|
+
CredentialID []byte
|
|
160
|
+
User User
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func extensionsInt(extensions []Extension) int {
|
|
164
|
+
exts := 0
|
|
165
|
+
for _, extension := range extensions {
|
|
166
|
+
switch extension {
|
|
167
|
+
case HMACSecretExtension:
|
|
168
|
+
exts |= int(C.FIDO_EXT_HMAC_SECRET)
|
|
169
|
+
case CredProtectExtension:
|
|
170
|
+
exts |= int(C.FIDO_EXT_CRED_PROTECT)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return exts
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// OptionValue is value for option.
|
|
177
|
+
type OptionValue string
|
|
178
|
+
|
|
179
|
+
const (
|
|
180
|
+
// Default is device default (omitted).
|
|
181
|
+
Default OptionValue = ""
|
|
182
|
+
// True is enabled/yes/true option.
|
|
183
|
+
True OptionValue = "true"
|
|
184
|
+
// False is disabled/no/false option.
|
|
185
|
+
False OptionValue = "false"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
// CredentialsInfo ...
|
|
189
|
+
type CredentialsInfo struct {
|
|
190
|
+
RKExisting int64
|
|
191
|
+
RKRemaining int64
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const maxDevices = 64
|
|
195
|
+
|
|
196
|
+
// DeviceLocations lists found devices.
|
|
197
|
+
func DeviceLocations() ([]*DeviceLocation, error) {
|
|
198
|
+
logger.Debugf("Finding devices...")
|
|
199
|
+
cMax := C.size_t(maxDevices)
|
|
200
|
+
info := C.fido_dev_info_new(cMax)
|
|
201
|
+
var cFound C.size_t = 0
|
|
202
|
+
cErr := C.fido_dev_info_manifest(info, cMax, &cFound)
|
|
203
|
+
if cErr != C.FIDO_OK {
|
|
204
|
+
return nil, errors.Errorf("fido_dev_info_manifest error %d", cErr)
|
|
205
|
+
}
|
|
206
|
+
defer C.fido_dev_info_free(&info, C.size_t(maxDevices))
|
|
207
|
+
found := int(cFound)
|
|
208
|
+
|
|
209
|
+
locs := make([]*DeviceLocation, 0, found)
|
|
210
|
+
for i := 0; i < found; i++ {
|
|
211
|
+
cIdx := C.size_t(i)
|
|
212
|
+
devInfo := C.fido_dev_info_ptr(info, cIdx)
|
|
213
|
+
if devInfo == nil {
|
|
214
|
+
return nil, errors.Errorf("device info is empty")
|
|
215
|
+
}
|
|
216
|
+
cPath := C.fido_dev_info_path(devInfo)
|
|
217
|
+
cProductID := C.fido_dev_info_product(devInfo)
|
|
218
|
+
cVendorID := C.fido_dev_info_vendor(devInfo)
|
|
219
|
+
cManufacturer := C.fido_dev_info_manufacturer_string(devInfo)
|
|
220
|
+
cProduct := C.fido_dev_info_product_string(devInfo)
|
|
221
|
+
|
|
222
|
+
locs = append(locs, &DeviceLocation{
|
|
223
|
+
Path: C.GoString(cPath),
|
|
224
|
+
ProductID: int16(cProductID),
|
|
225
|
+
VendorID: int16(cVendorID),
|
|
226
|
+
Manufacturer: C.GoString(cManufacturer),
|
|
227
|
+
Product: C.GoString(cProduct),
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
return locs, nil
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// NewDevice opens device at path.
|
|
234
|
+
func NewDevice(path string) (*Device, error) {
|
|
235
|
+
if path == "" {
|
|
236
|
+
return nil, errors.Errorf("empty device path")
|
|
237
|
+
}
|
|
238
|
+
return &Device{
|
|
239
|
+
path: fmt.Sprintf("%s", path),
|
|
240
|
+
}, nil
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
func (d *Device) open() (*C.fido_dev_t, error) {
|
|
244
|
+
dev := C.fido_dev_new()
|
|
245
|
+
if cErr := C.fido_dev_open(dev, C.CString(d.path)); cErr != C.FIDO_OK {
|
|
246
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to open")
|
|
247
|
+
}
|
|
248
|
+
d.dev = dev
|
|
249
|
+
return dev, nil
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
func (d *Device) close(dev *C.fido_dev_t) {
|
|
253
|
+
d.Lock()
|
|
254
|
+
d.dev = nil
|
|
255
|
+
d.Unlock()
|
|
256
|
+
|
|
257
|
+
if cErr := C.fido_dev_close(dev); cErr != C.FIDO_OK {
|
|
258
|
+
logger.Errorf("%v", errors.Wrap(errFromCode(cErr), "failed to close"))
|
|
259
|
+
}
|
|
260
|
+
C.fido_dev_free(&dev)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Cancel an action.
|
|
264
|
+
func (d *Device) Cancel() error {
|
|
265
|
+
d.Lock()
|
|
266
|
+
defer d.Unlock()
|
|
267
|
+
if d.dev != nil {
|
|
268
|
+
if cErr := C.fido_dev_cancel(d.dev); cErr != C.FIDO_OK {
|
|
269
|
+
return errors.Wrap(errFromCode(cErr), "failed to cancel")
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return nil
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// CTAPHIDInfo ...
|
|
276
|
+
func (d *Device) CTAPHIDInfo() (*HIDInfo, error) {
|
|
277
|
+
dev, err := d.open()
|
|
278
|
+
if err != nil {
|
|
279
|
+
return nil, err
|
|
280
|
+
}
|
|
281
|
+
defer d.close(dev)
|
|
282
|
+
|
|
283
|
+
protocol := C.fido_dev_protocol(dev)
|
|
284
|
+
major := C.fido_dev_major(dev)
|
|
285
|
+
minor := C.fido_dev_minor(dev)
|
|
286
|
+
build := C.fido_dev_build(dev)
|
|
287
|
+
flags := C.fido_dev_flags(dev)
|
|
288
|
+
|
|
289
|
+
return &HIDInfo{
|
|
290
|
+
Protocol: uint8(protocol),
|
|
291
|
+
Major: uint8(major),
|
|
292
|
+
Minor: uint8(minor),
|
|
293
|
+
Build: uint8(build),
|
|
294
|
+
Flags: uint8(flags),
|
|
295
|
+
}, nil
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// IsFIDO2 returns true if device supports FIDO2.
|
|
299
|
+
func (d *Device) IsFIDO2() (bool, error) {
|
|
300
|
+
dev, err := d.open()
|
|
301
|
+
if err != nil {
|
|
302
|
+
return false, err
|
|
303
|
+
}
|
|
304
|
+
defer d.close(dev)
|
|
305
|
+
|
|
306
|
+
isFIDO2 := bool(C.fido_dev_is_fido2(dev))
|
|
307
|
+
return isFIDO2, nil
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Type returns device type.
|
|
311
|
+
func (d *Device) Type() (DeviceType, error) {
|
|
312
|
+
dev, err := d.open()
|
|
313
|
+
if err != nil {
|
|
314
|
+
return UnknownDevice, err
|
|
315
|
+
}
|
|
316
|
+
defer d.close(dev)
|
|
317
|
+
|
|
318
|
+
isFIDO2 := bool(C.fido_dev_is_fido2(dev))
|
|
319
|
+
if isFIDO2 {
|
|
320
|
+
return FIDO2, nil
|
|
321
|
+
}
|
|
322
|
+
return U2F, nil
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Info represents authenticatorGetInfo (0x04).
|
|
326
|
+
// https://fidoalliance.org/specs/fido2/fido-client-to-authenticator-protocol-v2.1-rd-20191217.html#authenticatorGetInfo
|
|
327
|
+
func (d *Device) Info() (*DeviceInfo, error) {
|
|
328
|
+
dev, err := d.open()
|
|
329
|
+
if err != nil {
|
|
330
|
+
return nil, err
|
|
331
|
+
}
|
|
332
|
+
defer d.close(dev)
|
|
333
|
+
|
|
334
|
+
isFIDO2 := bool(C.fido_dev_is_fido2(dev))
|
|
335
|
+
if !isFIDO2 {
|
|
336
|
+
return nil, ErrNotFIDO2
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
info := C.fido_cbor_info_new()
|
|
340
|
+
defer C.fido_cbor_info_free(&info)
|
|
341
|
+
|
|
342
|
+
if cErr := C.fido_dev_get_cbor_info(dev, info); cErr != C.FIDO_OK {
|
|
343
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to get info")
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
var aaguid []byte
|
|
347
|
+
var protocols []byte
|
|
348
|
+
var extensions []string
|
|
349
|
+
var versions []string
|
|
350
|
+
var options []Option
|
|
351
|
+
|
|
352
|
+
cAAGUIDLen := C.fido_cbor_info_aaguid_len(info)
|
|
353
|
+
cAAGUIDPtr := C.fido_cbor_info_aaguid_ptr(info)
|
|
354
|
+
if cAAGUIDPtr != nil {
|
|
355
|
+
aaguid = C.GoBytes(unsafe.Pointer(cAAGUIDPtr), C.int(cAAGUIDLen))
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
cProtocolsLen := C.fido_cbor_info_protocols_len(info)
|
|
359
|
+
cProtocolsPtr := C.fido_cbor_info_protocols_ptr(info)
|
|
360
|
+
if cProtocolsPtr != nil {
|
|
361
|
+
protocols = C.GoBytes(unsafe.Pointer(cProtocolsPtr), C.int(cProtocolsLen))
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
cExtensionsLen := C.fido_cbor_info_extensions_len(info)
|
|
365
|
+
cExtensionsPtr := C.fido_cbor_info_extensions_ptr(info)
|
|
366
|
+
if cExtensionsPtr != nil {
|
|
367
|
+
extensions = goStrings(C.int(cExtensionsLen), cExtensionsPtr)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
cVersionsLen := C.fido_cbor_info_versions_len(info)
|
|
371
|
+
cVersionsPtr := C.fido_cbor_info_versions_ptr(info)
|
|
372
|
+
if cVersionsPtr != nil {
|
|
373
|
+
versions = goStrings(C.int(cVersionsLen), cVersionsPtr)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
cOptionsLen := C.fido_cbor_info_options_len(info)
|
|
377
|
+
cOptionsNamePtr := C.fido_cbor_info_options_name_ptr(info)
|
|
378
|
+
cOptionsValuePtr := C.fido_cbor_info_options_value_ptr(info)
|
|
379
|
+
if cOptionsNamePtr != nil {
|
|
380
|
+
names := goStrings(C.int(cOptionsLen), cOptionsNamePtr)
|
|
381
|
+
values := goBools(C.int(cOptionsLen), cOptionsValuePtr)
|
|
382
|
+
|
|
383
|
+
options = make([]Option, 0, len(names))
|
|
384
|
+
for i, name := range names {
|
|
385
|
+
val := False
|
|
386
|
+
if values[i] {
|
|
387
|
+
val = True
|
|
388
|
+
}
|
|
389
|
+
options = append(options, Option{Name: name, Value: val})
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return &DeviceInfo{
|
|
394
|
+
AAGUID: aaguid,
|
|
395
|
+
Protocols: protocols,
|
|
396
|
+
Versions: versions,
|
|
397
|
+
Extensions: extensions,
|
|
398
|
+
Options: options,
|
|
399
|
+
}, nil
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// MakeCredentialOpts ...
|
|
403
|
+
type MakeCredentialOpts struct {
|
|
404
|
+
Extensions []Extension
|
|
405
|
+
RK OptionValue
|
|
406
|
+
UV OptionValue
|
|
407
|
+
CredProtect CredProtect
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// CredProtect option if extension is supported.
|
|
411
|
+
type CredProtect string
|
|
412
|
+
|
|
413
|
+
const (
|
|
414
|
+
// CredProtectNone if unset.
|
|
415
|
+
CredProtectNone CredProtect = ""
|
|
416
|
+
// CredProtectUVOptional UV optional
|
|
417
|
+
CredProtectUVOptional CredProtect = "uv-optional"
|
|
418
|
+
// CredProtectUVOptionalWithID UV optional with ID
|
|
419
|
+
CredProtectUVOptionalWithID CredProtect = "uv-optional-with-id"
|
|
420
|
+
// CredProtectUVRequired UV required
|
|
421
|
+
CredProtectUVRequired CredProtect = "uv-required"
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
// MakeCredential represents authenticatorMakeCredential (0x01).
|
|
425
|
+
// RP, User ID and name are required by some devices, so we return an error if missing.
|
|
426
|
+
//
|
|
427
|
+
// See https://fidoalliance.org/specs/fido2/fido-client-to-authenticator-protocol-v2.1-rd-20191217.html#authenticatorMakeCredential
|
|
428
|
+
func (d *Device) MakeCredential(
|
|
429
|
+
clientDataHash []byte,
|
|
430
|
+
rp RelyingParty,
|
|
431
|
+
user User,
|
|
432
|
+
typ CredentialType,
|
|
433
|
+
pin string,
|
|
434
|
+
opts *MakeCredentialOpts) (*Attestation, error) {
|
|
435
|
+
|
|
436
|
+
if opts == nil {
|
|
437
|
+
opts = &MakeCredentialOpts{}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if rp.ID == "" {
|
|
441
|
+
return nil, errors.Errorf("no rp id specified")
|
|
442
|
+
}
|
|
443
|
+
// if rp.Name == "" {
|
|
444
|
+
// return nil, errors.Errorf("no rp name specified")
|
|
445
|
+
// }
|
|
446
|
+
if len(user.ID) == 0 {
|
|
447
|
+
return nil, errors.Errorf("no user id specified")
|
|
448
|
+
}
|
|
449
|
+
if user.Name == "" {
|
|
450
|
+
return nil, errors.Errorf("no user name specified")
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
dev, err := d.open()
|
|
454
|
+
if err != nil {
|
|
455
|
+
return nil, err
|
|
456
|
+
}
|
|
457
|
+
defer d.close(dev)
|
|
458
|
+
|
|
459
|
+
cCred := C.fido_cred_new()
|
|
460
|
+
defer C.fido_cred_free(&cCred)
|
|
461
|
+
if cErr := C.fido_cred_set_clientdata_hash(cCred, cBytes(clientDataHash), cLen(clientDataHash)); cErr != C.FIDO_OK {
|
|
462
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set client data hash")
|
|
463
|
+
}
|
|
464
|
+
if cErr := C.fido_cred_set_rp(cCred, C.CString(rp.ID), cStringOrNil(rp.Name)); cErr != C.FIDO_OK {
|
|
465
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set rp")
|
|
466
|
+
}
|
|
467
|
+
if cErr := C.fido_cred_set_user(cCred, cBytes(user.ID), cLen(user.ID), cStringOrNil(user.Name), cStringOrNil(user.DisplayName), cStringOrNil(user.Icon)); cErr != C.FIDO_OK {
|
|
468
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set user")
|
|
469
|
+
}
|
|
470
|
+
if cErr := C.fido_cred_set_type(cCred, C.int(typ)); cErr != C.FIDO_OK {
|
|
471
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set type")
|
|
472
|
+
}
|
|
473
|
+
cRK, err := cOpt(opts.RK)
|
|
474
|
+
if err != nil {
|
|
475
|
+
return nil, err
|
|
476
|
+
}
|
|
477
|
+
if cErr := C.fido_cred_set_rk(cCred, cRK); cErr != C.FIDO_OK {
|
|
478
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set rk")
|
|
479
|
+
}
|
|
480
|
+
cUV, err := cOpt(opts.UV)
|
|
481
|
+
if err != nil {
|
|
482
|
+
return nil, err
|
|
483
|
+
}
|
|
484
|
+
if cErr := C.fido_cred_set_uv(cCred, cUV); cErr != C.FIDO_OK {
|
|
485
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set uv")
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if opts.CredProtect != CredProtectNone {
|
|
489
|
+
cProt, err := cCredProtect(opts.CredProtect)
|
|
490
|
+
if err != nil {
|
|
491
|
+
return nil, err
|
|
492
|
+
}
|
|
493
|
+
if cErr := C.fido_cred_set_prot(cCred, cProt); cErr != C.FIDO_OK {
|
|
494
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set prot")
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if exts := extensionsInt(opts.Extensions); exts > 0 {
|
|
499
|
+
if cErr := C.fido_cred_set_extensions(cCred, C.int(exts)); cErr != C.FIDO_OK {
|
|
500
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set extensions")
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if cErr := C.fido_dev_make_cred(dev, cCred, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
505
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to make credential")
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
at, err := attestation(cCred)
|
|
509
|
+
if err != nil {
|
|
510
|
+
return nil, err
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return at, nil
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
func attestation(cCred *C.fido_cred_t) (*Attestation, error) {
|
|
517
|
+
cAuthDataLen := C.fido_cred_authdata_len(cCred)
|
|
518
|
+
cAuthDataPtr := C.fido_cred_authdata_ptr(cCred)
|
|
519
|
+
authData := C.GoBytes(unsafe.Pointer(cAuthDataPtr), C.int(cAuthDataLen))
|
|
520
|
+
|
|
521
|
+
cClientDataHashLen := C.fido_cred_clientdata_hash_len(cCred)
|
|
522
|
+
cClientDataHashPtr := C.fido_cred_clientdata_hash_ptr(cCred)
|
|
523
|
+
clientDataHashOut := C.GoBytes(unsafe.Pointer(cClientDataHashPtr), C.int(cClientDataHashLen))
|
|
524
|
+
|
|
525
|
+
cIDLen := C.fido_cred_id_len(cCred)
|
|
526
|
+
cIDPtr := C.fido_cred_id_ptr(cCred)
|
|
527
|
+
id := C.GoBytes(unsafe.Pointer(cIDPtr), C.int(cIDLen))
|
|
528
|
+
|
|
529
|
+
cFormat := C.fido_cred_fmt(cCred)
|
|
530
|
+
typOut := CredentialType(C.fido_cred_type(cCred))
|
|
531
|
+
|
|
532
|
+
cPubKeyLen := C.fido_cred_pubkey_len(cCred)
|
|
533
|
+
cPubKeyPtr := C.fido_cred_pubkey_ptr(cCred)
|
|
534
|
+
pubKey := C.GoBytes(unsafe.Pointer(cPubKeyPtr), C.int(cPubKeyLen))
|
|
535
|
+
|
|
536
|
+
cCertLen := C.fido_cred_x5c_len(cCred)
|
|
537
|
+
cCertPtr := C.fido_cred_x5c_ptr(cCred)
|
|
538
|
+
cert := C.GoBytes(unsafe.Pointer(cCertPtr), C.int(cCertLen))
|
|
539
|
+
|
|
540
|
+
cSigLen := C.fido_cred_sig_len(cCred)
|
|
541
|
+
cSigPtr := C.fido_cred_sig_ptr(cCred)
|
|
542
|
+
sig := C.GoBytes(unsafe.Pointer(cSigPtr), C.int(cSigLen))
|
|
543
|
+
|
|
544
|
+
at := &Attestation{
|
|
545
|
+
AuthData: authData,
|
|
546
|
+
ClientDataHash: clientDataHashOut,
|
|
547
|
+
CredentialID: id,
|
|
548
|
+
CredentialType: typOut,
|
|
549
|
+
PubKey: pubKey,
|
|
550
|
+
Cert: cert,
|
|
551
|
+
Sig: sig,
|
|
552
|
+
Format: C.GoString(cFormat),
|
|
553
|
+
}
|
|
554
|
+
return at, nil
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
func credential(cCred *C.fido_cred_t) (*Credential, error) {
|
|
558
|
+
cUserIDLen := C.fido_cred_user_id_len(cCred)
|
|
559
|
+
cUserIDPtr := C.fido_cred_user_id_ptr(cCred)
|
|
560
|
+
userID := C.GoBytes(unsafe.Pointer(cUserIDPtr), C.int(cUserIDLen))
|
|
561
|
+
cDisplayName := C.fido_cred_display_name(cCred)
|
|
562
|
+
cName := C.fido_cred_user_name(cCred)
|
|
563
|
+
|
|
564
|
+
// cRPID := C.fido_cred_rp_id(cCred)
|
|
565
|
+
// cRPName := C.fido_cred_rp_name(cCred)
|
|
566
|
+
|
|
567
|
+
cIDLen := C.fido_cred_id_len(cCred)
|
|
568
|
+
cIDPtr := C.fido_cred_id_ptr(cCred)
|
|
569
|
+
id := C.GoBytes(unsafe.Pointer(cIDPtr), C.int(cIDLen))
|
|
570
|
+
|
|
571
|
+
// cFormat := C.fido_cred_fmt(cCred)
|
|
572
|
+
typOut := CredentialType(C.fido_cred_type(cCred))
|
|
573
|
+
|
|
574
|
+
cred := &Credential{
|
|
575
|
+
ID: id,
|
|
576
|
+
Type: typOut,
|
|
577
|
+
User: User{
|
|
578
|
+
ID: userID,
|
|
579
|
+
Name: C.GoString(cName),
|
|
580
|
+
DisplayName: C.GoString(cDisplayName),
|
|
581
|
+
},
|
|
582
|
+
}
|
|
583
|
+
return cred, nil
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// SetPIN ...
|
|
587
|
+
func (d *Device) SetPIN(pin string, old string) error {
|
|
588
|
+
dev, err := d.open()
|
|
589
|
+
if err != nil {
|
|
590
|
+
return err
|
|
591
|
+
}
|
|
592
|
+
defer d.close(dev)
|
|
593
|
+
|
|
594
|
+
if cErr := C.fido_dev_set_pin(dev, C.CString(pin), cStringOrNil(old)); cErr != C.FIDO_OK {
|
|
595
|
+
return errors.Wrap(errFromCode(cErr), "failed to set pin")
|
|
596
|
+
}
|
|
597
|
+
return nil
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Reset represents authenticatorReset.
|
|
601
|
+
// https://fidoalliance.org/specs/fido2/fido-client-to-authenticator-protocol-v2.1-rd-20191217.html#authenticatorReset
|
|
602
|
+
// The actual user-flow to perform a reset is outside the scope of the FIDO2 specification, and may therefore vary
|
|
603
|
+
// depending on the authenticator. Yubico authenticators will return ErrNotAllowed if a reset is issued later than 5
|
|
604
|
+
// seconds after power-up, and ErrActionTimeout if the user fails to confirm the reset by touching the key within 30
|
|
605
|
+
// seconds.
|
|
606
|
+
func (d *Device) Reset() error {
|
|
607
|
+
dev, err := d.open()
|
|
608
|
+
if err != nil {
|
|
609
|
+
return err
|
|
610
|
+
}
|
|
611
|
+
defer d.close(dev)
|
|
612
|
+
|
|
613
|
+
if cErr := C.fido_dev_reset(dev); cErr != C.FIDO_OK {
|
|
614
|
+
return errors.Wrap(errFromCode(cErr), "failed to reset")
|
|
615
|
+
}
|
|
616
|
+
return nil
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// RetryCount ...
|
|
620
|
+
func (d *Device) RetryCount() (int, error) {
|
|
621
|
+
dev, err := d.open()
|
|
622
|
+
if err != nil {
|
|
623
|
+
return 0, err
|
|
624
|
+
}
|
|
625
|
+
defer d.close(dev)
|
|
626
|
+
|
|
627
|
+
var retryCount C.int
|
|
628
|
+
if cErr := C.fido_dev_get_retry_count(dev, &retryCount); cErr != C.FIDO_OK {
|
|
629
|
+
return 0, errors.Wrap(errFromCode(cErr), "failed to get retry count")
|
|
630
|
+
}
|
|
631
|
+
return int(retryCount), nil
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// AssertionOpts ...
|
|
635
|
+
type AssertionOpts struct {
|
|
636
|
+
Extensions []Extension
|
|
637
|
+
UV OptionValue
|
|
638
|
+
UP OptionValue
|
|
639
|
+
HMACSalt []byte
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Assertion ...
|
|
643
|
+
func (d *Device) Assertion(
|
|
644
|
+
rpID string,
|
|
645
|
+
clientDataHash []byte,
|
|
646
|
+
credentialIDs [][]byte,
|
|
647
|
+
pin string,
|
|
648
|
+
opts *AssertionOpts) (*Assertion, error) {
|
|
649
|
+
|
|
650
|
+
if opts == nil {
|
|
651
|
+
opts = &AssertionOpts{}
|
|
652
|
+
}
|
|
653
|
+
if rpID == "" {
|
|
654
|
+
return nil, errors.Errorf("no rpID specified")
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
dev, err := d.open()
|
|
658
|
+
if err != nil {
|
|
659
|
+
return nil, err
|
|
660
|
+
}
|
|
661
|
+
defer d.close(dev)
|
|
662
|
+
|
|
663
|
+
cAssert := C.fido_assert_new()
|
|
664
|
+
defer C.fido_assert_free(&cAssert)
|
|
665
|
+
|
|
666
|
+
if cErr := C.fido_assert_set_rp(cAssert, C.CString(rpID)); cErr != C.FIDO_OK {
|
|
667
|
+
return nil, errors.Wrapf(errFromCode(cErr), "failed to set assertion RP ID")
|
|
668
|
+
}
|
|
669
|
+
if cErr := C.fido_assert_set_clientdata_hash(cAssert, cBytes(clientDataHash), cLen(clientDataHash)); cErr != C.FIDO_OK {
|
|
670
|
+
return nil, errors.Wrapf(errFromCode(cErr), "failed to set client data hash")
|
|
671
|
+
}
|
|
672
|
+
for _, credentialID := range credentialIDs {
|
|
673
|
+
if cErr := C.fido_assert_allow_cred(cAssert, cBytes(credentialID), cLen(credentialID)); cErr != C.FIDO_OK {
|
|
674
|
+
return nil, errors.Wrapf(errFromCode(cErr), "failed to set allowed credentials")
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if exts := extensionsInt(opts.Extensions); exts > 0 {
|
|
678
|
+
if cErr := C.fido_assert_set_extensions(cAssert, C.int(exts)); cErr != C.FIDO_OK {
|
|
679
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set extensions")
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
cUV, err := cOpt(opts.UV)
|
|
683
|
+
if err != nil {
|
|
684
|
+
return nil, err
|
|
685
|
+
}
|
|
686
|
+
if cErr := C.fido_assert_set_uv(cAssert, cUV); cErr != C.FIDO_OK {
|
|
687
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set uv")
|
|
688
|
+
}
|
|
689
|
+
cUP, err := cOpt(opts.UP)
|
|
690
|
+
if err != nil {
|
|
691
|
+
return nil, err
|
|
692
|
+
}
|
|
693
|
+
if cErr := C.fido_assert_set_up(cAssert, cUP); cErr != C.FIDO_OK {
|
|
694
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to set up")
|
|
695
|
+
}
|
|
696
|
+
if opts.HMACSalt != nil {
|
|
697
|
+
if cErr := C.fido_assert_set_hmac_salt(cAssert, cBytes(opts.HMACSalt), cLen(opts.HMACSalt)); cErr != C.FIDO_OK {
|
|
698
|
+
return nil, errors.Wrapf(errFromCode(cErr), "failed to set hmac salt")
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Get assertion
|
|
703
|
+
if cErr := C.fido_dev_get_assert(dev, cAssert, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
704
|
+
return nil, errors.Wrapf(errFromCode(cErr), "failed to get assertion")
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// count := int(C.fido_assert_count(cAssert))
|
|
708
|
+
cIdx := C.size_t(0)
|
|
709
|
+
|
|
710
|
+
// Authdata here is CBOR encoded
|
|
711
|
+
cAuthDataLen := C.fido_assert_authdata_len(cAssert, cIdx)
|
|
712
|
+
cAuthDataPtr := C.fido_assert_authdata_ptr(cAssert, cIdx)
|
|
713
|
+
authDataCBOR := C.GoBytes(unsafe.Pointer(cAuthDataPtr), C.int(cAuthDataLen))
|
|
714
|
+
|
|
715
|
+
cHMACLen := C.fido_assert_hmac_secret_len(cAssert, cIdx)
|
|
716
|
+
cHMACPtr := C.fido_assert_hmac_secret_ptr(cAssert, cIdx)
|
|
717
|
+
hmacSecret := C.GoBytes(unsafe.Pointer(cHMACPtr), C.int(cHMACLen))
|
|
718
|
+
|
|
719
|
+
cSigLen := C.fido_assert_sig_len(cAssert, cIdx)
|
|
720
|
+
cSigPtr := C.fido_assert_sig_ptr(cAssert, cIdx)
|
|
721
|
+
sig := C.GoBytes(unsafe.Pointer(cSigPtr), C.int(cSigLen))
|
|
722
|
+
|
|
723
|
+
cIDLen := C.fido_assert_id_len(cAssert, cIdx)
|
|
724
|
+
cIDPtr := C.fido_assert_id_ptr(cAssert, cIdx)
|
|
725
|
+
cID := C.GoBytes(unsafe.Pointer(cIDPtr), C.int(cIDLen))
|
|
726
|
+
|
|
727
|
+
cUserIDLen := C.fido_assert_user_id_len(cAssert, cIdx)
|
|
728
|
+
cUserIDPtr := C.fido_assert_user_id_ptr(cAssert, cIdx)
|
|
729
|
+
userID := C.GoBytes(unsafe.Pointer(cUserIDPtr), C.int(cUserIDLen))
|
|
730
|
+
|
|
731
|
+
// cUserName := C.fido_assert_user_name(cAssert, cIdx)
|
|
732
|
+
// cUserDisplayName := C.fido_assert_user_display_name(cAssert, cIdx)
|
|
733
|
+
// cUserIcon := C.fido_assert_user_icon(cAssert, cIdx)
|
|
734
|
+
|
|
735
|
+
assertion := &Assertion{
|
|
736
|
+
AuthDataCBOR: authDataCBOR,
|
|
737
|
+
HMACSecret: hmacSecret,
|
|
738
|
+
Sig: sig,
|
|
739
|
+
CredentialID: cID,
|
|
740
|
+
User: User{
|
|
741
|
+
ID: userID,
|
|
742
|
+
// Name: C.GoString(cUserName),
|
|
743
|
+
// DisplayName: C.GoString(cUserDisplayName),
|
|
744
|
+
// Icon: C.GoString(cUserIcon),
|
|
745
|
+
},
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return assertion, nil
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// CredentialsInfo ...
|
|
752
|
+
func (d *Device) CredentialsInfo(pin string) (*CredentialsInfo, error) {
|
|
753
|
+
if pin == "" {
|
|
754
|
+
return nil, errors.Errorf("pin is required")
|
|
755
|
+
}
|
|
756
|
+
dev, err := d.open()
|
|
757
|
+
if err != nil {
|
|
758
|
+
return nil, err
|
|
759
|
+
}
|
|
760
|
+
defer d.close(dev)
|
|
761
|
+
|
|
762
|
+
cCredMeta := C.fido_credman_metadata_new()
|
|
763
|
+
defer C.fido_credman_metadata_free(&cCredMeta)
|
|
764
|
+
|
|
765
|
+
if cErr := C.fido_credman_get_dev_metadata(dev, cCredMeta, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
766
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to get credentials info")
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
rkExisting := int64(C.fido_credman_rk_existing(cCredMeta))
|
|
770
|
+
rkRemaining := int64(C.fido_credman_rk_remaining(cCredMeta))
|
|
771
|
+
|
|
772
|
+
return &CredentialsInfo{
|
|
773
|
+
RKExisting: rkExisting,
|
|
774
|
+
RKRemaining: rkRemaining,
|
|
775
|
+
}, nil
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Credentials lists credentials (if credMgmt is supported).
|
|
779
|
+
func (d *Device) Credentials(rpID string, pin string) ([]*Credential, error) {
|
|
780
|
+
if rpID == "" {
|
|
781
|
+
return nil, errors.Errorf("no rpID specified")
|
|
782
|
+
}
|
|
783
|
+
dev, err := d.open()
|
|
784
|
+
if err != nil {
|
|
785
|
+
return nil, err
|
|
786
|
+
}
|
|
787
|
+
defer d.close(dev)
|
|
788
|
+
|
|
789
|
+
cRK := C.fido_credman_rk_new()
|
|
790
|
+
defer C.fido_credman_rk_free(&cRK)
|
|
791
|
+
|
|
792
|
+
if cErr := C.fido_credman_get_dev_rk(dev, C.CString(rpID), cRK, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
793
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to get resident key info")
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
count := int(C.fido_credman_rk_count(cRK))
|
|
797
|
+
credentials := make([]*Credential, 0, count)
|
|
798
|
+
for i := 0; i < count; i++ {
|
|
799
|
+
cCred := C.fido_credman_rk(cRK, C.size_t(i))
|
|
800
|
+
cred, err := credential(cCred)
|
|
801
|
+
if err != nil {
|
|
802
|
+
return nil, err
|
|
803
|
+
}
|
|
804
|
+
credentials = append(credentials, cred)
|
|
805
|
+
}
|
|
806
|
+
return credentials, nil
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// DeleteCredential deletes a resident credential (if credMgmt is supported).
|
|
810
|
+
func (d *Device) DeleteCredential(credID []byte, pin string) error {
|
|
811
|
+
dev, err := d.open()
|
|
812
|
+
if err != nil {
|
|
813
|
+
return err
|
|
814
|
+
}
|
|
815
|
+
defer d.close(dev)
|
|
816
|
+
|
|
817
|
+
if cErr := C.fido_credman_del_dev_rk(dev, cBytes(credID), cLen(credID), cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
818
|
+
return errors.Wrap(errFromCode(cErr), "failed to delete key")
|
|
819
|
+
}
|
|
820
|
+
return nil
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// RelyingParties ...
|
|
824
|
+
func (d *Device) RelyingParties(pin string) ([]*RelyingParty, error) {
|
|
825
|
+
dev, err := d.open()
|
|
826
|
+
if err != nil {
|
|
827
|
+
return nil, err
|
|
828
|
+
}
|
|
829
|
+
defer d.close(dev)
|
|
830
|
+
|
|
831
|
+
cRP := C.fido_credman_rp_new()
|
|
832
|
+
defer C.fido_credman_rp_free(&cRP)
|
|
833
|
+
|
|
834
|
+
if cErr := C.fido_credman_get_dev_rp(dev, cRP, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
835
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to get relying party info")
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
count := int(C.fido_credman_rp_count(cRP))
|
|
839
|
+
rps := make([]*RelyingParty, 0, count)
|
|
840
|
+
for i := 0; i < count; i++ {
|
|
841
|
+
cRPID := C.fido_credman_rp_id(cRP, C.size_t(i))
|
|
842
|
+
cRPName := C.fido_credman_rp_name(cRP, C.size_t(i))
|
|
843
|
+
// TODO: fido_credman_rp_id_hash_ptr?
|
|
844
|
+
rps = append(rps, &RelyingParty{
|
|
845
|
+
ID: C.GoString(cRPID),
|
|
846
|
+
Name: C.GoString(cRPName),
|
|
847
|
+
})
|
|
848
|
+
}
|
|
849
|
+
return rps, nil
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
func plural(n uint8) string {
|
|
853
|
+
plural := ""
|
|
854
|
+
if n > 1 {
|
|
855
|
+
plural = "s"
|
|
856
|
+
}
|
|
857
|
+
return plural
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// BioEnrollment starts a bio-enabled device enrollment
|
|
861
|
+
func (d *Device) BioEnroll(pin string) error {
|
|
862
|
+
dev, err := d.open()
|
|
863
|
+
if err != nil {
|
|
864
|
+
return err
|
|
865
|
+
}
|
|
866
|
+
defer d.close(dev)
|
|
867
|
+
|
|
868
|
+
template := C.fido_bio_template_new()
|
|
869
|
+
if template == nil {
|
|
870
|
+
return errors.New("bio template is empty")
|
|
871
|
+
}
|
|
872
|
+
defer C.fido_bio_template_free(&template)
|
|
873
|
+
|
|
874
|
+
enrollment := C.fido_bio_enroll_new()
|
|
875
|
+
if enrollment == nil {
|
|
876
|
+
return errors.New("enroll object is empty")
|
|
877
|
+
}
|
|
878
|
+
defer C.fido_bio_enroll_free(&enrollment)
|
|
879
|
+
|
|
880
|
+
if cErr := C.fido_bio_dev_enroll_begin(dev, template, enrollment, 10000, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
881
|
+
return errors.Wrap(errFromCode(cErr), "failed to begin bio enrollment")
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
for C.fido_bio_enroll_remaining_samples(enrollment) > 0 {
|
|
885
|
+
remainingSamples := uint8(C.fido_bio_enroll_remaining_samples(enrollment))
|
|
886
|
+
|
|
887
|
+
fmt.Printf("Touch you security key (%d sample%s left)\n",
|
|
888
|
+
remainingSamples, plural(remainingSamples))
|
|
889
|
+
|
|
890
|
+
if cErr := C.fido_bio_dev_enroll_continue(dev, template, enrollment, 10000); cErr != C.FIDO_OK {
|
|
891
|
+
if err := d.Cancel(); err != nil {
|
|
892
|
+
return err
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
return nil
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
type BioTemplate struct {
|
|
901
|
+
ID string
|
|
902
|
+
Name string
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
func goBioTemplate(tempalateArray *C.fido_bio_template_array_t, idx C.size_t) (*BioTemplate, error) {
|
|
906
|
+
template := C.fido_bio_template(tempalateArray, idx)
|
|
907
|
+
if template == nil {
|
|
908
|
+
return nil, errors.New("template is empty")
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
templateIdPtr := C.fido_bio_template_id_ptr(template)
|
|
912
|
+
templateIdLen := C.fido_bio_template_id_len(template)
|
|
913
|
+
templateName := C.GoString(C.fido_bio_template_name(template))
|
|
914
|
+
|
|
915
|
+
if templateIdPtr == nil {
|
|
916
|
+
return nil, errors.New("empty template id")
|
|
917
|
+
}
|
|
918
|
+
templateIdBuf := C.GoBytes(unsafe.Pointer(templateIdPtr), C.int(templateIdLen))
|
|
919
|
+
return &BioTemplate{
|
|
920
|
+
ID: hex.EncodeToString(templateIdBuf),
|
|
921
|
+
Name: string(templateName),
|
|
922
|
+
}, nil
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// BioList lists all bio templates.
|
|
926
|
+
func (d *Device) BioList(pin string) ([]BioTemplate, error) {
|
|
927
|
+
dev, err := d.open()
|
|
928
|
+
if err != nil {
|
|
929
|
+
return nil, err
|
|
930
|
+
}
|
|
931
|
+
defer d.close(dev)
|
|
932
|
+
|
|
933
|
+
templateArray := C.fido_bio_template_array_new()
|
|
934
|
+
if templateArray == nil {
|
|
935
|
+
return nil, errors.New("empty template array")
|
|
936
|
+
}
|
|
937
|
+
defer C.fido_bio_template_array_free(&templateArray)
|
|
938
|
+
|
|
939
|
+
if cErr := C.fido_bio_dev_get_template_array(dev, templateArray, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
940
|
+
return nil, errors.Wrap(errFromCode(cErr), "failed to retrieve template array")
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
var i C.size_t
|
|
944
|
+
var bioTemplates []BioTemplate
|
|
945
|
+
count := C.size_t(C.fido_bio_template_array_count(templateArray))
|
|
946
|
+
|
|
947
|
+
for i = 0; i < count; i++ {
|
|
948
|
+
bioTemplate, err := goBioTemplate(templateArray, i)
|
|
949
|
+
if err != nil {
|
|
950
|
+
return nil, errors.Wrapf(err, "unable to read bio template at index %d", i)
|
|
951
|
+
}
|
|
952
|
+
if bioTemplate == nil {
|
|
953
|
+
return nil, errors.New("empty bio template")
|
|
954
|
+
}
|
|
955
|
+
bioTemplates = append(bioTemplates, *bioTemplate)
|
|
956
|
+
}
|
|
957
|
+
return bioTemplates, nil
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// BioDelete deletes a bio template.
|
|
961
|
+
func (d *Device) BioDelete(pin, templateId string) error {
|
|
962
|
+
dev, err := d.open()
|
|
963
|
+
if err != nil {
|
|
964
|
+
return err
|
|
965
|
+
}
|
|
966
|
+
defer d.close(dev)
|
|
967
|
+
|
|
968
|
+
template := C.fido_bio_template_new()
|
|
969
|
+
if template == nil {
|
|
970
|
+
return errors.New("bio template is empty")
|
|
971
|
+
}
|
|
972
|
+
defer C.fido_bio_template_free(&template)
|
|
973
|
+
|
|
974
|
+
templateIdBuf, err := hex.DecodeString(templateId)
|
|
975
|
+
if err != nil {
|
|
976
|
+
return errors.Wrap(err, "failed to decode string from base64")
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if cErr := C.fido_bio_template_set_id(template, cBytes(templateIdBuf), cLen(templateIdBuf)); cErr != C.FIDO_OK {
|
|
980
|
+
return errors.Wrap(errFromCode(cErr), "failed to set template id")
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if cErr := C.fido_bio_dev_enroll_remove(dev, template, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
984
|
+
return errors.Wrap(errFromCode(cErr), "failed to remove template")
|
|
985
|
+
}
|
|
986
|
+
return nil
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// BioSetTemplateName sets the name of template with templateId.
|
|
990
|
+
func (d *Device) BioSetTemplateName(pin, templateId, name string) error {
|
|
991
|
+
dev, err := d.open()
|
|
992
|
+
if err != nil {
|
|
993
|
+
return err
|
|
994
|
+
}
|
|
995
|
+
defer d.close(dev)
|
|
996
|
+
|
|
997
|
+
template := C.fido_bio_template_new()
|
|
998
|
+
if template == nil {
|
|
999
|
+
return errors.New("bio template is empty")
|
|
1000
|
+
}
|
|
1001
|
+
defer C.fido_bio_template_free(&template)
|
|
1002
|
+
|
|
1003
|
+
templateIdBuf, err := hex.DecodeString(templateId)
|
|
1004
|
+
if err != nil {
|
|
1005
|
+
return errors.Wrap(err, "failed to decode string from base64")
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if cErr := C.fido_bio_template_set_id(template, cBytes(templateIdBuf), cLen(templateIdBuf)); cErr != C.FIDO_OK {
|
|
1009
|
+
return errors.Wrap(errFromCode(cErr), "failed to set template id")
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if cErr := C.fido_bio_template_set_name(template, cStringOrNil(name)); cErr != C.FIDO_OK {
|
|
1013
|
+
return errors.Wrap(errFromCode(cErr), "failed to set template name")
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
if cErr := C.fido_bio_dev_set_template_name(dev, template, cStringOrNil(pin)); cErr != C.FIDO_OK {
|
|
1017
|
+
return errors.Wrap(errFromCode(cErr), "failed to update template")
|
|
1018
|
+
}
|
|
1019
|
+
return nil
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
func goStrings(argc C.int, argv **C.char) []string {
|
|
1023
|
+
length := int(argc)
|
|
1024
|
+
tmpslice := (*[1 << 30]*C.char)(unsafe.Pointer(argv))[:length:length]
|
|
1025
|
+
gostrings := make([]string, length)
|
|
1026
|
+
for i, s := range tmpslice {
|
|
1027
|
+
gostrings[i] = C.GoString(s)
|
|
1028
|
+
}
|
|
1029
|
+
return gostrings
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
func goBools(argc C.int, argv *C.bool) []bool {
|
|
1033
|
+
length := int(argc)
|
|
1034
|
+
tmpslice := (*[1 << 30]C.bool)(unsafe.Pointer(argv))[:length:length]
|
|
1035
|
+
gobools := make([]bool, length)
|
|
1036
|
+
for i, s := range tmpslice {
|
|
1037
|
+
gobools[i] = bool(s)
|
|
1038
|
+
}
|
|
1039
|
+
return gobools
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
func cStringOrNil(s string) *C.char {
|
|
1043
|
+
if s == "" {
|
|
1044
|
+
return nil
|
|
1045
|
+
}
|
|
1046
|
+
return C.CString(s)
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
func cBytes(b []byte) *C.uchar {
|
|
1050
|
+
return (*C.uchar)(&[]byte(b)[0])
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
func cLen(b []byte) C.size_t {
|
|
1054
|
+
return C.size_t(len(b))
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
func cOpt(o OptionValue) (C.fido_opt_t, error) {
|
|
1058
|
+
switch o {
|
|
1059
|
+
case Default:
|
|
1060
|
+
return C.FIDO_OPT_OMIT, nil
|
|
1061
|
+
case True:
|
|
1062
|
+
return C.FIDO_OPT_TRUE, nil
|
|
1063
|
+
case False:
|
|
1064
|
+
return C.FIDO_OPT_FALSE, nil
|
|
1065
|
+
default:
|
|
1066
|
+
return C.FIDO_OPT_OMIT, errors.Errorf("invalid cred protect")
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
func cCredProtect(c CredProtect) (C.int, error) {
|
|
1071
|
+
switch c {
|
|
1072
|
+
case CredProtectUVOptional:
|
|
1073
|
+
return C.FIDO_CRED_PROT_UV_OPTIONAL, nil
|
|
1074
|
+
case CredProtectUVOptionalWithID:
|
|
1075
|
+
return C.FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID, nil
|
|
1076
|
+
case CredProtectUVRequired:
|
|
1077
|
+
return C.FIDO_CRED_PROT_UV_REQUIRED, nil
|
|
1078
|
+
default:
|
|
1079
|
+
return C.FIDO_CRED_PROT_UV_OPTIONAL, errors.Errorf("invalid cred protect")
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// Error is a generic error with code.
|
|
1084
|
+
type Error struct {
|
|
1085
|
+
Code int
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
func (e Error) Error() string {
|
|
1089
|
+
return fmt.Sprintf("libfido2 error %d", e.Code)
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// ErrInvalidArgument if arguments are invalid.
|
|
1093
|
+
var ErrInvalidArgument = errors.New("invalid argument")
|
|
1094
|
+
|
|
1095
|
+
// ErrUserPresenceRequired is user presence required.
|
|
1096
|
+
var ErrUserPresenceRequired = errors.New("user presence required")
|
|
1097
|
+
|
|
1098
|
+
// ErrTX if there was an error transmitting.
|
|
1099
|
+
var ErrTX = errors.New("tx error")
|
|
1100
|
+
|
|
1101
|
+
// ErrRX if there was an error receiving.
|
|
1102
|
+
var ErrRX = errors.New("rx error")
|
|
1103
|
+
|
|
1104
|
+
// ErrNotAllowed if not allowed.
|
|
1105
|
+
var ErrNotAllowed = errors.New("not allowed")
|
|
1106
|
+
|
|
1107
|
+
// ErrActionTimeout if action timed out.
|
|
1108
|
+
var ErrActionTimeout = errors.New("action timed out")
|
|
1109
|
+
|
|
1110
|
+
// ErrPinNotSet if PIN is not set and is required for command.
|
|
1111
|
+
var ErrPinNotSet = errors.New("pin not set")
|
|
1112
|
+
|
|
1113
|
+
// ErrInvalidCommand if command is not supported.
|
|
1114
|
+
var ErrInvalidCommand = errors.New("invalid command")
|
|
1115
|
+
|
|
1116
|
+
// ErrInvalidLength if invalid length.
|
|
1117
|
+
var ErrInvalidLength = errors.New("invalid length")
|
|
1118
|
+
|
|
1119
|
+
// ErrInvalidCredential if credential is invalid.
|
|
1120
|
+
var ErrInvalidCredential = errors.New("invalid credential")
|
|
1121
|
+
|
|
1122
|
+
// ErrUnsupportedOption if option is unsupported.
|
|
1123
|
+
var ErrUnsupportedOption = errors.New("unsupported option")
|
|
1124
|
+
|
|
1125
|
+
// ErrPinInvalid if pin is wrong.
|
|
1126
|
+
var ErrPinInvalid = errors.New("pin invalid")
|
|
1127
|
+
|
|
1128
|
+
// ErrRXNotCBOR rx not CBOR.
|
|
1129
|
+
var ErrRXNotCBOR = errors.New("rx not CBOR")
|
|
1130
|
+
|
|
1131
|
+
// ErrPinPolicyViolation if PIN policy violation.
|
|
1132
|
+
var ErrPinPolicyViolation = errors.New("pin policy violation")
|
|
1133
|
+
|
|
1134
|
+
// ErrInternal internal error.
|
|
1135
|
+
var ErrInternal = errors.New("internal error")
|
|
1136
|
+
|
|
1137
|
+
// ErrNoCredentials if no credentials.
|
|
1138
|
+
var ErrNoCredentials = errors.New("no credentials")
|
|
1139
|
+
|
|
1140
|
+
// ErrPinAuthBlocked if too many PIN failures.
|
|
1141
|
+
var ErrPinAuthBlocked = errors.New("pin auth blocked")
|
|
1142
|
+
|
|
1143
|
+
// ErrPinRequired if PIN is required.
|
|
1144
|
+
var ErrPinRequired = errors.New("pin required")
|
|
1145
|
+
|
|
1146
|
+
// ErrMissingParameter if missing parameter.
|
|
1147
|
+
var ErrMissingParameter = errors.New("missing parameter")
|
|
1148
|
+
|
|
1149
|
+
// ErrUPRequired if user presence is required.
|
|
1150
|
+
var ErrUPRequired = errors.New("up required")
|
|
1151
|
+
|
|
1152
|
+
// ErrRXInvalidCBOR if receiving invalid CBOR.
|
|
1153
|
+
var ErrRXInvalidCBOR = errors.New("rx invalid cbor")
|
|
1154
|
+
|
|
1155
|
+
// ErrOperationDenied if operation denied.
|
|
1156
|
+
var ErrOperationDenied = errors.New("operation denied")
|
|
1157
|
+
|
|
1158
|
+
// ErrNotFIDO2 if device is not a FIDO2 device.
|
|
1159
|
+
var ErrNotFIDO2 = errors.Errorf("not a FIDO2 device")
|
|
1160
|
+
|
|
1161
|
+
// ErrKeepaliveCancel if action was cancelled.
|
|
1162
|
+
var ErrKeepaliveCancel = errors.Errorf("keep alive cancel")
|
|
1163
|
+
|
|
1164
|
+
// ErrInvalidOption if option is invalid.
|
|
1165
|
+
var ErrInvalidOption = errors.Errorf("invalid option")
|
|
1166
|
+
|
|
1167
|
+
// ErrOther if other error?
|
|
1168
|
+
var ErrOther = errors.Errorf("other error")
|
|
1169
|
+
|
|
1170
|
+
func errFromCode(code C.int) error {
|
|
1171
|
+
switch code {
|
|
1172
|
+
case C.FIDO_ERR_TX: // -1
|
|
1173
|
+
return ErrTX
|
|
1174
|
+
case C.FIDO_ERR_RX: // -2
|
|
1175
|
+
return ErrRX
|
|
1176
|
+
case C.FIDO_ERR_INVALID_ARGUMENT: // -7
|
|
1177
|
+
return ErrInvalidArgument
|
|
1178
|
+
case C.FIDO_ERR_USER_PRESENCE_REQUIRED: // -8
|
|
1179
|
+
return ErrUserPresenceRequired
|
|
1180
|
+
case C.FIDO_ERR_INVALID_COMMAND: // 0x01
|
|
1181
|
+
return ErrInvalidCommand
|
|
1182
|
+
case C.FIDO_ERR_INVALID_LENGTH: // 0x03
|
|
1183
|
+
return ErrInvalidLength
|
|
1184
|
+
case C.FIDO_ERR_MISSING_PARAMETER:
|
|
1185
|
+
return ErrMissingParameter // 0x14
|
|
1186
|
+
case C.FIDO_ERR_NOT_ALLOWED:
|
|
1187
|
+
return ErrNotAllowed
|
|
1188
|
+
case C.FIDO_ERR_ACTION_TIMEOUT:
|
|
1189
|
+
return ErrActionTimeout
|
|
1190
|
+
case C.FIDO_ERR_PIN_NOT_SET:
|
|
1191
|
+
return ErrPinNotSet
|
|
1192
|
+
case C.FIDO_ERR_INVALID_CREDENTIAL:
|
|
1193
|
+
return ErrInvalidCredential
|
|
1194
|
+
case C.FIDO_ERR_UNSUPPORTED_OPTION:
|
|
1195
|
+
return ErrUnsupportedOption
|
|
1196
|
+
case C.FIDO_ERR_PIN_INVALID:
|
|
1197
|
+
return ErrPinInvalid
|
|
1198
|
+
case C.FIDO_ERR_RX_NOT_CBOR:
|
|
1199
|
+
return ErrRXNotCBOR
|
|
1200
|
+
case C.FIDO_ERR_INTERNAL:
|
|
1201
|
+
return ErrInternal
|
|
1202
|
+
case C.FIDO_ERR_PIN_POLICY_VIOLATION:
|
|
1203
|
+
return ErrPinPolicyViolation
|
|
1204
|
+
case C.FIDO_ERR_NO_CREDENTIALS:
|
|
1205
|
+
return ErrNoCredentials
|
|
1206
|
+
case C.FIDO_ERR_PIN_AUTH_BLOCKED:
|
|
1207
|
+
return ErrPinAuthBlocked
|
|
1208
|
+
case C.FIDO_ERR_PIN_REQUIRED:
|
|
1209
|
+
return ErrPinRequired
|
|
1210
|
+
case C.FIDO_ERR_UP_REQUIRED:
|
|
1211
|
+
return ErrUPRequired
|
|
1212
|
+
case C.FIDO_ERR_RX_INVALID_CBOR:
|
|
1213
|
+
return ErrRXInvalidCBOR
|
|
1214
|
+
case C.FIDO_ERR_OPERATION_DENIED:
|
|
1215
|
+
return ErrOperationDenied
|
|
1216
|
+
case C.FIDO_ERR_KEEPALIVE_CANCEL:
|
|
1217
|
+
return ErrKeepaliveCancel
|
|
1218
|
+
case C.FIDO_ERR_INVALID_OPTION:
|
|
1219
|
+
return ErrInvalidOption
|
|
1220
|
+
case C.FIDO_ERR_ERR_OTHER:
|
|
1221
|
+
return ErrOther
|
|
1222
|
+
default:
|
|
1223
|
+
return Error{Code: int(code)}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// RandBytes returns random bytes of length.
|
|
1228
|
+
func RandBytes(length int) []byte {
|
|
1229
|
+
buf := make([]byte, length)
|
|
1230
|
+
if _, err := rand.Read(buf); err != nil {
|
|
1231
|
+
panic(err)
|
|
1232
|
+
}
|
|
1233
|
+
return buf
|
|
1234
|
+
}
|