@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.
Files changed (91) hide show
  1. package/README.md +285 -0
  2. package/bin/eldlock +11 -0
  3. package/docs/architecture.md +164 -0
  4. package/docs/threat-model.md +47 -0
  5. package/eldlock-cli/README.md +56 -0
  6. package/eldlock-cli/bin/eldlock +3 -0
  7. package/eldlock-cli/package-lock.json +805 -0
  8. package/eldlock-cli/package.json +71 -0
  9. package/eldlock-cli/src/api.ts +250 -0
  10. package/eldlock-cli/src/cli.ts +490 -0
  11. package/eldlock-cli/src/main.ts +10 -0
  12. package/eldlock-cli/src/tui.ts +676 -0
  13. package/eldlock-cli/tsconfig.json +13 -0
  14. package/eldlock-cli/vendor/npm/ansi-regex-6.2.2.tgz +0 -0
  15. package/eldlock-cli/vendor/npm/bun-ffi-structs-0.2.2.tgz +0 -0
  16. package/eldlock-cli/vendor/npm/diff-9.0.0.tgz +0 -0
  17. package/eldlock-cli/vendor/npm/emoji-regex-10.6.0.tgz +0 -0
  18. package/eldlock-cli/vendor/npm/esbuild-0.28.0.tgz +0 -0
  19. package/eldlock-cli/vendor/npm/esbuild-darwin-arm64-0.28.0.tgz +0 -0
  20. package/eldlock-cli/vendor/npm/esbuild-darwin-x64-0.28.0.tgz +0 -0
  21. package/eldlock-cli/vendor/npm/esbuild-linux-arm64-0.28.0.tgz +0 -0
  22. package/eldlock-cli/vendor/npm/esbuild-linux-x64-0.28.0.tgz +0 -0
  23. package/eldlock-cli/vendor/npm/fsevents-2.3.3.tgz +0 -0
  24. package/eldlock-cli/vendor/npm/get-east-asian-width-1.6.0.tgz +0 -0
  25. package/eldlock-cli/vendor/npm/marked-17.0.1.tgz +0 -0
  26. package/eldlock-cli/vendor/npm/opentui-core-0.3.1.tgz +0 -0
  27. package/eldlock-cli/vendor/npm/opentui-core-darwin-arm64-0.3.1.tgz +0 -0
  28. package/eldlock-cli/vendor/npm/opentui-core-darwin-x64-0.3.1.tgz +0 -0
  29. package/eldlock-cli/vendor/npm/opentui-core-linux-arm64-0.3.1.tgz +0 -0
  30. package/eldlock-cli/vendor/npm/opentui-core-linux-x64-0.3.1.tgz +0 -0
  31. package/eldlock-cli/vendor/npm/string-width-7.2.0.tgz +0 -0
  32. package/eldlock-cli/vendor/npm/strip-ansi-7.1.2.tgz +0 -0
  33. package/eldlock-cli/vendor/npm/tsx-4.22.4.tgz +0 -0
  34. package/eldlock-cli/vendor/npm/types-node-22.19.19.tgz +0 -0
  35. package/eldlock-cli/vendor/npm/typescript-5.9.3.tgz +0 -0
  36. package/eldlock-cli/vendor/npm/undici-types-6.21.0.tgz +0 -0
  37. package/eldlock-cli/vendor/npm/web-tree-sitter-0.25.10.tgz +0 -0
  38. package/eldlock-cli/vendor/npm/yoga-layout-3.2.1.tgz +0 -0
  39. package/eldlock-server/cmd/eldlock-server/main.go +132 -0
  40. package/eldlock-server/go.mod +10 -0
  41. package/eldlock-server/go.sum +11 -0
  42. package/eldlock-server/internal/api/README.md +14 -0
  43. package/eldlock-server/internal/api/core.go +126 -0
  44. package/eldlock-server/internal/api/exec.go +97 -0
  45. package/eldlock-server/internal/api/secrets.go +358 -0
  46. package/eldlock-server/internal/api/server.go +72 -0
  47. package/eldlock-server/internal/api/service_test.go +416 -0
  48. package/eldlock-server/internal/api/types.go +48 -0
  49. package/eldlock-server/internal/api/vault.go +69 -0
  50. package/eldlock-server/internal/api/vendor.go +44 -0
  51. package/eldlock-server/internal/libfido2/LICENSE +21 -0
  52. package/eldlock-server/internal/libfido2/README.md +127 -0
  53. package/eldlock-server/internal/libfido2/examples_test.go +614 -0
  54. package/eldlock-server/internal/libfido2/fido2.go +1234 -0
  55. package/eldlock-server/internal/libfido2/fido2_darwin.go +7 -0
  56. package/eldlock-server/internal/libfido2/fido2_other.go +9 -0
  57. package/eldlock-server/internal/libfido2/fido2_test.go +101 -0
  58. package/eldlock-server/internal/libfido2/go.mod +10 -0
  59. package/eldlock-server/internal/libfido2/go.sum +16 -0
  60. package/eldlock-server/internal/libfido2/log.go +87 -0
  61. package/eldlock-server/internal/store/README.md +7 -0
  62. package/eldlock-server/internal/store/store.go +434 -0
  63. package/eldlock-server/internal/store/store_test.go +125 -0
  64. package/eldlock-server/internal/yubikey/README.md +25 -0
  65. package/eldlock-server/internal/yubikey/default_fido2.go +7 -0
  66. package/eldlock-server/internal/yubikey/default_stub.go +7 -0
  67. package/eldlock-server/internal/yubikey/fido2_disabled.go +9 -0
  68. package/eldlock-server/internal/yubikey/fido2_libfido2.go +225 -0
  69. package/eldlock-server/internal/yubikey/fido2_libfido2_test.go +66 -0
  70. package/eldlock-server/internal/yubikey/passkey.go +139 -0
  71. package/eldlock-server/internal/yubikey/passkey_test.go +36 -0
  72. package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/LICENSE +21 -0
  73. package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/README.md +127 -0
  74. package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2.go +1234 -0
  75. package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2_darwin.go +7 -0
  76. package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2_other.go +9 -0
  77. package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/log.go +87 -0
  78. package/eldlock-server/vendor/github.com/pkg/errors/.travis.yml +10 -0
  79. package/eldlock-server/vendor/github.com/pkg/errors/LICENSE +23 -0
  80. package/eldlock-server/vendor/github.com/pkg/errors/Makefile +44 -0
  81. package/eldlock-server/vendor/github.com/pkg/errors/README.md +59 -0
  82. package/eldlock-server/vendor/github.com/pkg/errors/appveyor.yml +32 -0
  83. package/eldlock-server/vendor/github.com/pkg/errors/errors.go +288 -0
  84. package/eldlock-server/vendor/github.com/pkg/errors/go113.go +38 -0
  85. package/eldlock-server/vendor/github.com/pkg/errors/stack.go +177 -0
  86. package/eldlock-server/vendor/modules.txt +7 -0
  87. package/examples/eldlock.toml +17 -0
  88. package/install.sh +66 -0
  89. package/package.json +66 -0
  90. package/scripts/build-production.mjs +177 -0
  91. 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
+ }