diff options
author | djm@openbsd.org <djm@openbsd.org> | 2019-12-30 09:21:16 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2019-12-30 20:58:19 +1100 |
commit | 14cea36df397677b8f8568204300ef654114fd76 (patch) | |
tree | 4c9b0bf5108df396f8d9eaff576537e7c9cc50e9 | |
parent | 2fe05fcb4a2695f190b4fcf27770b655586ab349 (diff) |
upstream: resident keys support in SK API
Adds a sk_load_resident_keys() function to the security key
API that accepts a security key provider and a PIN and returns
a list of keys.
Implement support for this in the usbhid middleware.
feedback and ok markus@
OpenBSD-Commit-ID: 67e984e4e87f4999ce447a6178c4249a9174eff0
-rw-r--r-- | sk-api.h | 13 | ||||
-rw-r--r-- | sk-usbhid.c | 238 | ||||
-rw-r--r-- | ssh-sk.c | 116 | ||||
-rw-r--r-- | ssh-sk.h | 11 |
4 files changed, 366 insertions, 12 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sk-api.h,v 1.3 2019/12/30 09:19:52 djm Exp $ */ | 1 | /* $OpenBSD: sk-api.h,v 1.4 2019/12/30 09:21:16 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -52,6 +52,13 @@ struct sk_sign_response { | |||
52 | size_t sig_s_len; | 52 | size_t sig_s_len; |
53 | }; | 53 | }; |
54 | 54 | ||
55 | struct sk_resident_key { | ||
56 | uint8_t alg; | ||
57 | size_t slot; | ||
58 | char *application; | ||
59 | struct sk_enroll_response key; | ||
60 | }; | ||
61 | |||
55 | #define SSH_SK_VERSION_MAJOR 0x00020000 /* current API version */ | 62 | #define SSH_SK_VERSION_MAJOR 0x00020000 /* current API version */ |
56 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 | 63 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 |
57 | 64 | ||
@@ -68,4 +75,8 @@ int sk_sign(int alg, const uint8_t *message, size_t message_len, | |||
68 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | 75 | const char *application, const uint8_t *key_handle, size_t key_handle_len, |
69 | uint8_t flags, struct sk_sign_response **sign_response); | 76 | uint8_t flags, struct sk_sign_response **sign_response); |
70 | 77 | ||
78 | /* Enumerate all resident keys */ | ||
79 | int sk_load_resident_keys(const char *pin, | ||
80 | struct sk_resident_key ***rks, size_t *nrks); | ||
81 | |||
71 | #endif /* _SK_API_H */ | 82 | #endif /* _SK_API_H */ |
diff --git a/sk-usbhid.c b/sk-usbhid.c index 61b52bbb9..fa4424483 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #endif /* WITH_OPENSSL */ | 34 | #endif /* WITH_OPENSSL */ |
35 | 35 | ||
36 | #include <fido.h> | 36 | #include <fido.h> |
37 | #include <fido/credman.h> | ||
37 | 38 | ||
38 | #ifndef SK_STANDALONE | 39 | #ifndef SK_STANDALONE |
39 | #include "log.h" | 40 | #include "log.h" |
@@ -84,11 +85,19 @@ struct sk_sign_response { | |||
84 | size_t sig_s_len; | 85 | size_t sig_s_len; |
85 | }; | 86 | }; |
86 | 87 | ||
88 | struct sk_resident_key { | ||
89 | uint8_t alg; | ||
90 | size_t slot; | ||
91 | char *application; | ||
92 | struct sk_enroll_response key; | ||
93 | }; | ||
94 | |||
87 | /* If building as part of OpenSSH, then rename exported functions */ | 95 | /* If building as part of OpenSSH, then rename exported functions */ |
88 | #if !defined(SK_STANDALONE) | 96 | #if !defined(SK_STANDALONE) |
89 | #define sk_api_version ssh_sk_api_version | 97 | #define sk_api_version ssh_sk_api_version |
90 | #define sk_enroll ssh_sk_enroll | 98 | #define sk_enroll ssh_sk_enroll |
91 | #define sk_sign ssh_sk_sign | 99 | #define sk_sign ssh_sk_sign |
100 | #define sk_load_resident_keys ssh_sk_load_resident_keys | ||
92 | #endif | 101 | #endif |
93 | 102 | ||
94 | /* Return the version of the middleware API */ | 103 | /* Return the version of the middleware API */ |
@@ -104,6 +113,10 @@ int sk_sign(int alg, const uint8_t *message, size_t message_len, | |||
104 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | 113 | const char *application, const uint8_t *key_handle, size_t key_handle_len, |
105 | uint8_t flags, struct sk_sign_response **sign_response); | 114 | uint8_t flags, struct sk_sign_response **sign_response); |
106 | 115 | ||
116 | /* Load resident keys */ | ||
117 | int sk_load_resident_keys(const char *pin, | ||
118 | struct sk_resident_key ***rks, size_t *nrks); | ||
119 | |||
107 | static void skdebug(const char *func, const char *fmt, ...) | 120 | static void skdebug(const char *func, const char *fmt, ...) |
108 | __attribute__((__format__ (printf, 2, 3))); | 121 | __attribute__((__format__ (printf, 2, 3))); |
109 | 122 | ||
@@ -281,7 +294,8 @@ find_device(const uint8_t *message, size_t message_len, const char *application, | |||
281 | * but the API expects a SEC1 octet string. | 294 | * but the API expects a SEC1 octet string. |
282 | */ | 295 | */ |
283 | static int | 296 | static int |
284 | pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response) | 297 | pack_public_key_ecdsa(const fido_cred_t *cred, |
298 | struct sk_enroll_response *response) | ||
285 | { | 299 | { |
286 | const uint8_t *ptr; | 300 | const uint8_t *ptr; |
287 | BIGNUM *x = NULL, *y = NULL; | 301 | BIGNUM *x = NULL, *y = NULL; |
@@ -351,7 +365,8 @@ pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response) | |||
351 | #endif /* WITH_OPENSSL */ | 365 | #endif /* WITH_OPENSSL */ |
352 | 366 | ||
353 | static int | 367 | static int |
354 | pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response) | 368 | pack_public_key_ed25519(const fido_cred_t *cred, |
369 | struct sk_enroll_response *response) | ||
355 | { | 370 | { |
356 | const uint8_t *ptr; | 371 | const uint8_t *ptr; |
357 | size_t len; | 372 | size_t len; |
@@ -382,7 +397,8 @@ pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response) | |||
382 | } | 397 | } |
383 | 398 | ||
384 | static int | 399 | static int |
385 | pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response) | 400 | pack_public_key(int alg, const fido_cred_t *cred, |
401 | struct sk_enroll_response *response) | ||
386 | { | 402 | { |
387 | switch(alg) { | 403 | switch(alg) { |
388 | #ifdef WITH_OPENSSL | 404 | #ifdef WITH_OPENSSL |
@@ -715,4 +731,214 @@ sk_sign(int alg, const uint8_t *message, size_t message_len, | |||
715 | } | 731 | } |
716 | return ret; | 732 | return ret; |
717 | } | 733 | } |
734 | |||
735 | static int | ||
736 | read_rks(const char *devpath, const char *pin, | ||
737 | struct sk_resident_key ***rksp, size_t *nrksp) | ||
738 | { | ||
739 | int r = -1; | ||
740 | fido_dev_t *dev = NULL; | ||
741 | fido_credman_metadata_t *metadata = NULL; | ||
742 | fido_credman_rp_t *rp = NULL; | ||
743 | fido_credman_rk_t *rk = NULL; | ||
744 | size_t i, j, nrp, nrk; | ||
745 | const fido_cred_t *cred; | ||
746 | struct sk_resident_key *srk = NULL, **tmp; | ||
747 | |||
748 | if ((dev = fido_dev_new()) == NULL) { | ||
749 | skdebug(__func__, "fido_dev_new failed"); | ||
750 | return -1; | ||
751 | } | ||
752 | if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) { | ||
753 | skdebug(__func__, "fido_dev_open %s failed: %s", | ||
754 | devpath, fido_strerr(r)); | ||
755 | fido_dev_free(&dev); | ||
756 | return -1; | ||
757 | } | ||
758 | if ((metadata = fido_credman_metadata_new()) == NULL) { | ||
759 | r = -1; | ||
760 | skdebug(__func__, "alloc failed"); | ||
761 | goto out; | ||
762 | } | ||
763 | |||
764 | if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) { | ||
765 | if (r == FIDO_ERR_INVALID_COMMAND) { | ||
766 | skdebug(__func__, "device %s does not support " | ||
767 | "resident keys", devpath); | ||
768 | r = 0; | ||
769 | goto out; | ||
770 | } | ||
771 | skdebug(__func__, "get metadata for %s failed: %s", | ||
772 | devpath, fido_strerr(r)); | ||
773 | goto out; | ||
774 | } | ||
775 | skdebug(__func__, "existing %llu, remaining %llu", | ||
776 | (unsigned long long)fido_credman_rk_existing(metadata), | ||
777 | (unsigned long long)fido_credman_rk_remaining(metadata)); | ||
778 | if ((rp = fido_credman_rp_new()) == NULL) { | ||
779 | r = -1; | ||
780 | skdebug(__func__, "alloc rp failed"); | ||
781 | goto out; | ||
782 | } | ||
783 | if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) { | ||
784 | skdebug(__func__, "get RPs for %s failed: %s", | ||
785 | devpath, fido_strerr(r)); | ||
786 | goto out; | ||
787 | } | ||
788 | nrp = fido_credman_rp_count(rp); | ||
789 | skdebug(__func__, "Device %s has resident keys for %zu RPs", | ||
790 | devpath, nrp); | ||
791 | |||
792 | /* Iterate over RP IDs that have resident keys */ | ||
793 | for (i = 0; i < nrp; i++) { | ||
794 | skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu", | ||
795 | i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i), | ||
796 | fido_credman_rp_id_hash_len(rp, i)); | ||
797 | |||
798 | /* Skip non-SSH RP IDs */ | ||
799 | if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0) | ||
800 | continue; | ||
801 | |||
802 | fido_credman_rk_free(&rk); | ||
803 | if ((rk = fido_credman_rk_new()) == NULL) { | ||
804 | r = -1; | ||
805 | skdebug(__func__, "alloc rk failed"); | ||
806 | goto out; | ||
807 | } | ||
808 | if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i), | ||
809 | rk, pin)) != 0) { | ||
810 | skdebug(__func__, "get RKs for %s slot %zu failed: %s", | ||
811 | devpath, i, fido_strerr(r)); | ||
812 | goto out; | ||
813 | } | ||
814 | nrk = fido_credman_rk_count(rk); | ||
815 | skdebug(__func__, "RP \"%s\" has %zu resident keys", | ||
816 | fido_credman_rp_id(rp, i), nrk); | ||
817 | |||
818 | /* Iterate over resident keys for this RP ID */ | ||
819 | for (j = 0; j < nrk; j++) { | ||
820 | if ((cred = fido_credman_rk(rk, j)) == NULL) { | ||
821 | skdebug(__func__, "no RK in slot %zu", j); | ||
822 | continue; | ||
823 | } | ||
824 | skdebug(__func__, "Device %s RP \"%s\" slot %zu: " | ||
825 | "type %d", devpath, fido_credman_rp_id(rp, i), j, | ||
826 | fido_cred_type(cred)); | ||
827 | |||
828 | /* build response entry */ | ||
829 | if ((srk = calloc(1, sizeof(*srk))) == NULL || | ||
830 | (srk->key.key_handle = calloc(1, | ||
831 | fido_cred_id_len(cred))) == NULL || | ||
832 | (srk->application = strdup(fido_credman_rp_id(rp, | ||
833 | i))) == NULL) { | ||
834 | r = -1; | ||
835 | skdebug(__func__, "alloc sk_resident_key"); | ||
836 | goto out; | ||
837 | } | ||
838 | |||
839 | srk->key.key_handle_len = fido_cred_id_len(cred); | ||
840 | memcpy(srk->key.key_handle, | ||
841 | fido_cred_id_ptr(cred), | ||
842 | srk->key.key_handle_len); | ||
843 | |||
844 | switch (fido_cred_type(cred)) { | ||
845 | case COSE_ES256: | ||
846 | srk->alg = SK_ECDSA; | ||
847 | break; | ||
848 | case COSE_EDDSA: | ||
849 | srk->alg = SK_ED25519; | ||
850 | break; | ||
851 | default: | ||
852 | skdebug(__func__, "unsupported key type %d", | ||
853 | fido_cred_type(cred)); | ||
854 | goto out; | ||
855 | } | ||
856 | |||
857 | if ((r = pack_public_key(srk->alg, cred, | ||
858 | &srk->key)) != 0) { | ||
859 | skdebug(__func__, "pack public key failed"); | ||
860 | goto out; | ||
861 | } | ||
862 | /* append */ | ||
863 | if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1, | ||
864 | sizeof(**rksp))) == NULL) { | ||
865 | r = -1; | ||
866 | skdebug(__func__, "alloc rksp"); | ||
867 | goto out; | ||
868 | } | ||
869 | *rksp = tmp; | ||
870 | (*rksp)[(*nrksp)++] = srk; | ||
871 | srk = NULL; | ||
872 | } | ||
873 | } | ||
874 | /* Success */ | ||
875 | r = 0; | ||
876 | out: | ||
877 | if (srk != NULL) { | ||
878 | free(srk->application); | ||
879 | freezero(srk->key.public_key, srk->key.public_key_len); | ||
880 | freezero(srk->key.key_handle, srk->key.key_handle_len); | ||
881 | freezero(srk, sizeof(*srk)); | ||
882 | } | ||
883 | fido_credman_rp_free(&rp); | ||
884 | fido_credman_rk_free(&rk); | ||
885 | fido_dev_close(dev); | ||
886 | fido_dev_free(&dev); | ||
887 | fido_credman_metadata_free(&metadata); | ||
888 | return r; | ||
889 | } | ||
890 | |||
891 | int | ||
892 | sk_load_resident_keys(const char *pin, | ||
893 | struct sk_resident_key ***rksp, size_t *nrksp) | ||
894 | { | ||
895 | int r = -1; | ||
896 | fido_dev_info_t *devlist = NULL; | ||
897 | size_t i, ndev = 0, nrks = 0; | ||
898 | const fido_dev_info_t *di; | ||
899 | struct sk_resident_key **rks = NULL; | ||
900 | *rksp = NULL; | ||
901 | *nrksp = 0; | ||
902 | |||
903 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | ||
904 | skdebug(__func__, "fido_dev_info_new failed"); | ||
905 | goto out; | ||
906 | } | ||
907 | if ((r = fido_dev_info_manifest(devlist, | ||
908 | MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) { | ||
909 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | ||
910 | fido_strerr(r)); | ||
911 | goto out; | ||
912 | } | ||
913 | for (i = 0; i < ndev; i++) { | ||
914 | if ((di = fido_dev_info_ptr(devlist, i)) == NULL) { | ||
915 | skdebug(__func__, "no dev info at %zu", i); | ||
916 | continue; | ||
917 | } | ||
918 | skdebug(__func__, "trying %s", fido_dev_info_path(di)); | ||
919 | if ((r = read_rks(fido_dev_info_path(di), pin, | ||
920 | &rks, &nrks)) != 0) { | ||
921 | skdebug(__func__, "read_rks failed for %s", | ||
922 | fido_dev_info_path(di)); | ||
923 | continue; | ||
924 | } | ||
925 | } | ||
926 | /* success */ | ||
927 | r = 0; | ||
928 | *rksp = rks; | ||
929 | *nrksp = nrks; | ||
930 | rks = NULL; | ||
931 | nrks = 0; | ||
932 | out: | ||
933 | for (i = 0; i < nrks; i++) { | ||
934 | free(rks[i]->application); | ||
935 | freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); | ||
936 | freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); | ||
937 | freezero(rks[i], sizeof(*rks[i])); | ||
938 | } | ||
939 | free(rks); | ||
940 | fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); | ||
941 | return r; | ||
942 | } | ||
943 | |||
718 | #endif /* ENABLE_SK_INTERNAL */ | 944 | #endif /* ENABLE_SK_INTERNAL */ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk.c,v 1.19 2019/12/30 09:20:36 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk.c,v 1.20 2019/12/30 09:21:16 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -60,6 +60,10 @@ struct sshsk_provider { | |||
60 | const char *application, | 60 | const char *application, |
61 | const uint8_t *key_handle, size_t key_handle_len, | 61 | const uint8_t *key_handle, size_t key_handle_len, |
62 | uint8_t flags, struct sk_sign_response **sign_response); | 62 | uint8_t flags, struct sk_sign_response **sign_response); |
63 | |||
64 | /* Enumerate resident keys */ | ||
65 | int (*sk_load_resident_keys)(const char *pin, | ||
66 | struct sk_resident_key ***rks, size_t *nrks); | ||
63 | }; | 67 | }; |
64 | 68 | ||
65 | /* Built-in version */ | 69 | /* Built-in version */ |
@@ -70,6 +74,8 @@ int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, | |||
70 | const char *application, | 74 | const char *application, |
71 | const uint8_t *key_handle, size_t key_handle_len, | 75 | const uint8_t *key_handle, size_t key_handle_len, |
72 | uint8_t flags, struct sk_sign_response **sign_response); | 76 | uint8_t flags, struct sk_sign_response **sign_response); |
77 | int ssh_sk_load_resident_keys(const char *pin, | ||
78 | struct sk_resident_key ***rks, size_t *nrks); | ||
73 | 79 | ||
74 | static void | 80 | static void |
75 | sshsk_free(struct sshsk_provider *p) | 81 | sshsk_free(struct sshsk_provider *p) |
@@ -101,6 +107,7 @@ sshsk_open(const char *path) | |||
101 | #ifdef ENABLE_SK_INTERNAL | 107 | #ifdef ENABLE_SK_INTERNAL |
102 | ret->sk_enroll = ssh_sk_enroll; | 108 | ret->sk_enroll = ssh_sk_enroll; |
103 | ret->sk_sign = ssh_sk_sign; | 109 | ret->sk_sign = ssh_sk_sign; |
110 | ret->sk_load_resident_keys = ssh_sk_load_resident_keys; | ||
104 | #else | 111 | #else |
105 | error("internal security key support not enabled"); | 112 | error("internal security key support not enabled"); |
106 | #endif | 113 | #endif |
@@ -136,6 +143,12 @@ sshsk_open(const char *path) | |||
136 | path, dlerror()); | 143 | path, dlerror()); |
137 | goto fail; | 144 | goto fail; |
138 | } | 145 | } |
146 | if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle, | ||
147 | "sk_load_resident_keys")) == NULL) { | ||
148 | error("Security key provider %s dlsym(sk_load_resident_keys) " | ||
149 | "failed: %s", path, dlerror()); | ||
150 | goto fail; | ||
151 | } | ||
139 | /* success */ | 152 | /* success */ |
140 | return ret; | 153 | return ret; |
141 | fail: | 154 | fail: |
@@ -264,9 +277,7 @@ sshsk_key_from_response(int alg, const char *application, uint8_t flags, | |||
264 | *keyp = NULL; | 277 | *keyp = NULL; |
265 | 278 | ||
266 | /* Check response validity */ | 279 | /* Check response validity */ |
267 | if (resp->public_key == NULL || resp->key_handle == NULL || | 280 | if (resp->public_key == NULL || resp->key_handle == NULL) { |
268 | resp->signature == NULL || | ||
269 | (resp->attestation_cert == NULL && resp->attestation_cert_len != 0)) { | ||
270 | error("%s: sk_enroll response invalid", __func__); | 281 | error("%s: sk_enroll response invalid", __func__); |
271 | r = SSH_ERR_INVALID_FORMAT; | 282 | r = SSH_ERR_INVALID_FORMAT; |
272 | goto out; | 283 | goto out; |
@@ -595,4 +606,101 @@ sshsk_sign(const char *provider_path, struct sshkey *key, | |||
595 | sshbuf_free(inner_sig); | 606 | sshbuf_free(inner_sig); |
596 | return r; | 607 | return r; |
597 | } | 608 | } |
609 | |||
610 | static void | ||
611 | sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) | ||
612 | { | ||
613 | size_t i; | ||
614 | |||
615 | if (nrks == 0 || rks == NULL) | ||
616 | return; | ||
617 | for (i = 0; i < nrks; i++) { | ||
618 | free(rks[i]->application); | ||
619 | freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); | ||
620 | freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); | ||
621 | freezero(rks[i]->key.signature, rks[i]->key.signature_len); | ||
622 | freezero(rks[i]->key.attestation_cert, | ||
623 | rks[i]->key.attestation_cert_len); | ||
624 | freezero(rks[i], sizeof(**rks)); | ||
625 | } | ||
626 | free(rks); | ||
627 | } | ||
628 | |||
629 | int | ||
630 | sshsk_load_resident(const char *provider_path, const char *pin, | ||
631 | struct sshkey ***keysp, size_t *nkeysp) | ||
632 | { | ||
633 | struct sshsk_provider *skp = NULL; | ||
634 | int r = SSH_ERR_INTERNAL_ERROR; | ||
635 | struct sk_resident_key **rks = NULL; | ||
636 | size_t i, nrks = 0, nkeys = 0; | ||
637 | struct sshkey *key = NULL, **keys = NULL, **tmp; | ||
638 | uint8_t flags; | ||
639 | |||
640 | debug("%s: provider \"%s\"%s", __func__, provider_path, | ||
641 | (pin != NULL && *pin != '\0') ? ", have-pin": ""); | ||
642 | |||
643 | if (keysp == NULL || nkeysp == NULL) | ||
644 | return SSH_ERR_INVALID_ARGUMENT; | ||
645 | *keysp = NULL; | ||
646 | *nkeysp = 0; | ||
647 | |||
648 | if ((skp = sshsk_open(provider_path)) == NULL) { | ||
649 | r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ | ||
650 | goto out; | ||
651 | } | ||
652 | if ((r = skp->sk_load_resident_keys(pin, &rks, &nrks)) != 0) { | ||
653 | error("Security key provider %s returned failure %d", | ||
654 | provider_path, r); | ||
655 | r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */ | ||
656 | goto out; | ||
657 | } | ||
658 | for (i = 0; i < nrks; i++) { | ||
659 | debug3("%s: rk %zu: slot = %zu, alg = %d, application = \"%s\"", | ||
660 | __func__, i, rks[i]->slot, rks[i]->alg, | ||
661 | rks[i]->application); | ||
662 | /* XXX need better filter here */ | ||
663 | if (strncmp(rks[i]->application, "ssh:", 4) != 0) | ||
664 | continue; | ||
665 | switch (rks[i]->alg) { | ||
666 | case SSH_SK_ECDSA: | ||
667 | case SSH_SK_ED25519: | ||
668 | break; | ||
669 | default: | ||
670 | continue; | ||
671 | } | ||
672 | /* XXX where to get flags? */ | ||
673 | flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY; | ||
674 | if ((r = sshsk_key_from_response(rks[i]->alg, | ||
675 | rks[i]->application, flags, &rks[i]->key, &key)) != 0) | ||
676 | goto out; | ||
677 | if ((tmp = recallocarray(keys, nkeys, nkeys + 1, | ||
678 | sizeof(*tmp))) == NULL) { | ||
679 | error("%s: recallocarray failed", __func__); | ||
680 | r = SSH_ERR_ALLOC_FAIL; | ||
681 | goto out; | ||
682 | } | ||
683 | keys = tmp; | ||
684 | keys[nkeys++] = key; | ||
685 | key = NULL; | ||
686 | /* XXX synthesise comment */ | ||
687 | } | ||
688 | /* success */ | ||
689 | *keysp = keys; | ||
690 | *nkeysp = nkeys; | ||
691 | keys = NULL; | ||
692 | nkeys = 0; | ||
693 | r = 0; | ||
694 | out: | ||
695 | sshsk_free(skp); | ||
696 | sshsk_free_sk_resident_keys(rks, nrks); | ||
697 | sshkey_free(key); | ||
698 | if (nkeys != 0) { | ||
699 | for (i = 0; i < nkeys; i++) | ||
700 | sshkey_free(keys[i]); | ||
701 | free(keys); | ||
702 | } | ||
703 | return r; | ||
704 | } | ||
705 | |||
598 | #endif /* ENABLE_SK */ | 706 | #endif /* ENABLE_SK */ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk.h,v 1.6 2019/12/13 19:09:10 djm Exp $ */ | 1 | /* $OpenBSD: ssh-sk.h,v 1.7 2019/12/30 09:21:16 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -45,5 +45,14 @@ int sshsk_sign(const char *provider_path, struct sshkey *key, | |||
45 | u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, | 45 | u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, |
46 | u_int compat); | 46 | u_int compat); |
47 | 47 | ||
48 | /* | ||
49 | * Enumerates and loads all SSH-compatible resident keys from a security | ||
50 | * key. | ||
51 | * | ||
52 | * Returns 0 on success or a ssherr.h error code on failure. | ||
53 | */ | ||
54 | int sshsk_load_resident(const char *provider_path, const char *pin, | ||
55 | struct sshkey ***keysp, size_t *nkeysp); | ||
56 | |||
48 | #endif /* _SSH_SK_H */ | 57 | #endif /* _SSH_SK_H */ |
49 | 58 | ||