diff options
author | djm@openbsd.org <djm@openbsd.org> | 2020-01-06 02:00:46 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2020-01-06 13:12:46 +1100 |
commit | c312ca077cd2a6c15545cd6b4d34ee2f69289174 (patch) | |
tree | b8dd974c55dd0de351dfcbfc4f33fddb935a1c12 | |
parent | 2ab335712d084d9ccaf3f53afc3fa9535329da87 (diff) |
upstream: Extends the SK API to accept a set of key/value options
for all operations. These are intended to future-proof the API a little by
making it easier to specify additional fields for without having to change
the API version for each.
At present, only two options are defined: one to explicitly specify
the device for an operation (rather than accepting the middleware's
autoselection) and another to specify the FIDO2 username that may
be used when generating a resident key. These new options may be
invoked at key generation time via ssh-keygen -O
This also implements a suggestion from Markus to avoid "int" in favour
of uint32_t for the algorithm argument in the API, to make implementation
of ssh-sk-client/helper a little easier.
feedback, fixes and ok markus@
OpenBSD-Commit-ID: 973ce11704609022ab36abbdeb6bc23c8001eabc
-rw-r--r-- | PROTOCOL.u2f | 47 | ||||
-rw-r--r-- | sk-api.h | 23 | ||||
-rw-r--r-- | sk-usbhid.c | 194 | ||||
-rw-r--r-- | ssh-add.c | 5 | ||||
-rw-r--r-- | ssh-keygen.1 | 23 | ||||
-rw-r--r-- | ssh-keygen.c | 39 | ||||
-rw-r--r-- | ssh-sk-client.c | 14 | ||||
-rw-r--r-- | ssh-sk-helper.c | 45 | ||||
-rw-r--r-- | ssh-sk.c | 121 | ||||
-rw-r--r-- | ssh-sk.h | 14 |
10 files changed, 404 insertions, 121 deletions
diff --git a/PROTOCOL.u2f b/PROTOCOL.u2f index 5f44c3acc..fd0cd0de0 100644 --- a/PROTOCOL.u2f +++ b/PROTOCOL.u2f | |||
@@ -233,7 +233,7 @@ support for the common case of USB HID security keys internally. | |||
233 | 233 | ||
234 | The middleware library need only expose a handful of functions: | 234 | The middleware library need only expose a handful of functions: |
235 | 235 | ||
236 | #define SSH_SK_VERSION_MAJOR 0x00030000 /* API version */ | 236 | #define SSH_SK_VERSION_MAJOR 0x00040000 /* API version */ |
237 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 | 237 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 |
238 | 238 | ||
239 | /* Flags */ | 239 | /* Flags */ |
@@ -245,6 +245,11 @@ The middleware library need only expose a handful of functions: | |||
245 | #define SSH_SK_ECDSA 0x00 | 245 | #define SSH_SK_ECDSA 0x00 |
246 | #define SSH_SK_ED25519 0x01 | 246 | #define SSH_SK_ED25519 0x01 |
247 | 247 | ||
248 | /* Error codes */ | ||
249 | #define SSH_SK_ERR_GENERAL -1 | ||
250 | #define SSH_SK_ERR_UNSUPPORTED -2 | ||
251 | #define SSH_SK_ERR_PIN_REQUIRED -3 | ||
252 | |||
248 | struct sk_enroll_response { | 253 | struct sk_enroll_response { |
249 | uint8_t *public_key; | 254 | uint8_t *public_key; |
250 | size_t public_key_len; | 255 | size_t public_key_len; |
@@ -266,35 +271,63 @@ The middleware library need only expose a handful of functions: | |||
266 | }; | 271 | }; |
267 | 272 | ||
268 | struct sk_resident_key { | 273 | struct sk_resident_key { |
269 | uint8_t alg; | 274 | uint32_t alg; |
270 | size_t slot; | 275 | size_t slot; |
271 | char *application; | 276 | char *application; |
272 | struct sk_enroll_response key; | 277 | struct sk_enroll_response key; |
273 | }; | 278 | }; |
274 | 279 | ||
280 | struct sk_option { | ||
281 | char *name; | ||
282 | char *value; | ||
283 | uint8_t important; | ||
284 | }; | ||
285 | |||
275 | /* Return the version of the middleware API */ | 286 | /* Return the version of the middleware API */ |
276 | uint32_t sk_api_version(void); | 287 | uint32_t sk_api_version(void); |
277 | 288 | ||
278 | /* Enroll a U2F key (private key generation) */ | 289 | /* Enroll a U2F key (private key generation) */ |
279 | int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | 290 | int sk_enroll(uint32_t alg, |
291 | const uint8_t *challenge, size_t challenge_len, | ||
280 | const char *application, uint8_t flags, const char *pin, | 292 | const char *application, uint8_t flags, const char *pin, |
293 | struct sk_option **options, | ||
281 | struct sk_enroll_response **enroll_response); | 294 | struct sk_enroll_response **enroll_response); |
282 | 295 | ||
283 | /* Sign a challenge */ | 296 | /* Sign a challenge */ |
284 | int sk_sign(int alg, const uint8_t *message, size_t message_len, | 297 | int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, |
285 | const char *application, | 298 | const char *application, |
286 | const uint8_t *key_handle, size_t key_handle_len, | 299 | const uint8_t *key_handle, size_t key_handle_len, |
287 | uint8_t flags, const char *pin, | 300 | uint8_t flags, const char *pin, struct sk_option **options, |
288 | struct sk_sign_response **sign_response); | 301 | struct sk_sign_response **sign_response); |
289 | 302 | ||
290 | /* Enumerate all resident keys */ | 303 | /* Enumerate all resident keys */ |
291 | int sk_load_resident_keys(const char *pin, | 304 | int sk_load_resident_keys(const char *pin, struct sk_option **options, |
292 | struct sk_resident_key ***rks, size_t *nrks); | 305 | struct sk_resident_key ***rks, size_t *nrks); |
293 | 306 | ||
294 | The SSH_SK_VERSION_MAJOR should be incremented for each incompatible | 307 | The SSH_SK_VERSION_MAJOR should be incremented for each incompatible |
295 | API change. | 308 | API change. |
296 | 309 | ||
297 | In OpenSSH, these will be invoked by using a similar mechanism to | 310 | The options may be used to pass miscellaneous options to the middleware |
311 | as a NULL-terminated array of pointers to struct sk_option. The middleware | ||
312 | may ignore unsupported or unknown options unless the "important" flag is | ||
313 | set, in which case it should return failure if an unsupported option is | ||
314 | requested. | ||
315 | |||
316 | At present the following options names are supported: | ||
317 | |||
318 | "device" | ||
319 | |||
320 | Specifies a specific FIDO device on which to perform the | ||
321 | operation. The value in this field is interpreted by the | ||
322 | middleware but it would be typical to specify a path to | ||
323 | a /dev node for the device in question. | ||
324 | |||
325 | "user" | ||
326 | |||
327 | Specifies the FIDO2 username used when enrolling a key, | ||
328 | overriding OpenSSH's default of using an all-zero username. | ||
329 | |||
330 | In OpenSSH, the middleware will be invoked by using a similar mechanism to | ||
298 | ssh-pkcs11-helper to provide address-space containment of the | 331 | ssh-pkcs11-helper to provide address-space containment of the |
299 | middleware from ssh-agent. | 332 | middleware from ssh-agent. |
300 | 333 | ||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sk-api.h,v 1.6 2019/12/30 09:24:45 djm Exp $ */ | 1 | /* $OpenBSD: sk-api.h,v 1.7 2020/01/06 02:00:46 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -58,30 +58,37 @@ struct sk_sign_response { | |||
58 | }; | 58 | }; |
59 | 59 | ||
60 | struct sk_resident_key { | 60 | struct sk_resident_key { |
61 | uint8_t alg; | 61 | uint32_t alg; |
62 | size_t slot; | 62 | size_t slot; |
63 | char *application; | 63 | char *application; |
64 | struct sk_enroll_response key; | 64 | struct sk_enroll_response key; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | #define SSH_SK_VERSION_MAJOR 0x00030000 /* current API version */ | 67 | struct sk_option { |
68 | char *name; | ||
69 | char *value; | ||
70 | uint8_t required; | ||
71 | }; | ||
72 | |||
73 | #define SSH_SK_VERSION_MAJOR 0x00040000 /* current API version */ | ||
68 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 | 74 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 |
69 | 75 | ||
70 | /* Return the version of the middleware API */ | 76 | /* Return the version of the middleware API */ |
71 | uint32_t sk_api_version(void); | 77 | uint32_t sk_api_version(void); |
72 | 78 | ||
73 | /* Enroll a U2F key (private key generation) */ | 79 | /* Enroll a U2F key (private key generation) */ |
74 | int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | 80 | int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, |
75 | const char *application, uint8_t flags, const char *pin, | 81 | const char *application, uint8_t flags, const char *pin, |
76 | struct sk_enroll_response **enroll_response); | 82 | struct sk_option **options, struct sk_enroll_response **enroll_response); |
77 | 83 | ||
78 | /* Sign a challenge */ | 84 | /* Sign a challenge */ |
79 | int sk_sign(int alg, const uint8_t *message, size_t message_len, | 85 | int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, |
80 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | 86 | const char *application, const uint8_t *key_handle, size_t key_handle_len, |
81 | uint8_t flags, const char *pin, struct sk_sign_response **sign_response); | 87 | uint8_t flags, const char *pin, struct sk_option **options, |
88 | struct sk_sign_response **sign_response); | ||
82 | 89 | ||
83 | /* Enumerate all resident keys */ | 90 | /* Enumerate all resident keys */ |
84 | int sk_load_resident_keys(const char *pin, | 91 | int sk_load_resident_keys(const char *pin, struct sk_option **options, |
85 | struct sk_resident_key ***rks, size_t *nrks); | 92 | struct sk_resident_key ***rks, size_t *nrks); |
86 | 93 | ||
87 | #endif /* _SK_API_H */ | 94 | #endif /* _SK_API_H */ |
diff --git a/sk-usbhid.c b/sk-usbhid.c index 22a4c5df5..2e1573c48 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c | |||
@@ -54,7 +54,7 @@ | |||
54 | } while (0) | 54 | } while (0) |
55 | #endif | 55 | #endif |
56 | 56 | ||
57 | #define SK_VERSION_MAJOR 0x00030000 /* current API version */ | 57 | #define SK_VERSION_MAJOR 0x00040000 /* current API version */ |
58 | 58 | ||
59 | /* Flags */ | 59 | /* Flags */ |
60 | #define SK_USER_PRESENCE_REQD 0x01 | 60 | #define SK_USER_PRESENCE_REQD 0x01 |
@@ -91,12 +91,18 @@ struct sk_sign_response { | |||
91 | }; | 91 | }; |
92 | 92 | ||
93 | struct sk_resident_key { | 93 | struct sk_resident_key { |
94 | uint8_t alg; | 94 | uint32_t alg; |
95 | size_t slot; | 95 | size_t slot; |
96 | char *application; | 96 | char *application; |
97 | struct sk_enroll_response key; | 97 | struct sk_enroll_response key; |
98 | }; | 98 | }; |
99 | 99 | ||
100 | struct sk_option { | ||
101 | char *name; | ||
102 | char *value; | ||
103 | uint8_t required; | ||
104 | }; | ||
105 | |||
100 | /* If building as part of OpenSSH, then rename exported functions */ | 106 | /* If building as part of OpenSSH, then rename exported functions */ |
101 | #if !defined(SK_STANDALONE) | 107 | #if !defined(SK_STANDALONE) |
102 | #define sk_api_version ssh_sk_api_version | 108 | #define sk_api_version ssh_sk_api_version |
@@ -109,17 +115,18 @@ struct sk_resident_key { | |||
109 | uint32_t sk_api_version(void); | 115 | uint32_t sk_api_version(void); |
110 | 116 | ||
111 | /* Enroll a U2F key (private key generation) */ | 117 | /* Enroll a U2F key (private key generation) */ |
112 | int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | 118 | int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, |
113 | const char *application, uint8_t flags, const char *pin, | 119 | const char *application, uint8_t flags, const char *pin, |
114 | struct sk_enroll_response **enroll_response); | 120 | struct sk_option **options, struct sk_enroll_response **enroll_response); |
115 | 121 | ||
116 | /* Sign a challenge */ | 122 | /* Sign a challenge */ |
117 | int sk_sign(int alg, const uint8_t *message, size_t message_len, | 123 | int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, |
118 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | 124 | const char *application, const uint8_t *key_handle, size_t key_handle_len, |
119 | uint8_t flags, const char *pin, struct sk_sign_response **sign_response); | 125 | uint8_t flags, const char *pin, struct sk_option **options, |
126 | struct sk_sign_response **sign_response); | ||
120 | 127 | ||
121 | /* Load resident keys */ | 128 | /* Load resident keys */ |
122 | int sk_load_resident_keys(const char *pin, | 129 | int sk_load_resident_keys(const char *pin, struct sk_option **options, |
123 | struct sk_resident_key ***rks, size_t *nrks); | 130 | struct sk_resident_key ***rks, size_t *nrks); |
124 | 131 | ||
125 | static void skdebug(const char *func, const char *fmt, ...) | 132 | static void skdebug(const char *func, const char *fmt, ...) |
@@ -235,15 +242,27 @@ try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, | |||
235 | 242 | ||
236 | /* Iterate over configured devices looking for a specific key handle */ | 243 | /* Iterate over configured devices looking for a specific key handle */ |
237 | static fido_dev_t * | 244 | static fido_dev_t * |
238 | find_device(const uint8_t *message, size_t message_len, const char *application, | 245 | find_device(const char *path, const uint8_t *message, size_t message_len, |
239 | const uint8_t *key_handle, size_t key_handle_len) | 246 | const char *application, const uint8_t *key_handle, size_t key_handle_len) |
240 | { | 247 | { |
241 | fido_dev_info_t *devlist = NULL; | 248 | fido_dev_info_t *devlist = NULL; |
242 | fido_dev_t *dev = NULL; | 249 | fido_dev_t *dev = NULL; |
243 | size_t devlist_len = 0, i; | 250 | size_t devlist_len = 0, i; |
244 | const char *path; | ||
245 | int r; | 251 | int r; |
246 | 252 | ||
253 | if (path != NULL) { | ||
254 | if ((dev = fido_dev_new()) == NULL) { | ||
255 | skdebug(__func__, "fido_dev_new failed"); | ||
256 | return NULL; | ||
257 | } | ||
258 | if ((r = fido_dev_open(dev, path)) != FIDO_OK) { | ||
259 | skdebug(__func__, "fido_dev_open failed"); | ||
260 | fido_dev_free(&dev); | ||
261 | return NULL; | ||
262 | } | ||
263 | return dev; | ||
264 | } | ||
265 | |||
247 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | 266 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { |
248 | skdebug(__func__, "fido_dev_info_new failed"); | 267 | skdebug(__func__, "fido_dev_info_new failed"); |
249 | goto out; | 268 | goto out; |
@@ -402,7 +421,7 @@ pack_public_key_ed25519(const fido_cred_t *cred, | |||
402 | } | 421 | } |
403 | 422 | ||
404 | static int | 423 | static int |
405 | pack_public_key(int alg, const fido_cred_t *cred, | 424 | pack_public_key(uint32_t alg, const fido_cred_t *cred, |
406 | struct sk_enroll_response *response) | 425 | struct sk_enroll_response *response) |
407 | { | 426 | { |
408 | switch(alg) { | 427 | switch(alg) { |
@@ -431,10 +450,45 @@ fidoerr_to_skerr(int fidoerr) | |||
431 | } | 450 | } |
432 | } | 451 | } |
433 | 452 | ||
453 | static int | ||
454 | check_enroll_options(struct sk_option **options, char **devicep, | ||
455 | uint8_t *user_id, size_t user_id_len) | ||
456 | { | ||
457 | size_t i; | ||
458 | |||
459 | if (options == NULL) | ||
460 | return 0; | ||
461 | for (i = 0; options[i] != NULL; i++) { | ||
462 | if (strcmp(options[i]->name, "device") == 0) { | ||
463 | if ((*devicep = strdup(options[i]->value)) == NULL) { | ||
464 | skdebug(__func__, "strdup device failed"); | ||
465 | return -1; | ||
466 | } | ||
467 | skdebug(__func__, "requested device %s", *devicep); | ||
468 | } if (strcmp(options[i]->name, "user") == 0) { | ||
469 | if (strlcpy(user_id, options[i]->value, user_id_len) >= | ||
470 | user_id_len) { | ||
471 | skdebug(__func__, "user too long"); | ||
472 | return -1; | ||
473 | } | ||
474 | skdebug(__func__, "requested user %s", | ||
475 | (char *)user_id); | ||
476 | } else { | ||
477 | skdebug(__func__, "requested unsupported option %s", | ||
478 | options[i]->name); | ||
479 | if (options[i]->required) { | ||
480 | skdebug(__func__, "unknown required option"); | ||
481 | return -1; | ||
482 | } | ||
483 | } | ||
484 | } | ||
485 | return 0; | ||
486 | } | ||
487 | |||
434 | int | 488 | int |
435 | sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | 489 | sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, |
436 | const char *application, uint8_t flags, const char *pin, | 490 | const char *application, uint8_t flags, const char *pin, |
437 | struct sk_enroll_response **enroll_response) | 491 | struct sk_option **options, struct sk_enroll_response **enroll_response) |
438 | { | 492 | { |
439 | fido_cred_t *cred = NULL; | 493 | fido_cred_t *cred = NULL; |
440 | fido_dev_t *dev = NULL; | 494 | fido_dev_t *dev = NULL; |
@@ -454,6 +508,11 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | |||
454 | skdebug(__func__, "enroll_response == NULL"); | 508 | skdebug(__func__, "enroll_response == NULL"); |
455 | goto out; | 509 | goto out; |
456 | } | 510 | } |
511 | memset(user_id, 0, sizeof(user_id)); | ||
512 | if (check_enroll_options(options, &device, | ||
513 | user_id, sizeof(user_id)) != 0) | ||
514 | goto out; /* error already logged */ | ||
515 | |||
457 | *enroll_response = NULL; | 516 | *enroll_response = NULL; |
458 | switch(alg) { | 517 | switch(alg) { |
459 | #ifdef WITH_OPENSSL | 518 | #ifdef WITH_OPENSSL |
@@ -468,7 +527,7 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | |||
468 | skdebug(__func__, "unsupported key type %d", alg); | 527 | skdebug(__func__, "unsupported key type %d", alg); |
469 | goto out; | 528 | goto out; |
470 | } | 529 | } |
471 | if ((device = pick_first_device()) == NULL) { | 530 | if (device == NULL && (device = pick_first_device()) == NULL) { |
472 | skdebug(__func__, "pick_first_device failed"); | 531 | skdebug(__func__, "pick_first_device failed"); |
473 | goto out; | 532 | goto out; |
474 | } | 533 | } |
@@ -477,7 +536,6 @@ sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | |||
477 | skdebug(__func__, "fido_cred_new failed"); | 536 | skdebug(__func__, "fido_cred_new failed"); |
478 | goto out; | 537 | goto out; |
479 | } | 538 | } |
480 | memset(user_id, 0, sizeof(user_id)); | ||
481 | if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { | 539 | if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { |
482 | skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); | 540 | skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); |
483 | goto out; | 541 | goto out; |
@@ -654,7 +712,8 @@ pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) | |||
654 | } | 712 | } |
655 | 713 | ||
656 | static int | 714 | static int |
657 | pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) | 715 | pack_sig(uint32_t alg, fido_assert_t *assert, |
716 | struct sk_sign_response *response) | ||
658 | { | 717 | { |
659 | switch(alg) { | 718 | switch(alg) { |
660 | #ifdef WITH_OPENSSL | 719 | #ifdef WITH_OPENSSL |
@@ -668,13 +727,42 @@ pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) | |||
668 | } | 727 | } |
669 | } | 728 | } |
670 | 729 | ||
730 | /* Checks sk_options for sk_sign() and sk_load_resident_keys() */ | ||
731 | static int | ||
732 | check_sign_load_resident_options(struct sk_option **options, char **devicep) | ||
733 | { | ||
734 | size_t i; | ||
735 | |||
736 | if (options == NULL) | ||
737 | return 0; | ||
738 | for (i = 0; options[i] != NULL; i++) { | ||
739 | if (strcmp(options[i]->name, "device") == 0) { | ||
740 | if ((*devicep = strdup(options[i]->value)) == NULL) { | ||
741 | skdebug(__func__, "strdup device failed"); | ||
742 | return -1; | ||
743 | } | ||
744 | skdebug(__func__, "requested device %s", *devicep); | ||
745 | } else { | ||
746 | skdebug(__func__, "requested unsupported option %s", | ||
747 | options[i]->name); | ||
748 | if (options[i]->required) { | ||
749 | skdebug(__func__, "unknown required option"); | ||
750 | return -1; | ||
751 | } | ||
752 | } | ||
753 | } | ||
754 | return 0; | ||
755 | } | ||
756 | |||
671 | int | 757 | int |
672 | sk_sign(int alg, const uint8_t *message, size_t message_len, | 758 | sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, |
673 | const char *application, | 759 | const char *application, |
674 | const uint8_t *key_handle, size_t key_handle_len, | 760 | const uint8_t *key_handle, size_t key_handle_len, |
675 | uint8_t flags, const char *pin, struct sk_sign_response **sign_response) | 761 | uint8_t flags, const char *pin, struct sk_option **options, |
762 | struct sk_sign_response **sign_response) | ||
676 | { | 763 | { |
677 | fido_assert_t *assert = NULL; | 764 | fido_assert_t *assert = NULL; |
765 | char *device = NULL; | ||
678 | fido_dev_t *dev = NULL; | 766 | fido_dev_t *dev = NULL; |
679 | struct sk_sign_response *response = NULL; | 767 | struct sk_sign_response *response = NULL; |
680 | int ret = SSH_SK_ERR_GENERAL; | 768 | int ret = SSH_SK_ERR_GENERAL; |
@@ -689,8 +777,10 @@ sk_sign(int alg, const uint8_t *message, size_t message_len, | |||
689 | goto out; | 777 | goto out; |
690 | } | 778 | } |
691 | *sign_response = NULL; | 779 | *sign_response = NULL; |
692 | if ((dev = find_device(message, message_len, application, key_handle, | 780 | if (check_sign_load_resident_options(options, &device) != 0) |
693 | key_handle_len)) == NULL) { | 781 | goto out; /* error already logged */ |
782 | if ((dev = find_device(device, message, message_len, | ||
783 | application, key_handle, key_handle_len)) == NULL) { | ||
694 | skdebug(__func__, "couldn't find device for key handle"); | 784 | skdebug(__func__, "couldn't find device for key handle"); |
695 | goto out; | 785 | goto out; |
696 | } | 786 | } |
@@ -737,6 +827,7 @@ sk_sign(int alg, const uint8_t *message, size_t message_len, | |||
737 | response = NULL; | 827 | response = NULL; |
738 | ret = 0; | 828 | ret = 0; |
739 | out: | 829 | out: |
830 | free(device); | ||
740 | if (response != NULL) { | 831 | if (response != NULL) { |
741 | free(response->sig_r); | 832 | free(response->sig_r); |
742 | free(response->sig_s); | 833 | free(response->sig_s); |
@@ -789,6 +880,7 @@ read_rks(const char *devpath, const char *pin, | |||
789 | } | 880 | } |
790 | skdebug(__func__, "get metadata for %s failed: %s", | 881 | skdebug(__func__, "get metadata for %s failed: %s", |
791 | devpath, fido_strerr(r)); | 882 | devpath, fido_strerr(r)); |
883 | ret = fidoerr_to_skerr(r); | ||
792 | goto out; | 884 | goto out; |
793 | } | 885 | } |
794 | skdebug(__func__, "existing %llu, remaining %llu", | 886 | skdebug(__func__, "existing %llu, remaining %llu", |
@@ -904,7 +996,7 @@ read_rks(const char *devpath, const char *pin, | |||
904 | } | 996 | } |
905 | 997 | ||
906 | int | 998 | int |
907 | sk_load_resident_keys(const char *pin, | 999 | sk_load_resident_keys(const char *pin, struct sk_option **options, |
908 | struct sk_resident_key ***rksp, size_t *nrksp) | 1000 | struct sk_resident_key ***rksp, size_t *nrksp) |
909 | { | 1001 | { |
910 | int ret = SSH_SK_ERR_GENERAL, r = -1; | 1002 | int ret = SSH_SK_ERR_GENERAL, r = -1; |
@@ -912,39 +1004,57 @@ sk_load_resident_keys(const char *pin, | |||
912 | size_t i, ndev = 0, nrks = 0; | 1004 | size_t i, ndev = 0, nrks = 0; |
913 | const fido_dev_info_t *di; | 1005 | const fido_dev_info_t *di; |
914 | struct sk_resident_key **rks = NULL; | 1006 | struct sk_resident_key **rks = NULL; |
1007 | char *device = NULL; | ||
915 | *rksp = NULL; | 1008 | *rksp = NULL; |
916 | *nrksp = 0; | 1009 | *nrksp = 0; |
917 | 1010 | ||
918 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | 1011 | if (check_sign_load_resident_options(options, &device) != 0) |
919 | skdebug(__func__, "fido_dev_info_new failed"); | 1012 | goto out; /* error already logged */ |
920 | goto out; | 1013 | if (device != NULL) { |
921 | } | 1014 | skdebug(__func__, "trying %s", device); |
922 | if ((r = fido_dev_info_manifest(devlist, | 1015 | if ((r = read_rks(device, pin, &rks, &nrks)) != 0) { |
923 | MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) { | ||
924 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | ||
925 | fido_strerr(r)); | ||
926 | goto out; | ||
927 | } | ||
928 | for (i = 0; i < ndev; i++) { | ||
929 | if ((di = fido_dev_info_ptr(devlist, i)) == NULL) { | ||
930 | skdebug(__func__, "no dev info at %zu", i); | ||
931 | continue; | ||
932 | } | ||
933 | skdebug(__func__, "trying %s", fido_dev_info_path(di)); | ||
934 | if ((r = read_rks(fido_dev_info_path(di), pin, | ||
935 | &rks, &nrks)) != 0) { | ||
936 | skdebug(__func__, "read_rks failed for %s", | 1016 | skdebug(__func__, "read_rks failed for %s", |
937 | fido_dev_info_path(di)); | 1017 | fido_dev_info_path(di)); |
938 | continue; | 1018 | ret = r; |
1019 | goto out; | ||
1020 | } | ||
1021 | } else { | ||
1022 | /* Try all devices */ | ||
1023 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | ||
1024 | skdebug(__func__, "fido_dev_info_new failed"); | ||
1025 | goto out; | ||
1026 | } | ||
1027 | if ((r = fido_dev_info_manifest(devlist, | ||
1028 | MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) { | ||
1029 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | ||
1030 | fido_strerr(r)); | ||
1031 | goto out; | ||
1032 | } | ||
1033 | for (i = 0; i < ndev; i++) { | ||
1034 | if ((di = fido_dev_info_ptr(devlist, i)) == NULL) { | ||
1035 | skdebug(__func__, "no dev info at %zu", i); | ||
1036 | continue; | ||
1037 | } | ||
1038 | skdebug(__func__, "trying %s", fido_dev_info_path(di)); | ||
1039 | if ((r = read_rks(fido_dev_info_path(di), pin, | ||
1040 | &rks, &nrks)) != 0) { | ||
1041 | skdebug(__func__, "read_rks failed for %s", | ||
1042 | fido_dev_info_path(di)); | ||
1043 | /* remember last error */ | ||
1044 | ret = r; | ||
1045 | continue; | ||
1046 | } | ||
939 | } | 1047 | } |
940 | } | 1048 | } |
941 | /* success */ | 1049 | /* success, unless we have no keys but a specific error */ |
942 | ret = 0; | 1050 | if (nrks > 0 || ret == SSH_SK_ERR_GENERAL) |
1051 | ret = 0; | ||
943 | *rksp = rks; | 1052 | *rksp = rks; |
944 | *nrksp = nrks; | 1053 | *nrksp = nrks; |
945 | rks = NULL; | 1054 | rks = NULL; |
946 | nrks = 0; | 1055 | nrks = 0; |
947 | out: | 1056 | out: |
1057 | free(device); | ||
948 | for (i = 0; i < nrks; i++) { | 1058 | for (i = 0; i < nrks; i++) { |
949 | free(rks[i]->application); | 1059 | free(rks[i]->application); |
950 | freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); | 1060 | freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-add.c,v 1.148 2019/12/30 09:22:49 djm Exp $ */ | 1 | /* $OpenBSD: ssh-add.c,v 1.149 2020/01/06 02:00:46 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -549,7 +549,8 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag) | |||
549 | char *fp; | 549 | char *fp; |
550 | 550 | ||
551 | pass = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN); | 551 | pass = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN); |
552 | if ((r = sshsk_load_resident(skprovider, pass, &keys, &nkeys)) != 0) { | 552 | if ((r = sshsk_load_resident(skprovider, NULL, pass, |
553 | &keys, &nkeys)) != 0) { | ||
553 | error("Unable to load resident keys: %s", ssh_err(r)); | 554 | error("Unable to load resident keys: %s", ssh_err(r)); |
554 | return r; | 555 | return r; |
555 | } | 556 | } |
diff --git a/ssh-keygen.1 b/ssh-keygen.1 index 7b83a2240..92c516588 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 | |||
@@ -1,4 +1,4 @@ | |||
1 | .\" $OpenBSD: ssh-keygen.1,v 1.188 2020/01/03 07:33:33 jmc Exp $ | 1 | .\" $OpenBSD: ssh-keygen.1,v 1.189 2020/01/06 02:00:46 djm Exp $ |
2 | .\" | 2 | .\" |
3 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | .\" Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -35,7 +35,7 @@ | |||
35 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 35 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
36 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 36 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
37 | .\" | 37 | .\" |
38 | .Dd $Mdocdate: January 3 2020 $ | 38 | .Dd $Mdocdate: January 6 2020 $ |
39 | .Dt SSH-KEYGEN 1 | 39 | .Dt SSH-KEYGEN 1 |
40 | .Os | 40 | .Os |
41 | .Sh NAME | 41 | .Sh NAME |
@@ -462,8 +462,18 @@ section may be specified. | |||
462 | .Pp | 462 | .Pp |
463 | When generating a key that will be hosted on a FIDO authenticator, this | 463 | When generating a key that will be hosted on a FIDO authenticator, this |
464 | flag may be used to specify key-specific options. | 464 | flag may be used to specify key-specific options. |
465 | Two FIDO authenticator options are supported at present: | 465 | The FIDO authenticator options are supported at present are: |
466 | .Pp | 466 | .Pp |
467 | .Cm application | ||
468 | overrides the default FIDO application/origin string of | ||
469 | .Dq ssh: . | ||
470 | This option may be useful when generating host or domain-specific resident | ||
471 | keys. | ||
472 | .Cm device | ||
473 | explicitly specify a device to generate the key on, rather than accepting | ||
474 | the authenticator middleware's automatic selection. | ||
475 | .Xr fido 4 | ||
476 | device to use, rather than letting the token middleware select one. | ||
467 | .Cm no-touch-required | 477 | .Cm no-touch-required |
468 | indicates that the generated private key should not require touch | 478 | indicates that the generated private key should not require touch |
469 | events (user presence) when making signatures. | 479 | events (user presence) when making signatures. |
@@ -478,6 +488,11 @@ Resident keys may be supported on FIDO2 tokens and typically require that | |||
478 | a PIN be set on the token prior to generation. | 488 | a PIN be set on the token prior to generation. |
479 | Resident keys may be loaded off the token using | 489 | Resident keys may be loaded off the token using |
480 | .Xr ssh-add 1 . | 490 | .Xr ssh-add 1 . |
491 | .Cm user | ||
492 | allows specification of a username to be associated with a resident key, | ||
493 | overriding the empty default username. | ||
494 | Specifying a username may be useful when generating multiple resident keys | ||
495 | for the same application name. | ||
481 | .Pp | 496 | .Pp |
482 | The | 497 | The |
483 | .Fl O | 498 | .Fl O |
diff --git a/ssh-keygen.c b/ssh-keygen.c index 7731339f7..d0ffa5cd7 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-keygen.c,v 1.381 2020/01/02 22:40:09 djm Exp $ */ | 1 | /* $OpenBSD: ssh-keygen.c,v 1.382 2020/01/06 02:00:46 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -2915,7 +2915,7 @@ skip_ssh_url_preamble(const char *s) | |||
2915 | } | 2915 | } |
2916 | 2916 | ||
2917 | static int | 2917 | static int |
2918 | do_download_sk(const char *skprovider) | 2918 | do_download_sk(const char *skprovider, const char *device) |
2919 | { | 2919 | { |
2920 | struct sshkey **keys; | 2920 | struct sshkey **keys; |
2921 | size_t nkeys, i; | 2921 | size_t nkeys, i; |
@@ -2927,7 +2927,8 @@ do_download_sk(const char *skprovider) | |||
2927 | fatal("Cannot download keys without provider"); | 2927 | fatal("Cannot download keys without provider"); |
2928 | 2928 | ||
2929 | pin = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN); | 2929 | pin = read_passphrase("Enter PIN for security key: ", RP_ALLOW_STDIN); |
2930 | if ((r = sshsk_load_resident(skprovider, pin, &keys, &nkeys)) != 0) { | 2930 | if ((r = sshsk_load_resident(skprovider, device, pin, |
2931 | &keys, &nkeys)) != 0) { | ||
2931 | freezero(pin, strlen(pin)); | 2932 | freezero(pin, strlen(pin)); |
2932 | error("Unable to load resident keys: %s", ssh_err(r)); | 2933 | error("Unable to load resident keys: %s", ssh_err(r)); |
2933 | return -1; | 2934 | return -1; |
@@ -3067,6 +3068,7 @@ main(int argc, char **argv) | |||
3067 | int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0; | 3068 | int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0; |
3068 | unsigned long long cert_serial = 0; | 3069 | unsigned long long cert_serial = 0; |
3069 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; | 3070 | char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; |
3071 | char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; | ||
3070 | size_t i, nopts = 0; | 3072 | size_t i, nopts = 0; |
3071 | u_int32_t bits = 0; | 3073 | u_int32_t bits = 0; |
3072 | uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; | 3074 | uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; |
@@ -3396,8 +3398,17 @@ main(int argc, char **argv) | |||
3396 | } | 3398 | } |
3397 | if (pkcs11provider != NULL) | 3399 | if (pkcs11provider != NULL) |
3398 | do_download(pw); | 3400 | do_download(pw); |
3399 | if (download_sk) | 3401 | if (download_sk) { |
3400 | return do_download_sk(sk_provider); | 3402 | for (i = 0; i < nopts; i++) { |
3403 | if (strncasecmp(opts[i], "device=", 7) == 0) { | ||
3404 | sk_device = xstrdup(opts[i] + 7); | ||
3405 | } else { | ||
3406 | fatal("Option \"%s\" is unsupported for " | ||
3407 | "FIDO authenticator download", opts[i]); | ||
3408 | } | ||
3409 | } | ||
3410 | return do_download_sk(sk_provider, sk_device); | ||
3411 | } | ||
3401 | if (print_fingerprint || print_bubblebabble) | 3412 | if (print_fingerprint || print_bubblebabble) |
3402 | do_fingerprint(pw); | 3413 | do_fingerprint(pw); |
3403 | if (change_passphrase) | 3414 | if (change_passphrase) |
@@ -3484,6 +3495,13 @@ main(int argc, char **argv) | |||
3484 | sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; | 3495 | sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; |
3485 | } else if (strcasecmp(opts[i], "resident") == 0) { | 3496 | } else if (strcasecmp(opts[i], "resident") == 0) { |
3486 | sk_flags |= SSH_SK_RESIDENT_KEY; | 3497 | sk_flags |= SSH_SK_RESIDENT_KEY; |
3498 | } else if (strncasecmp(opts[i], "device=", 7) == 0) { | ||
3499 | sk_device = xstrdup(opts[i] + 7); | ||
3500 | } else if (strncasecmp(opts[i], "user=", 5) == 0) { | ||
3501 | sk_user = xstrdup(opts[i] + 5); | ||
3502 | } else if (strncasecmp(opts[i], | ||
3503 | "application=", 12) == 0) { | ||
3504 | sk_application = xstrdup(opts[i] + 12); | ||
3487 | } else { | 3505 | } else { |
3488 | fatal("Option \"%s\" is unsupported for " | 3506 | fatal("Option \"%s\" is unsupported for " |
3489 | "FIDO authenticator enrollment", opts[i]); | 3507 | "FIDO authenticator enrollment", opts[i]); |
@@ -3495,14 +3513,11 @@ main(int argc, char **argv) | |||
3495 | } | 3513 | } |
3496 | passphrase = NULL; | 3514 | passphrase = NULL; |
3497 | for (i = 0 ; i < 3; i++) { | 3515 | for (i = 0 ; i < 3; i++) { |
3498 | if (!quiet) { | ||
3499 | printf("You may need to touch your security " | ||
3500 | "key to authorize key generation.\n"); | ||
3501 | } | ||
3502 | fflush(stdout); | 3516 | fflush(stdout); |
3503 | r = sshsk_enroll(type, sk_provider, | 3517 | r = sshsk_enroll(type, sk_provider, sk_device, |
3504 | cert_key_id == NULL ? "ssh:" : cert_key_id, | 3518 | sk_application == NULL ? "ssh:" : sk_application, |
3505 | sk_flags, passphrase, NULL, &private, NULL); | 3519 | sk_user, sk_flags, passphrase, NULL, |
3520 | &private, NULL); | ||
3506 | if (r == 0) | 3521 | if (r == 0) |
3507 | break; | 3522 | break; |
3508 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) | 3523 | if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) |
diff --git a/ssh-sk-client.c b/ssh-sk-client.c index 0033a6655..d3d37f792 100644 --- a/ssh-sk-client.c +++ b/ssh-sk-client.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk-client.c,v 1.3 2019/12/30 09:23:28 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk-client.c,v 1.4 2020/01/06 02:00:46 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -282,8 +282,9 @@ sshsk_sign(const char *provider, struct sshkey *key, | |||
282 | } | 282 | } |
283 | 283 | ||
284 | int | 284 | int |
285 | sshsk_enroll(int type, const char *provider_path, const char *application, | 285 | sshsk_enroll(int type, const char *provider_path, const char *device, |
286 | uint8_t flags, const char *pin, struct sshbuf *challenge_buf, | 286 | const char *application, const char *userid, uint8_t flags, |
287 | const char *pin, struct sshbuf *challenge_buf, | ||
287 | struct sshkey **keyp, struct sshbuf *attest) | 288 | struct sshkey **keyp, struct sshbuf *attest) |
288 | { | 289 | { |
289 | int oerrno, r = SSH_ERR_INTERNAL_ERROR; | 290 | int oerrno, r = SSH_ERR_INTERNAL_ERROR; |
@@ -311,7 +312,9 @@ sshsk_enroll(int type, const char *provider_path, const char *application, | |||
311 | if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_ENROLL)) != 0 || | 312 | if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_ENROLL)) != 0 || |
312 | (r = sshbuf_put_u32(req, (u_int)type)) != 0 || | 313 | (r = sshbuf_put_u32(req, (u_int)type)) != 0 || |
313 | (r = sshbuf_put_cstring(req, provider_path)) != 0 || | 314 | (r = sshbuf_put_cstring(req, provider_path)) != 0 || |
315 | (r = sshbuf_put_cstring(req, device)) != 0 || | ||
314 | (r = sshbuf_put_cstring(req, application)) != 0 || | 316 | (r = sshbuf_put_cstring(req, application)) != 0 || |
317 | (r = sshbuf_put_cstring(req, userid)) != 0 || | ||
315 | (r = sshbuf_put_u8(req, flags)) != 0 || | 318 | (r = sshbuf_put_u8(req, flags)) != 0 || |
316 | (r = sshbuf_put_cstring(req, pin)) != 0 || | 319 | (r = sshbuf_put_cstring(req, pin)) != 0 || |
317 | (r = sshbuf_put_stringb(req, challenge_buf)) != 0) { | 320 | (r = sshbuf_put_stringb(req, challenge_buf)) != 0) { |
@@ -358,8 +361,8 @@ sshsk_enroll(int type, const char *provider_path, const char *application, | |||
358 | } | 361 | } |
359 | 362 | ||
360 | int | 363 | int |
361 | sshsk_load_resident(const char *provider_path, const char *pin, | 364 | sshsk_load_resident(const char *provider_path, const char *device, |
362 | struct sshkey ***keysp, size_t *nkeysp) | 365 | const char *pin, struct sshkey ***keysp, size_t *nkeysp) |
363 | { | 366 | { |
364 | int oerrno, r = SSH_ERR_INTERNAL_ERROR; | 367 | int oerrno, r = SSH_ERR_INTERNAL_ERROR; |
365 | struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL; | 368 | struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL; |
@@ -378,6 +381,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, | |||
378 | 381 | ||
379 | if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_LOAD_RESIDENT)) != 0 || | 382 | if ((r = sshbuf_put_u32(req, SSH_SK_HELPER_LOAD_RESIDENT)) != 0 || |
380 | (r = sshbuf_put_cstring(req, provider_path)) != 0 || | 383 | (r = sshbuf_put_cstring(req, provider_path)) != 0 || |
384 | (r = sshbuf_put_cstring(req, device)) != 0 || | ||
381 | (r = sshbuf_put_cstring(req, pin)) != 0) { | 385 | (r = sshbuf_put_cstring(req, pin)) != 0) { |
382 | error("%s: compose: %s", __func__, ssh_err(r)); | 386 | error("%s: compose: %s", __func__, ssh_err(r)); |
383 | goto out; | 387 | goto out; |
diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index 590ff8501..85a461d53 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk-helper.c,v 1.6 2019/12/30 09:23:28 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk-helper.c,v 1.7 2020/01/06 02:00:46 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -77,6 +77,17 @@ reply_error(int r, char *fmt, ...) | |||
77 | return resp; | 77 | return resp; |
78 | } | 78 | } |
79 | 79 | ||
80 | /* If the specified string is zero length, then free it and replace with NULL */ | ||
81 | static void | ||
82 | null_empty(char **s) | ||
83 | { | ||
84 | if (s == NULL || *s == NULL || **s != '\0') | ||
85 | return; | ||
86 | |||
87 | free(*s); | ||
88 | *s = NULL; | ||
89 | } | ||
90 | |||
80 | static struct sshbuf * | 91 | static struct sshbuf * |
81 | process_sign(struct sshbuf *req) | 92 | process_sign(struct sshbuf *req) |
82 | { | 93 | { |
@@ -108,10 +119,7 @@ process_sign(struct sshbuf *req) | |||
108 | "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), | 119 | "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), |
109 | provider, msglen, (u_long)compat); | 120 | provider, msglen, (u_long)compat); |
110 | 121 | ||
111 | if (*pin == 0) { | 122 | null_empty(&pin); |
112 | free(pin); | ||
113 | pin = NULL; | ||
114 | } | ||
115 | 123 | ||
116 | if ((r = sshsk_sign(provider, key, &sig, &siglen, | 124 | if ((r = sshsk_sign(provider, key, &sig, &siglen, |
117 | message, msglen, compat, pin)) != 0) { | 125 | message, msglen, compat, pin)) != 0) { |
@@ -138,7 +146,7 @@ process_enroll(struct sshbuf *req) | |||
138 | { | 146 | { |
139 | int r; | 147 | int r; |
140 | u_int type; | 148 | u_int type; |
141 | char *provider, *application, *pin; | 149 | char *provider, *application, *pin, *device, *userid; |
142 | uint8_t flags; | 150 | uint8_t flags; |
143 | struct sshbuf *challenge, *attest, *kbuf, *resp; | 151 | struct sshbuf *challenge, *attest, *kbuf, *resp; |
144 | struct sshkey *key; | 152 | struct sshkey *key; |
@@ -149,7 +157,9 @@ process_enroll(struct sshbuf *req) | |||
149 | 157 | ||
150 | if ((r = sshbuf_get_u32(req, &type)) != 0 || | 158 | if ((r = sshbuf_get_u32(req, &type)) != 0 || |
151 | (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || | 159 | (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || |
160 | (r = sshbuf_get_cstring(req, &device, NULL)) != 0 || | ||
152 | (r = sshbuf_get_cstring(req, &application, NULL)) != 0 || | 161 | (r = sshbuf_get_cstring(req, &application, NULL)) != 0 || |
162 | (r = sshbuf_get_cstring(req, &userid, NULL)) != 0 || | ||
153 | (r = sshbuf_get_u8(req, &flags)) != 0 || | 163 | (r = sshbuf_get_u8(req, &flags)) != 0 || |
154 | (r = sshbuf_get_cstring(req, &pin, NULL)) != 0 || | 164 | (r = sshbuf_get_cstring(req, &pin, NULL)) != 0 || |
155 | (r = sshbuf_froms(req, &challenge)) != 0) | 165 | (r = sshbuf_froms(req, &challenge)) != 0) |
@@ -163,13 +173,12 @@ process_enroll(struct sshbuf *req) | |||
163 | sshbuf_free(challenge); | 173 | sshbuf_free(challenge); |
164 | challenge = NULL; | 174 | challenge = NULL; |
165 | } | 175 | } |
166 | if (*pin == 0) { | 176 | null_empty(&device); |
167 | free(pin); | 177 | null_empty(&userid); |
168 | pin = NULL; | 178 | null_empty(&pin); |
169 | } | ||
170 | 179 | ||
171 | if ((r = sshsk_enroll((int)type, provider, application, flags, pin, | 180 | if ((r = sshsk_enroll((int)type, provider, device, application, userid, |
172 | challenge, &key, attest)) != 0) { | 181 | flags, pin, challenge, &key, attest)) != 0) { |
173 | resp = reply_error(r, "Enrollment failed: %s", ssh_err(r)); | 182 | resp = reply_error(r, "Enrollment failed: %s", ssh_err(r)); |
174 | goto out; | 183 | goto out; |
175 | } | 184 | } |
@@ -200,7 +209,7 @@ static struct sshbuf * | |||
200 | process_load_resident(struct sshbuf *req) | 209 | process_load_resident(struct sshbuf *req) |
201 | { | 210 | { |
202 | int r; | 211 | int r; |
203 | char *provider, *pin; | 212 | char *provider, *pin, *device; |
204 | struct sshbuf *kbuf, *resp; | 213 | struct sshbuf *kbuf, *resp; |
205 | struct sshkey **keys = NULL; | 214 | struct sshkey **keys = NULL; |
206 | size_t nkeys = 0, i; | 215 | size_t nkeys = 0, i; |
@@ -209,17 +218,17 @@ process_load_resident(struct sshbuf *req) | |||
209 | fatal("%s: sshbuf_new failed", __progname); | 218 | fatal("%s: sshbuf_new failed", __progname); |
210 | 219 | ||
211 | if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || | 220 | if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || |
221 | (r = sshbuf_get_cstring(req, &device, NULL)) != 0 || | ||
212 | (r = sshbuf_get_cstring(req, &pin, NULL)) != 0) | 222 | (r = sshbuf_get_cstring(req, &pin, NULL)) != 0) |
213 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); | 223 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); |
214 | if (sshbuf_len(req) != 0) | 224 | if (sshbuf_len(req) != 0) |
215 | fatal("%s: trailing data in request", __progname); | 225 | fatal("%s: trailing data in request", __progname); |
216 | 226 | ||
217 | if (*pin == 0) { | 227 | null_empty(&device); |
218 | free(pin); | 228 | null_empty(&pin); |
219 | pin = NULL; | ||
220 | } | ||
221 | 229 | ||
222 | if ((r = sshsk_load_resident(provider, pin, &keys, &nkeys)) != 0) { | 230 | if ((r = sshsk_load_resident(provider, device, pin, |
231 | &keys, &nkeys)) != 0) { | ||
223 | resp = reply_error(r, " sshsk_load_resident failed: %s", | 232 | resp = reply_error(r, " sshsk_load_resident failed: %s", |
224 | ssh_err(r)); | 233 | ssh_err(r)); |
225 | goto out; | 234 | goto out; |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk.c,v 1.23 2019/12/30 09:24:45 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk.c,v 1.24 2020/01/06 02:00:47 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -53,29 +53,32 @@ struct sshsk_provider { | |||
53 | /* Enroll a U2F key (private key generation) */ | 53 | /* Enroll a U2F key (private key generation) */ |
54 | int (*sk_enroll)(int alg, const uint8_t *challenge, | 54 | int (*sk_enroll)(int alg, const uint8_t *challenge, |
55 | size_t challenge_len, const char *application, uint8_t flags, | 55 | size_t challenge_len, const char *application, uint8_t flags, |
56 | const char *pin, struct sk_enroll_response **enroll_response); | 56 | const char *pin, struct sk_option **opts, |
57 | struct sk_enroll_response **enroll_response); | ||
57 | 58 | ||
58 | /* Sign a challenge */ | 59 | /* Sign a challenge */ |
59 | int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, | 60 | int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, |
60 | const char *application, | 61 | const char *application, |
61 | const uint8_t *key_handle, size_t key_handle_len, | 62 | const uint8_t *key_handle, size_t key_handle_len, |
62 | uint8_t flags, const char *pin, | 63 | uint8_t flags, const char *pin, struct sk_option **opts, |
63 | struct sk_sign_response **sign_response); | 64 | struct sk_sign_response **sign_response); |
64 | 65 | ||
65 | /* Enumerate resident keys */ | 66 | /* Enumerate resident keys */ |
66 | int (*sk_load_resident_keys)(const char *pin, | 67 | int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts, |
67 | struct sk_resident_key ***rks, size_t *nrks); | 68 | struct sk_resident_key ***rks, size_t *nrks); |
68 | }; | 69 | }; |
69 | 70 | ||
70 | /* Built-in version */ | 71 | /* Built-in version */ |
71 | int ssh_sk_enroll(int alg, const uint8_t *challenge, | 72 | int ssh_sk_enroll(int alg, const uint8_t *challenge, |
72 | size_t challenge_len, const char *application, uint8_t flags, | 73 | size_t challenge_len, const char *application, uint8_t flags, |
73 | const char *pin, struct sk_enroll_response **enroll_response); | 74 | const char *pin, struct sk_option **opts, |
75 | struct sk_enroll_response **enroll_response); | ||
74 | int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, | 76 | int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, |
75 | const char *application, | 77 | const char *application, |
76 | const uint8_t *key_handle, size_t key_handle_len, | 78 | const uint8_t *key_handle, size_t key_handle_len, |
77 | uint8_t flags, const char *pin, struct sk_sign_response **sign_response); | 79 | uint8_t flags, const char *pin, struct sk_option **opts, |
78 | int ssh_sk_load_resident_keys(const char *pin, | 80 | struct sk_sign_response **sign_response); |
81 | int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts, | ||
79 | struct sk_resident_key ***rks, size_t *nrks); | 82 | struct sk_resident_key ***rks, size_t *nrks); |
80 | 83 | ||
81 | static void | 84 | static void |
@@ -339,9 +342,80 @@ skerr_to_ssherr(int skerr) | |||
339 | } | 342 | } |
340 | } | 343 | } |
341 | 344 | ||
345 | static void | ||
346 | sshsk_free_options(struct sk_option **opts) | ||
347 | { | ||
348 | size_t i; | ||
349 | |||
350 | if (opts == NULL) | ||
351 | return; | ||
352 | for (i = 0; opts[i] != NULL; i++) { | ||
353 | free(opts[i]->name); | ||
354 | free(opts[i]->value); | ||
355 | free(opts[i]); | ||
356 | } | ||
357 | free(opts); | ||
358 | } | ||
359 | |||
360 | static int | ||
361 | sshsk_add_option(struct sk_option ***optsp, size_t *noptsp, | ||
362 | const char *name, const char *value, uint8_t required) | ||
363 | { | ||
364 | struct sk_option **opts = *optsp; | ||
365 | size_t nopts = *noptsp; | ||
366 | |||
367 | if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ | ||
368 | sizeof(*opts))) == NULL) { | ||
369 | error("%s: array alloc failed", __func__); | ||
370 | return SSH_ERR_ALLOC_FAIL; | ||
371 | } | ||
372 | *optsp = opts; | ||
373 | *noptsp = nopts + 1; | ||
374 | if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { | ||
375 | error("%s: alloc failed", __func__); | ||
376 | return SSH_ERR_ALLOC_FAIL; | ||
377 | } | ||
378 | if ((opts[nopts]->name = strdup(name)) == NULL || | ||
379 | (opts[nopts]->value = strdup(value)) == NULL) { | ||
380 | error("%s: alloc failed", __func__); | ||
381 | return SSH_ERR_ALLOC_FAIL; | ||
382 | } | ||
383 | opts[nopts]->required = required; | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | static int | ||
388 | make_options(const char *device, const char *user_id, | ||
389 | struct sk_option ***optsp) | ||
390 | { | ||
391 | struct sk_option **opts = NULL; | ||
392 | size_t nopts = 0; | ||
393 | int r, ret = SSH_ERR_INTERNAL_ERROR; | ||
394 | |||
395 | if (device != NULL && | ||
396 | (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) { | ||
397 | ret = r; | ||
398 | goto out; | ||
399 | } | ||
400 | if (user_id != NULL && | ||
401 | (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) { | ||
402 | ret = r; | ||
403 | goto out; | ||
404 | } | ||
405 | /* success */ | ||
406 | *optsp = opts; | ||
407 | opts = NULL; | ||
408 | nopts = 0; | ||
409 | ret = 0; | ||
410 | out: | ||
411 | sshsk_free_options(opts); | ||
412 | return ret; | ||
413 | } | ||
414 | |||
342 | int | 415 | int |
343 | sshsk_enroll(int type, const char *provider_path, const char *application, | 416 | sshsk_enroll(int type, const char *provider_path, const char *device, |
344 | uint8_t flags, const char *pin, struct sshbuf *challenge_buf, | 417 | const char *application, const char *userid, uint8_t flags, |
418 | const char *pin, struct sshbuf *challenge_buf, | ||
345 | struct sshkey **keyp, struct sshbuf *attest) | 419 | struct sshkey **keyp, struct sshbuf *attest) |
346 | { | 420 | { |
347 | struct sshsk_provider *skp = NULL; | 421 | struct sshsk_provider *skp = NULL; |
@@ -350,17 +424,23 @@ sshsk_enroll(int type, const char *provider_path, const char *application, | |||
350 | const u_char *challenge; | 424 | const u_char *challenge; |
351 | size_t challenge_len; | 425 | size_t challenge_len; |
352 | struct sk_enroll_response *resp = NULL; | 426 | struct sk_enroll_response *resp = NULL; |
427 | struct sk_option **opts = NULL; | ||
353 | int r = SSH_ERR_INTERNAL_ERROR; | 428 | int r = SSH_ERR_INTERNAL_ERROR; |
354 | int alg; | 429 | int alg; |
355 | 430 | ||
356 | debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, " | 431 | debug("%s: provider \"%s\", device \"%s\", application \"%s\", " |
357 | "challenge len %zu%s", __func__, provider_path, application, | 432 | "userid \"%s\", flags 0x%02x, challenge len %zu%s", __func__, |
358 | flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), | 433 | provider_path, device, application, userid, flags, |
434 | challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), | ||
359 | (pin != NULL && *pin != '\0') ? " with-pin" : ""); | 435 | (pin != NULL && *pin != '\0') ? " with-pin" : ""); |
360 | 436 | ||
361 | *keyp = NULL; | 437 | *keyp = NULL; |
362 | if (attest) | 438 | if (attest) |
363 | sshbuf_reset(attest); | 439 | sshbuf_reset(attest); |
440 | |||
441 | if ((r = make_options(device, userid, &opts)) != 0) | ||
442 | goto out; | ||
443 | |||
364 | switch (type) { | 444 | switch (type) { |
365 | #ifdef WITH_OPENSSL | 445 | #ifdef WITH_OPENSSL |
366 | case KEY_ECDSA_SK: | 446 | case KEY_ECDSA_SK: |
@@ -407,7 +487,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application, | |||
407 | /* XXX validate flags? */ | 487 | /* XXX validate flags? */ |
408 | /* enroll key */ | 488 | /* enroll key */ |
409 | if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, | 489 | if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, |
410 | flags, pin, &resp)) != 0) { | 490 | flags, pin, opts, &resp)) != 0) { |
411 | error("Security key provider \"%s\" returned failure %d", | 491 | error("Security key provider \"%s\" returned failure %d", |
412 | provider_path, r); | 492 | provider_path, r); |
413 | r = skerr_to_ssherr(r); | 493 | r = skerr_to_ssherr(r); |
@@ -437,6 +517,7 @@ sshsk_enroll(int type, const char *provider_path, const char *application, | |||
437 | key = NULL; /* transferred */ | 517 | key = NULL; /* transferred */ |
438 | r = 0; | 518 | r = 0; |
439 | out: | 519 | out: |
520 | sshsk_free_options(opts); | ||
440 | sshsk_free(skp); | 521 | sshsk_free(skp); |
441 | sshkey_free(key); | 522 | sshkey_free(key); |
442 | sshsk_free_enroll_response(resp); | 523 | sshsk_free_enroll_response(resp); |
@@ -528,6 +609,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, | |||
528 | struct sk_sign_response *resp = NULL; | 609 | struct sk_sign_response *resp = NULL; |
529 | struct sshbuf *inner_sig = NULL, *sig = NULL; | 610 | struct sshbuf *inner_sig = NULL, *sig = NULL; |
530 | uint8_t message[32]; | 611 | uint8_t message[32]; |
612 | struct sk_option **opts = NULL; | ||
531 | 613 | ||
532 | debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__, | 614 | debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__, |
533 | provider_path, sshkey_type(key), key->sk_flags, | 615 | provider_path, sshkey_type(key), key->sk_flags, |
@@ -571,7 +653,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, | |||
571 | if ((r = skp->sk_sign(alg, message, sizeof(message), | 653 | if ((r = skp->sk_sign(alg, message, sizeof(message), |
572 | key->sk_application, | 654 | key->sk_application, |
573 | sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), | 655 | sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), |
574 | key->sk_flags, pin, &resp)) != 0) { | 656 | key->sk_flags, pin, opts, &resp)) != 0) { |
575 | debug("%s: sk_sign failed with code %d", __func__, r); | 657 | debug("%s: sk_sign failed with code %d", __func__, r); |
576 | r = skerr_to_ssherr(r); | 658 | r = skerr_to_ssherr(r); |
577 | goto out; | 659 | goto out; |
@@ -617,6 +699,7 @@ sshsk_sign(const char *provider_path, struct sshkey *key, | |||
617 | /* success */ | 699 | /* success */ |
618 | r = 0; | 700 | r = 0; |
619 | out: | 701 | out: |
702 | sshsk_free_options(opts); | ||
620 | explicit_bzero(message, sizeof(message)); | 703 | explicit_bzero(message, sizeof(message)); |
621 | sshsk_free(skp); | 704 | sshsk_free(skp); |
622 | sshsk_free_sign_response(resp); | 705 | sshsk_free_sign_response(resp); |
@@ -645,8 +728,8 @@ sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) | |||
645 | } | 728 | } |
646 | 729 | ||
647 | int | 730 | int |
648 | sshsk_load_resident(const char *provider_path, const char *pin, | 731 | sshsk_load_resident(const char *provider_path, const char *device, |
649 | struct sshkey ***keysp, size_t *nkeysp) | 732 | const char *pin, struct sshkey ***keysp, size_t *nkeysp) |
650 | { | 733 | { |
651 | struct sshsk_provider *skp = NULL; | 734 | struct sshsk_provider *skp = NULL; |
652 | int r = SSH_ERR_INTERNAL_ERROR; | 735 | int r = SSH_ERR_INTERNAL_ERROR; |
@@ -654,6 +737,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, | |||
654 | size_t i, nrks = 0, nkeys = 0; | 737 | size_t i, nrks = 0, nkeys = 0; |
655 | struct sshkey *key = NULL, **keys = NULL, **tmp; | 738 | struct sshkey *key = NULL, **keys = NULL, **tmp; |
656 | uint8_t flags; | 739 | uint8_t flags; |
740 | struct sk_option **opts = NULL; | ||
657 | 741 | ||
658 | debug("%s: provider \"%s\"%s", __func__, provider_path, | 742 | debug("%s: provider \"%s\"%s", __func__, provider_path, |
659 | (pin != NULL && *pin != '\0') ? ", have-pin": ""); | 743 | (pin != NULL && *pin != '\0') ? ", have-pin": ""); |
@@ -663,11 +747,13 @@ sshsk_load_resident(const char *provider_path, const char *pin, | |||
663 | *keysp = NULL; | 747 | *keysp = NULL; |
664 | *nkeysp = 0; | 748 | *nkeysp = 0; |
665 | 749 | ||
750 | if ((r = make_options(device, NULL, &opts)) != 0) | ||
751 | goto out; | ||
666 | if ((skp = sshsk_open(provider_path)) == NULL) { | 752 | if ((skp = sshsk_open(provider_path)) == NULL) { |
667 | r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ | 753 | r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ |
668 | goto out; | 754 | goto out; |
669 | } | 755 | } |
670 | if ((r = skp->sk_load_resident_keys(pin, &rks, &nrks)) != 0) { | 756 | if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) { |
671 | error("Security key provider \"%s\" returned failure %d", | 757 | error("Security key provider \"%s\" returned failure %d", |
672 | provider_path, r); | 758 | provider_path, r); |
673 | r = skerr_to_ssherr(r); | 759 | r = skerr_to_ssherr(r); |
@@ -710,6 +796,7 @@ sshsk_load_resident(const char *provider_path, const char *pin, | |||
710 | nkeys = 0; | 796 | nkeys = 0; |
711 | r = 0; | 797 | r = 0; |
712 | out: | 798 | out: |
799 | sshsk_free_options(opts); | ||
713 | sshsk_free(skp); | 800 | sshsk_free(skp); |
714 | sshsk_free_sk_resident_keys(rks, nrks); | 801 | sshsk_free_sk_resident_keys(rks, nrks); |
715 | sshkey_free(key); | 802 | sshkey_free(key); |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk.h,v 1.8 2019/12/30 09:23:28 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk.h,v 1.9 2020/01/06 02:00:47 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -20,9 +20,10 @@ | |||
20 | 20 | ||
21 | struct sshbuf; | 21 | struct sshbuf; |
22 | struct sshkey; | 22 | struct sshkey; |
23 | struct sk_option; | ||
23 | 24 | ||
24 | /* Version of protocol expected from ssh-sk-helper */ | 25 | /* Version of protocol expected from ssh-sk-helper */ |
25 | #define SSH_SK_HELPER_VERSION 3 | 26 | #define SSH_SK_HELPER_VERSION 4 |
26 | 27 | ||
27 | /* ssh-sk-helper messages */ | 28 | /* ssh-sk-helper messages */ |
28 | #define SSH_SK_HELPER_ERROR 0 /* Only valid H->C */ | 29 | #define SSH_SK_HELPER_ERROR 0 /* Only valid H->C */ |
@@ -40,8 +41,9 @@ struct sshkey; | |||
40 | * If successful and the attest_data buffer is not NULL then attestation | 41 | * If successful and the attest_data buffer is not NULL then attestation |
41 | * information is placed there. | 42 | * information is placed there. |
42 | */ | 43 | */ |
43 | int sshsk_enroll(int type, const char *provider_path, const char *application, | 44 | int sshsk_enroll(int type, const char *provider_path, const char *device, |
44 | uint8_t flags, const char *pin, struct sshbuf *challenge_buf, | 45 | const char *application, const char *userid, uint8_t flags, |
46 | const char *pin, struct sshbuf *challenge_buf, | ||
45 | struct sshkey **keyp, struct sshbuf *attest); | 47 | struct sshkey **keyp, struct sshbuf *attest); |
46 | 48 | ||
47 | /* | 49 | /* |
@@ -60,8 +62,8 @@ int sshsk_sign(const char *provider_path, struct sshkey *key, | |||
60 | * | 62 | * |
61 | * Returns 0 on success or a ssherr.h error code on failure. | 63 | * Returns 0 on success or a ssherr.h error code on failure. |
62 | */ | 64 | */ |
63 | int sshsk_load_resident(const char *provider_path, const char *pin, | 65 | int sshsk_load_resident(const char *provider_path, const char *device, |
64 | struct sshkey ***keysp, size_t *nkeysp); | 66 | const char *pin, struct sshkey ***keysp, size_t *nkeysp); |
65 | 67 | ||
66 | #endif /* _SSH_SK_H */ | 68 | #endif /* _SSH_SK_H */ |
67 | 69 | ||