summaryrefslogtreecommitdiff
path: root/sk-usbhid.c
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 /sk-usbhid.c
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
Diffstat (limited to 'sk-usbhid.c')
-rw-r--r--sk-usbhid.c194
1 files changed, 152 insertions, 42 deletions
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);