summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2020-01-06 02:00:46 +0000
committerDamien Miller <djm@mindrot.org>2020-01-06 13:12:46 +1100
commitc312ca077cd2a6c15545cd6b4d34ee2f69289174 (patch)
treeb8dd974c55dd0de351dfcbfc4f33fddb935a1c12
parent2ab335712d084d9ccaf3f53afc3fa9535329da87 (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.u2f47
-rw-r--r--sk-api.h23
-rw-r--r--sk-usbhid.c194
-rw-r--r--ssh-add.c5
-rw-r--r--ssh-keygen.123
-rw-r--r--ssh-keygen.c39
-rw-r--r--ssh-sk-client.c14
-rw-r--r--ssh-sk-helper.c45
-rw-r--r--ssh-sk.c121
-rw-r--r--ssh-sk.h14
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
234The middleware library need only expose a handful of functions: 234The 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
294The SSH_SK_VERSION_MAJOR should be incremented for each incompatible 307The SSH_SK_VERSION_MAJOR should be incremented for each incompatible
295API change. 308API change.
296 309
297In OpenSSH, these will be invoked by using a similar mechanism to 310The options may be used to pass miscellaneous options to the middleware
311as a NULL-terminated array of pointers to struct sk_option. The middleware
312may ignore unsupported or unknown options unless the "important" flag is
313set, in which case it should return failure if an unsupported option is
314requested.
315
316At 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
330In OpenSSH, the middleware will be invoked by using a similar mechanism to
298ssh-pkcs11-helper to provide address-space containment of the 331ssh-pkcs11-helper to provide address-space containment of the
299middleware from ssh-agent. 332middleware from ssh-agent.
300 333
diff --git a/sk-api.h b/sk-api.h
index dc786d556..93d6a1229 100644
--- a/sk-api.h
+++ b/sk-api.h
@@ -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
60struct sk_resident_key { 60struct 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 */ 67struct 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 */
71uint32_t sk_api_version(void); 77uint32_t sk_api_version(void);
72 78
73/* Enroll a U2F key (private key generation) */ 79/* Enroll a U2F key (private key generation) */
74int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, 80int 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 */
79int sk_sign(int alg, const uint8_t *message, size_t message_len, 85int 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 */
84int sk_load_resident_keys(const char *pin, 91int 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
93struct sk_resident_key { 93struct 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
100struct 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 {
109uint32_t sk_api_version(void); 115uint32_t sk_api_version(void);
110 116
111/* Enroll a U2F key (private key generation) */ 117/* Enroll a U2F key (private key generation) */
112int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, 118int 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 */
117int sk_sign(int alg, const uint8_t *message, size_t message_len, 123int 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 */
122int sk_load_resident_keys(const char *pin, 129int 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
125static void skdebug(const char *func, const char *fmt, ...) 132static 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 */
237static fido_dev_t * 244static fido_dev_t *
238find_device(const uint8_t *message, size_t message_len, const char *application, 245find_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
404static int 423static int
405pack_public_key(int alg, const fido_cred_t *cred, 424pack_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
453static int
454check_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
434int 488int
435sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, 489sk_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
656static int 714static int
657pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) 715pack_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() */
731static int
732check_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
671int 757int
672sk_sign(int alg, const uint8_t *message, size_t message_len, 758sk_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
906int 998int
907sk_load_resident_keys(const char *pin, 999sk_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);
diff --git a/ssh-add.c b/ssh-add.c
index c25b57cc1..fbb2578dd 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -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
463When generating a key that will be hosted on a FIDO authenticator, this 463When generating a key that will be hosted on a FIDO authenticator, this
464flag may be used to specify key-specific options. 464flag may be used to specify key-specific options.
465Two FIDO authenticator options are supported at present: 465The FIDO authenticator options are supported at present are:
466.Pp 466.Pp
467.Cm application
468overrides the default FIDO application/origin string of
469.Dq ssh: .
470This option may be useful when generating host or domain-specific resident
471keys.
472.Cm device
473explicitly specify a device to generate the key on, rather than accepting
474the authenticator middleware's automatic selection.
475.Xr fido 4
476device to use, rather than letting the token middleware select one.
467.Cm no-touch-required 477.Cm no-touch-required
468indicates that the generated private key should not require touch 478indicates that the generated private key should not require touch
469events (user presence) when making signatures. 479events (user presence) when making signatures.
@@ -478,6 +488,11 @@ Resident keys may be supported on FIDO2 tokens and typically require that
478a PIN be set on the token prior to generation. 488a PIN be set on the token prior to generation.
479Resident keys may be loaded off the token using 489Resident keys may be loaded off the token using
480.Xr ssh-add 1 . 490.Xr ssh-add 1 .
491.Cm user
492allows specification of a username to be associated with a resident key,
493overriding the empty default username.
494Specifying a username may be useful when generating multiple resident keys
495for the same application name.
481.Pp 496.Pp
482The 497The
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
2917static int 2917static int
2918do_download_sk(const char *skprovider) 2918do_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
284int 284int
285sshsk_enroll(int type, const char *provider_path, const char *application, 285sshsk_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
360int 363int
361sshsk_load_resident(const char *provider_path, const char *pin, 364sshsk_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 */
81static void
82null_empty(char **s)
83{
84 if (s == NULL || *s == NULL || **s != '\0')
85 return;
86
87 free(*s);
88 *s = NULL;
89}
90
80static struct sshbuf * 91static struct sshbuf *
81process_sign(struct sshbuf *req) 92process_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 *
200process_load_resident(struct sshbuf *req) 209process_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;
diff --git a/ssh-sk.c b/ssh-sk.c
index b1d0d6c58..0ef52e299 100644
--- a/ssh-sk.c
+++ b/ssh-sk.c
@@ -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 */
71int ssh_sk_enroll(int alg, const uint8_t *challenge, 72int 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);
74int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, 76int 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,
78int ssh_sk_load_resident_keys(const char *pin, 80 struct sk_sign_response **sign_response);
81int 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
81static void 84static void
@@ -339,9 +342,80 @@ skerr_to_ssherr(int skerr)
339 } 342 }
340} 343}
341 344
345static void
346sshsk_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
360static int
361sshsk_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
387static int
388make_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
342int 415int
343sshsk_enroll(int type, const char *provider_path, const char *application, 416sshsk_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
647int 730int
648sshsk_load_resident(const char *provider_path, const char *pin, 731sshsk_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);
diff --git a/ssh-sk.h b/ssh-sk.h
index 348759a98..ea9ff6e1a 100644
--- a/ssh-sk.h
+++ b/ssh-sk.h
@@ -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
21struct sshbuf; 21struct sshbuf;
22struct sshkey; 22struct sshkey;
23struct 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 */
43int sshsk_enroll(int type, const char *provider_path, const char *application, 44int 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 */
63int sshsk_load_resident(const char *provider_path, const char *pin, 65int 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