summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sk-api.h13
-rw-r--r--sk-usbhid.c238
-rw-r--r--ssh-sk.c116
-rw-r--r--ssh-sk.h11
4 files changed, 366 insertions, 12 deletions
diff --git a/sk-api.h b/sk-api.h
index 5947e0ed7..10f1fdb10 100644
--- a/sk-api.h
+++ b/sk-api.h
@@ -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
55struct 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 */
79int 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
88struct 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 */
117int sk_load_resident_keys(const char *pin,
118 struct sk_resident_key ***rks, size_t *nrks);
119
107static void skdebug(const char *func, const char *fmt, ...) 120static 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 */
283static int 296static int
284pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response) 297pack_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
353static int 367static int
354pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response) 368pack_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
384static int 399static int
385pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response) 400pack_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
735static int
736read_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
891int
892sk_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 */
diff --git a/ssh-sk.c b/ssh-sk.c
index 01927669e..d48f34e30 100644
--- a/ssh-sk.c
+++ b/ssh-sk.c
@@ -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);
77int ssh_sk_load_resident_keys(const char *pin,
78 struct sk_resident_key ***rks, size_t *nrks);
73 79
74static void 80static void
75sshsk_free(struct sshsk_provider *p) 81sshsk_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;
141fail: 154fail:
@@ -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
610static void
611sshsk_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
629int
630sshsk_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 */
diff --git a/ssh-sk.h b/ssh-sk.h
index 4d667884e..1afe839db 100644
--- a/ssh-sk.h
+++ b/ssh-sk.h
@@ -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 */
54int 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