xref: /openbsd/usr.bin/ssh/sk-usbhid.c (revision 480af03f)
1094c80e0Sdjm /*
2094c80e0Sdjm  * Copyright (c) 2019 Markus Friedl
3094c80e0Sdjm  *
4094c80e0Sdjm  * Permission to use, copy, modify, and distribute this software for any
5094c80e0Sdjm  * purpose with or without fee is hereby granted, provided that the above
6094c80e0Sdjm  * copyright notice and this permission notice appear in all copies.
7094c80e0Sdjm  *
8094c80e0Sdjm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9094c80e0Sdjm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10094c80e0Sdjm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11094c80e0Sdjm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12094c80e0Sdjm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13094c80e0Sdjm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14094c80e0Sdjm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15094c80e0Sdjm  */
16094c80e0Sdjm 
17094c80e0Sdjm #include <stdint.h>
18094c80e0Sdjm #include <stdlib.h>
19094c80e0Sdjm #include <string.h>
20094c80e0Sdjm #include <stdio.h>
21094c80e0Sdjm #include <stddef.h>
22094c80e0Sdjm #include <stdarg.h>
23094c80e0Sdjm 
24f8cd6cb1Snaddy #ifdef WITH_OPENSSL
25094c80e0Sdjm #include <openssl/opensslv.h>
26094c80e0Sdjm #include <openssl/crypto.h>
27094c80e0Sdjm #include <openssl/bn.h>
28094c80e0Sdjm #include <openssl/ec.h>
29094c80e0Sdjm #include <openssl/ecdsa.h>
30f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
31094c80e0Sdjm 
32094c80e0Sdjm #include <fido.h>
331ac4a90aSdjm #include <fido/credman.h>
34094c80e0Sdjm 
35094c80e0Sdjm #ifndef SK_STANDALONE
36094c80e0Sdjm #include "log.h"
37094c80e0Sdjm #include "xmalloc.h"
38094c80e0Sdjm #endif
39094c80e0Sdjm 
40094c80e0Sdjm /* #define SK_DEBUG 1 */
41094c80e0Sdjm 
42094c80e0Sdjm #define MAX_FIDO_DEVICES	256
43094c80e0Sdjm 
44094c80e0Sdjm /* Compatibility with OpenSSH 1.0.x */
45094c80e0Sdjm #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
46094c80e0Sdjm #define ECDSA_SIG_get0(sig, pr, ps) \
47094c80e0Sdjm 	do { \
48094c80e0Sdjm 		(*pr) = sig->r; \
49094c80e0Sdjm 		(*ps) = sig->s; \
50094c80e0Sdjm 	} while (0)
51094c80e0Sdjm #endif
52094c80e0Sdjm 
532db06755Sdjm #define SK_VERSION_MAJOR	0x00030000 /* current API version */
54094c80e0Sdjm 
55094c80e0Sdjm /* Flags */
56094c80e0Sdjm #define SK_USER_PRESENCE_REQD		0x01
578908bc36Sdjm #define SK_USER_VERIFICATION_REQD	0x04
588908bc36Sdjm #define SK_RESIDENT_KEY			0x20
59094c80e0Sdjm 
60094c80e0Sdjm /* Algs */
61094c80e0Sdjm #define	SK_ECDSA		0x00
62094c80e0Sdjm #define	SK_ED25519		0x01
63094c80e0Sdjm 
64*480af03fSdjm /* Error codes */
65*480af03fSdjm #define SSH_SK_ERR_GENERAL		-1
66*480af03fSdjm #define SSH_SK_ERR_UNSUPPORTED		-2
67*480af03fSdjm #define SSH_SK_ERR_PIN_REQUIRED		-3
68*480af03fSdjm 
69094c80e0Sdjm struct sk_enroll_response {
70094c80e0Sdjm 	uint8_t *public_key;
71094c80e0Sdjm 	size_t public_key_len;
72094c80e0Sdjm 	uint8_t *key_handle;
73094c80e0Sdjm 	size_t key_handle_len;
74094c80e0Sdjm 	uint8_t *signature;
75094c80e0Sdjm 	size_t signature_len;
76094c80e0Sdjm 	uint8_t *attestation_cert;
77094c80e0Sdjm 	size_t attestation_cert_len;
78094c80e0Sdjm };
79094c80e0Sdjm 
80094c80e0Sdjm struct sk_sign_response {
81094c80e0Sdjm 	uint8_t flags;
82094c80e0Sdjm 	uint32_t counter;
83094c80e0Sdjm 	uint8_t *sig_r;
84094c80e0Sdjm 	size_t sig_r_len;
85094c80e0Sdjm 	uint8_t *sig_s;
86094c80e0Sdjm 	size_t sig_s_len;
87094c80e0Sdjm };
88094c80e0Sdjm 
891ac4a90aSdjm struct sk_resident_key {
901ac4a90aSdjm 	uint8_t alg;
911ac4a90aSdjm 	size_t slot;
921ac4a90aSdjm 	char *application;
931ac4a90aSdjm 	struct sk_enroll_response key;
941ac4a90aSdjm };
951ac4a90aSdjm 
96094c80e0Sdjm /* If building as part of OpenSSH, then rename exported functions */
97094c80e0Sdjm #if !defined(SK_STANDALONE)
98094c80e0Sdjm #define sk_api_version		ssh_sk_api_version
99094c80e0Sdjm #define sk_enroll		ssh_sk_enroll
100094c80e0Sdjm #define sk_sign			ssh_sk_sign
1011ac4a90aSdjm #define sk_load_resident_keys	ssh_sk_load_resident_keys
102094c80e0Sdjm #endif
103094c80e0Sdjm 
104094c80e0Sdjm /* Return the version of the middleware API */
105094c80e0Sdjm uint32_t sk_api_version(void);
106094c80e0Sdjm 
107094c80e0Sdjm /* Enroll a U2F key (private key generation) */
108094c80e0Sdjm int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
1092db06755Sdjm     const char *application, uint8_t flags, const char *pin,
110094c80e0Sdjm     struct sk_enroll_response **enroll_response);
111094c80e0Sdjm 
112094c80e0Sdjm /* Sign a challenge */
113094c80e0Sdjm int sk_sign(int alg, const uint8_t *message, size_t message_len,
114094c80e0Sdjm     const char *application, const uint8_t *key_handle, size_t key_handle_len,
1152db06755Sdjm     uint8_t flags, const char *pin, struct sk_sign_response **sign_response);
116094c80e0Sdjm 
1171ac4a90aSdjm /* Load resident keys */
1181ac4a90aSdjm int sk_load_resident_keys(const char *pin,
1191ac4a90aSdjm     struct sk_resident_key ***rks, size_t *nrks);
1201ac4a90aSdjm 
121094c80e0Sdjm static void skdebug(const char *func, const char *fmt, ...)
122094c80e0Sdjm     __attribute__((__format__ (printf, 2, 3)));
123094c80e0Sdjm 
124094c80e0Sdjm static void
125094c80e0Sdjm skdebug(const char *func, const char *fmt, ...)
126094c80e0Sdjm {
127094c80e0Sdjm #if !defined(SK_STANDALONE)
128094c80e0Sdjm 	char *msg;
129094c80e0Sdjm 	va_list ap;
130094c80e0Sdjm 
131094c80e0Sdjm 	va_start(ap, fmt);
132094c80e0Sdjm 	xvasprintf(&msg, fmt, ap);
133094c80e0Sdjm 	va_end(ap);
13459959935Sdjm 	debug("%s: %s", func, msg);
135094c80e0Sdjm 	free(msg);
136094c80e0Sdjm #elif defined(SK_DEBUG)
137094c80e0Sdjm 	va_list ap;
138094c80e0Sdjm 
139094c80e0Sdjm 	va_start(ap, fmt);
140094c80e0Sdjm 	fprintf(stderr, "%s: ", func);
141094c80e0Sdjm 	vfprintf(stderr, fmt, ap);
142094c80e0Sdjm 	fputc('\n', stderr);
143094c80e0Sdjm 	va_end(ap);
144094c80e0Sdjm #else
145094c80e0Sdjm 	(void)func; /* XXX */
146094c80e0Sdjm 	(void)fmt; /* XXX */
147094c80e0Sdjm #endif
148094c80e0Sdjm }
149094c80e0Sdjm 
150094c80e0Sdjm uint32_t
151094c80e0Sdjm sk_api_version(void)
152094c80e0Sdjm {
153094c80e0Sdjm 	return SK_VERSION_MAJOR;
154094c80e0Sdjm }
155094c80e0Sdjm 
156094c80e0Sdjm /* Select the first identified FIDO device attached to the system */
157094c80e0Sdjm static char *
158094c80e0Sdjm pick_first_device(void)
159094c80e0Sdjm {
160094c80e0Sdjm 	char *ret = NULL;
161094c80e0Sdjm 	fido_dev_info_t *devlist = NULL;
162094c80e0Sdjm 	size_t olen = 0;
163094c80e0Sdjm 	int r;
164094c80e0Sdjm 	const fido_dev_info_t *di;
165094c80e0Sdjm 
166094c80e0Sdjm 	if ((devlist = fido_dev_info_new(1)) == NULL) {
167094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_new failed");
168094c80e0Sdjm 		goto out;
169094c80e0Sdjm 	}
170094c80e0Sdjm 	if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) {
171094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
172094c80e0Sdjm 		    fido_strerr(r));
173094c80e0Sdjm 		goto out;
174094c80e0Sdjm 	}
175094c80e0Sdjm 	if (olen != 1) {
176094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen);
177094c80e0Sdjm 		goto out;
178094c80e0Sdjm 	}
179094c80e0Sdjm 	di = fido_dev_info_ptr(devlist, 0);
180094c80e0Sdjm 	if ((ret = strdup(fido_dev_info_path(di))) == NULL) {
181094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_path failed");
182094c80e0Sdjm 		goto out;
183094c80e0Sdjm 	}
184094c80e0Sdjm  out:
185094c80e0Sdjm 	fido_dev_info_free(&devlist, 1);
186094c80e0Sdjm 	return ret;
187094c80e0Sdjm }
188094c80e0Sdjm 
189094c80e0Sdjm /* Check if the specified key handle exists on a given device. */
190094c80e0Sdjm static int
191094c80e0Sdjm try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
192094c80e0Sdjm     const char *application, const uint8_t *key_handle, size_t key_handle_len)
193094c80e0Sdjm {
194094c80e0Sdjm 	fido_assert_t *assert = NULL;
195094c80e0Sdjm 	int r = FIDO_ERR_INTERNAL;
196094c80e0Sdjm 
197094c80e0Sdjm 	if ((assert = fido_assert_new()) == NULL) {
198094c80e0Sdjm 		skdebug(__func__, "fido_assert_new failed");
199094c80e0Sdjm 		goto out;
200094c80e0Sdjm 	}
201094c80e0Sdjm 	if ((r = fido_assert_set_clientdata_hash(assert, message,
202094c80e0Sdjm 	    message_len)) != FIDO_OK) {
203094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
204094c80e0Sdjm 		    fido_strerr(r));
205094c80e0Sdjm 		goto out;
206094c80e0Sdjm 	}
207094c80e0Sdjm 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
208094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
209094c80e0Sdjm 		goto out;
210094c80e0Sdjm 	}
211094c80e0Sdjm 	if ((r = fido_assert_allow_cred(assert, key_handle,
212094c80e0Sdjm 	    key_handle_len)) != FIDO_OK) {
213094c80e0Sdjm 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
214094c80e0Sdjm 		goto out;
215094c80e0Sdjm 	}
216094c80e0Sdjm 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
217094c80e0Sdjm 		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
218094c80e0Sdjm 		goto out;
219094c80e0Sdjm 	}
220094c80e0Sdjm 	r = fido_dev_get_assert(dev, assert, NULL);
221094c80e0Sdjm 	skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
2227cf8e58dSdjm 	if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
2237cf8e58dSdjm 		/* U2F tokens may return this */
2247cf8e58dSdjm 		r = FIDO_OK;
2257cf8e58dSdjm 	}
226094c80e0Sdjm  out:
227094c80e0Sdjm 	fido_assert_free(&assert);
228094c80e0Sdjm 
229094c80e0Sdjm 	return r != FIDO_OK ? -1 : 0;
230094c80e0Sdjm }
231094c80e0Sdjm 
232094c80e0Sdjm /* Iterate over configured devices looking for a specific key handle */
233094c80e0Sdjm static fido_dev_t *
234094c80e0Sdjm find_device(const uint8_t *message, size_t message_len, const char *application,
235094c80e0Sdjm     const uint8_t *key_handle, size_t key_handle_len)
236094c80e0Sdjm {
237094c80e0Sdjm 	fido_dev_info_t *devlist = NULL;
238094c80e0Sdjm 	fido_dev_t *dev = NULL;
2392b479b17Sderaadt 	size_t devlist_len = 0, i;
240094c80e0Sdjm 	const char *path;
241094c80e0Sdjm 	int r;
242094c80e0Sdjm 
243094c80e0Sdjm 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
244094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_new failed");
245094c80e0Sdjm 		goto out;
246094c80e0Sdjm 	}
247094c80e0Sdjm 	if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
248094c80e0Sdjm 	    &devlist_len)) != FIDO_OK) {
249094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r));
250094c80e0Sdjm 		goto out;
251094c80e0Sdjm 	}
252094c80e0Sdjm 
253094c80e0Sdjm 	skdebug(__func__, "found %zu device(s)", devlist_len);
254094c80e0Sdjm 
2552b479b17Sderaadt 	for (i = 0; i < devlist_len; i++) {
256094c80e0Sdjm 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
257094c80e0Sdjm 
258094c80e0Sdjm 		if (di == NULL) {
259094c80e0Sdjm 			skdebug(__func__, "fido_dev_info_ptr %zu failed", i);
260094c80e0Sdjm 			continue;
261094c80e0Sdjm 		}
262094c80e0Sdjm 		if ((path = fido_dev_info_path(di)) == NULL) {
263094c80e0Sdjm 			skdebug(__func__, "fido_dev_info_path %zu failed", i);
264094c80e0Sdjm 			continue;
265094c80e0Sdjm 		}
266094c80e0Sdjm 		skdebug(__func__, "trying device %zu: %s", i, path);
267094c80e0Sdjm 		if ((dev = fido_dev_new()) == NULL) {
268094c80e0Sdjm 			skdebug(__func__, "fido_dev_new failed");
269094c80e0Sdjm 			continue;
270094c80e0Sdjm 		}
271094c80e0Sdjm 		if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
272094c80e0Sdjm 			skdebug(__func__, "fido_dev_open failed");
273094c80e0Sdjm 			fido_dev_free(&dev);
274094c80e0Sdjm 			continue;
275094c80e0Sdjm 		}
276094c80e0Sdjm 		if (try_device(dev, message, message_len, application,
277094c80e0Sdjm 		    key_handle, key_handle_len) == 0) {
278094c80e0Sdjm 			skdebug(__func__, "found key");
279094c80e0Sdjm 			break;
280094c80e0Sdjm 		}
281094c80e0Sdjm 		fido_dev_close(dev);
282094c80e0Sdjm 		fido_dev_free(&dev);
283094c80e0Sdjm 	}
284094c80e0Sdjm 
285094c80e0Sdjm  out:
286094c80e0Sdjm 	if (devlist != NULL)
287094c80e0Sdjm 		fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
288094c80e0Sdjm 
289094c80e0Sdjm 	return dev;
290094c80e0Sdjm }
291094c80e0Sdjm 
292f8cd6cb1Snaddy #ifdef WITH_OPENSSL
293094c80e0Sdjm /*
294094c80e0Sdjm  * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
295094c80e0Sdjm  * but the API expects a SEC1 octet string.
296094c80e0Sdjm  */
297094c80e0Sdjm static int
2981ac4a90aSdjm pack_public_key_ecdsa(const fido_cred_t *cred,
2991ac4a90aSdjm     struct sk_enroll_response *response)
300094c80e0Sdjm {
301094c80e0Sdjm 	const uint8_t *ptr;
302094c80e0Sdjm 	BIGNUM *x = NULL, *y = NULL;
303094c80e0Sdjm 	EC_POINT *q = NULL;
304094c80e0Sdjm 	EC_GROUP *g = NULL;
305094c80e0Sdjm 	int ret = -1;
306094c80e0Sdjm 
307094c80e0Sdjm 	response->public_key = NULL;
308094c80e0Sdjm 	response->public_key_len = 0;
309094c80e0Sdjm 
31007b718edSdjm 	if ((x = BN_new()) == NULL ||
31107b718edSdjm 	    (y = BN_new()) == NULL ||
312094c80e0Sdjm 	    (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
313094c80e0Sdjm 	    (q = EC_POINT_new(g)) == NULL) {
314094c80e0Sdjm 		skdebug(__func__, "libcrypto setup failed");
315094c80e0Sdjm 		goto out;
316094c80e0Sdjm 	}
317094c80e0Sdjm 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
318094c80e0Sdjm 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
319094c80e0Sdjm 		goto out;
320094c80e0Sdjm 	}
321094c80e0Sdjm 	if (fido_cred_pubkey_len(cred) != 64) {
322094c80e0Sdjm 		skdebug(__func__, "bad fido_cred_pubkey_len %zu",
323094c80e0Sdjm 		    fido_cred_pubkey_len(cred));
324094c80e0Sdjm 		goto out;
325094c80e0Sdjm 	}
326094c80e0Sdjm 
327094c80e0Sdjm 	if (BN_bin2bn(ptr, 32, x) == NULL ||
328094c80e0Sdjm 	    BN_bin2bn(ptr + 32, 32, y) == NULL) {
329094c80e0Sdjm 		skdebug(__func__, "BN_bin2bn failed");
330094c80e0Sdjm 		goto out;
331094c80e0Sdjm 	}
33207b718edSdjm 	if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
333094c80e0Sdjm 		skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
334094c80e0Sdjm 		goto out;
335094c80e0Sdjm 	}
336094c80e0Sdjm 	response->public_key_len = EC_POINT_point2oct(g, q,
33707b718edSdjm 	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
338094c80e0Sdjm 	if (response->public_key_len == 0 || response->public_key_len > 2048) {
339094c80e0Sdjm 		skdebug(__func__, "bad pubkey length %zu",
340094c80e0Sdjm 		    response->public_key_len);
341094c80e0Sdjm 		goto out;
342094c80e0Sdjm 	}
343094c80e0Sdjm 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
344094c80e0Sdjm 		skdebug(__func__, "malloc pubkey failed");
345094c80e0Sdjm 		goto out;
346094c80e0Sdjm 	}
347094c80e0Sdjm 	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
34807b718edSdjm 	    response->public_key, response->public_key_len, NULL) == 0) {
349094c80e0Sdjm 		skdebug(__func__, "EC_POINT_point2oct failed");
350094c80e0Sdjm 		goto out;
351094c80e0Sdjm 	}
352094c80e0Sdjm 	/* success */
353094c80e0Sdjm 	ret = 0;
354094c80e0Sdjm  out:
355094c80e0Sdjm 	if (ret != 0 && response->public_key != NULL) {
356094c80e0Sdjm 		memset(response->public_key, 0, response->public_key_len);
357094c80e0Sdjm 		free(response->public_key);
358094c80e0Sdjm 		response->public_key = NULL;
359094c80e0Sdjm 	}
360094c80e0Sdjm 	EC_POINT_free(q);
361094c80e0Sdjm 	EC_GROUP_free(g);
36207b718edSdjm 	BN_clear_free(x);
36307b718edSdjm 	BN_clear_free(y);
364094c80e0Sdjm 	return ret;
365094c80e0Sdjm }
366f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
367094c80e0Sdjm 
368094c80e0Sdjm static int
3691ac4a90aSdjm pack_public_key_ed25519(const fido_cred_t *cred,
3701ac4a90aSdjm     struct sk_enroll_response *response)
371094c80e0Sdjm {
372094c80e0Sdjm 	const uint8_t *ptr;
373094c80e0Sdjm 	size_t len;
374094c80e0Sdjm 	int ret = -1;
375094c80e0Sdjm 
376094c80e0Sdjm 	response->public_key = NULL;
377094c80e0Sdjm 	response->public_key_len = 0;
378094c80e0Sdjm 
379094c80e0Sdjm 	if ((len = fido_cred_pubkey_len(cred)) != 32) {
380094c80e0Sdjm 		skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
381094c80e0Sdjm 		goto out;
382094c80e0Sdjm 	}
383094c80e0Sdjm 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
384094c80e0Sdjm 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
385094c80e0Sdjm 		goto out;
386094c80e0Sdjm 	}
387094c80e0Sdjm 	response->public_key_len = len;
388094c80e0Sdjm 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
389094c80e0Sdjm 		skdebug(__func__, "malloc pubkey failed");
390094c80e0Sdjm 		goto out;
391094c80e0Sdjm 	}
392094c80e0Sdjm 	memcpy(response->public_key, ptr, len);
393094c80e0Sdjm 	ret = 0;
394094c80e0Sdjm  out:
395094c80e0Sdjm 	if (ret != 0)
396094c80e0Sdjm 		free(response->public_key);
397094c80e0Sdjm 	return ret;
398094c80e0Sdjm }
399094c80e0Sdjm 
400094c80e0Sdjm static int
4011ac4a90aSdjm pack_public_key(int alg, const fido_cred_t *cred,
4021ac4a90aSdjm     struct sk_enroll_response *response)
403094c80e0Sdjm {
404094c80e0Sdjm 	switch(alg) {
405f8cd6cb1Snaddy #ifdef WITH_OPENSSL
406094c80e0Sdjm 	case SK_ECDSA:
407094c80e0Sdjm 		return pack_public_key_ecdsa(cred, response);
408f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
409094c80e0Sdjm 	case SK_ED25519:
410094c80e0Sdjm 		return pack_public_key_ed25519(cred, response);
411094c80e0Sdjm 	default:
412094c80e0Sdjm 		return -1;
413094c80e0Sdjm 	}
414094c80e0Sdjm }
415094c80e0Sdjm 
416*480af03fSdjm static int
417*480af03fSdjm fidoerr_to_skerr(int fidoerr)
418*480af03fSdjm {
419*480af03fSdjm 	switch (fidoerr) {
420*480af03fSdjm 	case FIDO_ERR_UNSUPPORTED_OPTION:
421*480af03fSdjm 		return SSH_SK_ERR_UNSUPPORTED;
422*480af03fSdjm 	case FIDO_ERR_PIN_REQUIRED:
423*480af03fSdjm 	case FIDO_ERR_PIN_INVALID:
424*480af03fSdjm 		return SSH_SK_ERR_PIN_REQUIRED;
425*480af03fSdjm 	default:
426*480af03fSdjm 		return -1;
427*480af03fSdjm 	}
428*480af03fSdjm }
429*480af03fSdjm 
430094c80e0Sdjm int
431094c80e0Sdjm sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
4322db06755Sdjm     const char *application, uint8_t flags, const char *pin,
43335f0234cSmarkus     struct sk_enroll_response **enroll_response)
434094c80e0Sdjm {
435094c80e0Sdjm 	fido_cred_t *cred = NULL;
436094c80e0Sdjm 	fido_dev_t *dev = NULL;
437094c80e0Sdjm 	const uint8_t *ptr;
438094c80e0Sdjm 	uint8_t user_id[32];
439094c80e0Sdjm 	struct sk_enroll_response *response = NULL;
440094c80e0Sdjm 	size_t len;
441094c80e0Sdjm 	int cose_alg;
442*480af03fSdjm 	int ret = SSH_SK_ERR_GENERAL;
443094c80e0Sdjm 	int r;
444094c80e0Sdjm 	char *device = NULL;
445094c80e0Sdjm 
446094c80e0Sdjm #ifdef SK_DEBUG
447094c80e0Sdjm 	fido_init(FIDO_DEBUG);
448094c80e0Sdjm #endif
44935f0234cSmarkus 	if (enroll_response == NULL) {
45035f0234cSmarkus 		skdebug(__func__, "enroll_response == NULL");
451094c80e0Sdjm 		goto out;
452094c80e0Sdjm 	}
45335f0234cSmarkus 	*enroll_response = NULL;
454094c80e0Sdjm 	switch(alg) {
455f8cd6cb1Snaddy #ifdef WITH_OPENSSL
456094c80e0Sdjm 	case SK_ECDSA:
457094c80e0Sdjm 		cose_alg = COSE_ES256;
458094c80e0Sdjm 		break;
459f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
460094c80e0Sdjm 	case SK_ED25519:
461094c80e0Sdjm 		cose_alg = COSE_EDDSA;
462094c80e0Sdjm 		break;
463094c80e0Sdjm 	default:
464094c80e0Sdjm 		skdebug(__func__, "unsupported key type %d", alg);
465094c80e0Sdjm 		goto out;
466094c80e0Sdjm 	}
467094c80e0Sdjm 	if ((device = pick_first_device()) == NULL) {
468094c80e0Sdjm 		skdebug(__func__, "pick_first_device failed");
469094c80e0Sdjm 		goto out;
470094c80e0Sdjm 	}
471094c80e0Sdjm 	skdebug(__func__, "using device %s", device);
472094c80e0Sdjm 	if ((cred = fido_cred_new()) == NULL) {
473094c80e0Sdjm 		skdebug(__func__, "fido_cred_new failed");
474094c80e0Sdjm 		goto out;
475094c80e0Sdjm 	}
476094c80e0Sdjm 	memset(user_id, 0, sizeof(user_id));
477094c80e0Sdjm 	if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
478094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
479094c80e0Sdjm 		goto out;
480094c80e0Sdjm 	}
481094c80e0Sdjm 	if ((r = fido_cred_set_clientdata_hash(cred, challenge,
482094c80e0Sdjm 	    challenge_len)) != FIDO_OK) {
483094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
484094c80e0Sdjm 		    fido_strerr(r));
485094c80e0Sdjm 		goto out;
486094c80e0Sdjm 	}
4878908bc36Sdjm 	if ((r = fido_cred_set_rk(cred, (flags & SK_RESIDENT_KEY) != 0 ?
4888908bc36Sdjm 	    FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
4898908bc36Sdjm 		skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
4908908bc36Sdjm 		goto out;
4918908bc36Sdjm 	}
492094c80e0Sdjm 	if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
493094c80e0Sdjm 	    "openssh", "openssh", NULL)) != FIDO_OK) {
494094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
495094c80e0Sdjm 		goto out;
496094c80e0Sdjm 	}
497094c80e0Sdjm 	if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
498094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
499094c80e0Sdjm 		goto out;
500094c80e0Sdjm 	}
501094c80e0Sdjm 	if ((dev = fido_dev_new()) == NULL) {
502094c80e0Sdjm 		skdebug(__func__, "fido_dev_new failed");
503094c80e0Sdjm 		goto out;
504094c80e0Sdjm 	}
505094c80e0Sdjm 	if ((r = fido_dev_open(dev, device)) != FIDO_OK) {
506094c80e0Sdjm 		skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));
507094c80e0Sdjm 		goto out;
508094c80e0Sdjm 	}
509*480af03fSdjm 	if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) {
510094c80e0Sdjm 		skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
511*480af03fSdjm 		ret = fidoerr_to_skerr(r);
512094c80e0Sdjm 		goto out;
513094c80e0Sdjm 	}
514094c80e0Sdjm 	if (fido_cred_x5c_ptr(cred) != NULL) {
515094c80e0Sdjm 		if ((r = fido_cred_verify(cred)) != FIDO_OK) {
516094c80e0Sdjm 			skdebug(__func__, "fido_cred_verify: %s",
517094c80e0Sdjm 			    fido_strerr(r));
518094c80e0Sdjm 			goto out;
519094c80e0Sdjm 		}
520094c80e0Sdjm 	} else {
521094c80e0Sdjm 		skdebug(__func__, "self-attested credential");
522094c80e0Sdjm 		if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
523094c80e0Sdjm 			skdebug(__func__, "fido_cred_verify_self: %s",
524094c80e0Sdjm 			    fido_strerr(r));
525094c80e0Sdjm 			goto out;
526094c80e0Sdjm 		}
527094c80e0Sdjm 	}
528094c80e0Sdjm 	if ((response = calloc(1, sizeof(*response))) == NULL) {
529094c80e0Sdjm 		skdebug(__func__, "calloc response failed");
530094c80e0Sdjm 		goto out;
531094c80e0Sdjm 	}
532094c80e0Sdjm 	if (pack_public_key(alg, cred, response) != 0) {
533094c80e0Sdjm 		skdebug(__func__, "pack_public_key failed");
534094c80e0Sdjm 		goto out;
535094c80e0Sdjm 	}
536094c80e0Sdjm 	if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
537094c80e0Sdjm 		len = fido_cred_id_len(cred);
538094c80e0Sdjm 		if ((response->key_handle = calloc(1, len)) == NULL) {
539094c80e0Sdjm 			skdebug(__func__, "calloc key handle failed");
540094c80e0Sdjm 			goto out;
541094c80e0Sdjm 		}
542094c80e0Sdjm 		memcpy(response->key_handle, ptr, len);
543094c80e0Sdjm 		response->key_handle_len = len;
544094c80e0Sdjm 	}
545094c80e0Sdjm 	if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
546094c80e0Sdjm 		len = fido_cred_sig_len(cred);
547094c80e0Sdjm 		if ((response->signature = calloc(1, len)) == NULL) {
548094c80e0Sdjm 			skdebug(__func__, "calloc signature failed");
549094c80e0Sdjm 			goto out;
550094c80e0Sdjm 		}
551094c80e0Sdjm 		memcpy(response->signature, ptr, len);
552094c80e0Sdjm 		response->signature_len = len;
553094c80e0Sdjm 	}
554094c80e0Sdjm 	if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
555094c80e0Sdjm 		len = fido_cred_x5c_len(cred);
556094c80e0Sdjm 		if ((response->attestation_cert = calloc(1, len)) == NULL) {
557094c80e0Sdjm 			skdebug(__func__, "calloc attestation cert failed");
558094c80e0Sdjm 			goto out;
559094c80e0Sdjm 		}
560094c80e0Sdjm 		memcpy(response->attestation_cert, ptr, len);
561094c80e0Sdjm 		response->attestation_cert_len = len;
562094c80e0Sdjm 	}
56335f0234cSmarkus 	*enroll_response = response;
564094c80e0Sdjm 	response = NULL;
565094c80e0Sdjm 	ret = 0;
566094c80e0Sdjm  out:
567094c80e0Sdjm 	free(device);
568094c80e0Sdjm 	if (response != NULL) {
569094c80e0Sdjm 		free(response->public_key);
570094c80e0Sdjm 		free(response->key_handle);
571094c80e0Sdjm 		free(response->signature);
572094c80e0Sdjm 		free(response->attestation_cert);
573094c80e0Sdjm 		free(response);
574094c80e0Sdjm 	}
575094c80e0Sdjm 	if (dev != NULL) {
576094c80e0Sdjm 		fido_dev_close(dev);
577094c80e0Sdjm 		fido_dev_free(&dev);
578094c80e0Sdjm 	}
579094c80e0Sdjm 	if (cred != NULL) {
580094c80e0Sdjm 		fido_cred_free(&cred);
581094c80e0Sdjm 	}
582094c80e0Sdjm 	return ret;
583094c80e0Sdjm }
584094c80e0Sdjm 
585f8cd6cb1Snaddy #ifdef WITH_OPENSSL
586094c80e0Sdjm static int
587094c80e0Sdjm pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
588094c80e0Sdjm {
589094c80e0Sdjm 	ECDSA_SIG *sig = NULL;
590094c80e0Sdjm 	const BIGNUM *sig_r, *sig_s;
591094c80e0Sdjm 	const unsigned char *cp;
592094c80e0Sdjm 	size_t sig_len;
593094c80e0Sdjm 	int ret = -1;
594094c80e0Sdjm 
595094c80e0Sdjm 	cp = fido_assert_sig_ptr(assert, 0);
596094c80e0Sdjm 	sig_len = fido_assert_sig_len(assert, 0);
597094c80e0Sdjm 	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
598094c80e0Sdjm 		skdebug(__func__, "d2i_ECDSA_SIG failed");
599094c80e0Sdjm 		goto out;
600094c80e0Sdjm 	}
601094c80e0Sdjm 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
602094c80e0Sdjm 	response->sig_r_len = BN_num_bytes(sig_r);
603094c80e0Sdjm 	response->sig_s_len = BN_num_bytes(sig_s);
604094c80e0Sdjm 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
605094c80e0Sdjm 	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
606094c80e0Sdjm 		skdebug(__func__, "calloc signature failed");
607094c80e0Sdjm 		goto out;
608094c80e0Sdjm 	}
609094c80e0Sdjm 	BN_bn2bin(sig_r, response->sig_r);
610094c80e0Sdjm 	BN_bn2bin(sig_s, response->sig_s);
611094c80e0Sdjm 	ret = 0;
612094c80e0Sdjm  out:
613094c80e0Sdjm 	ECDSA_SIG_free(sig);
614094c80e0Sdjm 	if (ret != 0) {
615094c80e0Sdjm 		free(response->sig_r);
616094c80e0Sdjm 		free(response->sig_s);
617094c80e0Sdjm 		response->sig_r = NULL;
618094c80e0Sdjm 		response->sig_s = NULL;
619094c80e0Sdjm 	}
620094c80e0Sdjm 	return ret;
621094c80e0Sdjm }
622f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
623094c80e0Sdjm 
624094c80e0Sdjm static int
625094c80e0Sdjm pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
626094c80e0Sdjm {
627094c80e0Sdjm 	const unsigned char *ptr;
628094c80e0Sdjm 	size_t len;
629094c80e0Sdjm 	int ret = -1;
630094c80e0Sdjm 
631094c80e0Sdjm 	ptr = fido_assert_sig_ptr(assert, 0);
632094c80e0Sdjm 	len = fido_assert_sig_len(assert, 0);
633094c80e0Sdjm 	if (len != 64) {
634094c80e0Sdjm 		skdebug(__func__, "bad length %zu", len);
635094c80e0Sdjm 		goto out;
636094c80e0Sdjm 	}
637094c80e0Sdjm 	response->sig_r_len = len;
638094c80e0Sdjm 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
639094c80e0Sdjm 		skdebug(__func__, "calloc signature failed");
640094c80e0Sdjm 		goto out;
641094c80e0Sdjm 	}
642094c80e0Sdjm 	memcpy(response->sig_r, ptr, len);
643094c80e0Sdjm 	ret = 0;
644094c80e0Sdjm  out:
645094c80e0Sdjm 	if (ret != 0) {
646094c80e0Sdjm 		free(response->sig_r);
647094c80e0Sdjm 		response->sig_r = NULL;
648094c80e0Sdjm 	}
649094c80e0Sdjm 	return ret;
650094c80e0Sdjm }
651094c80e0Sdjm 
652094c80e0Sdjm static int
653094c80e0Sdjm pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response)
654094c80e0Sdjm {
655094c80e0Sdjm 	switch(alg) {
656f8cd6cb1Snaddy #ifdef WITH_OPENSSL
657094c80e0Sdjm 	case SK_ECDSA:
658094c80e0Sdjm 		return pack_sig_ecdsa(assert, response);
659f8cd6cb1Snaddy #endif /* WITH_OPENSSL */
660094c80e0Sdjm 	case SK_ED25519:
661094c80e0Sdjm 		return pack_sig_ed25519(assert, response);
662094c80e0Sdjm 	default:
663094c80e0Sdjm 		return -1;
664094c80e0Sdjm 	}
665094c80e0Sdjm }
666094c80e0Sdjm 
667094c80e0Sdjm int
668094c80e0Sdjm sk_sign(int alg, const uint8_t *message, size_t message_len,
669094c80e0Sdjm     const char *application,
670094c80e0Sdjm     const uint8_t *key_handle, size_t key_handle_len,
6712db06755Sdjm     uint8_t flags, const char *pin, struct sk_sign_response **sign_response)
672094c80e0Sdjm {
673094c80e0Sdjm 	fido_assert_t *assert = NULL;
674094c80e0Sdjm 	fido_dev_t *dev = NULL;
675094c80e0Sdjm 	struct sk_sign_response *response = NULL;
676*480af03fSdjm 	int ret = SSH_SK_ERR_GENERAL;
677094c80e0Sdjm 	int r;
678094c80e0Sdjm 
679094c80e0Sdjm #ifdef SK_DEBUG
680094c80e0Sdjm 	fido_init(FIDO_DEBUG);
681094c80e0Sdjm #endif
682094c80e0Sdjm 
683094c80e0Sdjm 	if (sign_response == NULL) {
684094c80e0Sdjm 		skdebug(__func__, "sign_response == NULL");
685094c80e0Sdjm 		goto out;
686094c80e0Sdjm 	}
687094c80e0Sdjm 	*sign_response = NULL;
688094c80e0Sdjm 	if ((dev = find_device(message, message_len, application, key_handle,
689094c80e0Sdjm 	    key_handle_len)) == NULL) {
690094c80e0Sdjm 		skdebug(__func__, "couldn't find device for key handle");
691094c80e0Sdjm 		goto out;
692094c80e0Sdjm 	}
693094c80e0Sdjm 	if ((assert = fido_assert_new()) == NULL) {
694094c80e0Sdjm 		skdebug(__func__, "fido_assert_new failed");
695094c80e0Sdjm 		goto out;
696094c80e0Sdjm 	}
697094c80e0Sdjm 	if ((r = fido_assert_set_clientdata_hash(assert, message,
698094c80e0Sdjm 	    message_len)) != FIDO_OK) {
699094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
700094c80e0Sdjm 		    fido_strerr(r));
701094c80e0Sdjm 		goto out;
702094c80e0Sdjm 	}
703094c80e0Sdjm 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
704094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
705094c80e0Sdjm 		goto out;
706094c80e0Sdjm 	}
707094c80e0Sdjm 	if ((r = fido_assert_allow_cred(assert, key_handle,
708094c80e0Sdjm 	    key_handle_len)) != FIDO_OK) {
709094c80e0Sdjm 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
710094c80e0Sdjm 		goto out;
711094c80e0Sdjm 	}
712094c80e0Sdjm 	if ((r = fido_assert_set_up(assert,
713094c80e0Sdjm 	    (flags & SK_USER_PRESENCE_REQD) ?
714094c80e0Sdjm 	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
715094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
716094c80e0Sdjm 		goto out;
717094c80e0Sdjm 	}
718094c80e0Sdjm 	if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
719094c80e0Sdjm 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
720094c80e0Sdjm 		goto out;
721094c80e0Sdjm 	}
722094c80e0Sdjm 	if ((response = calloc(1, sizeof(*response))) == NULL) {
723094c80e0Sdjm 		skdebug(__func__, "calloc response failed");
724094c80e0Sdjm 		goto out;
725094c80e0Sdjm 	}
726094c80e0Sdjm 	response->flags = fido_assert_flags(assert, 0);
727094c80e0Sdjm 	response->counter = fido_assert_sigcount(assert, 0);
728094c80e0Sdjm 	if (pack_sig(alg, assert, response) != 0) {
729094c80e0Sdjm 		skdebug(__func__, "pack_sig failed");
730094c80e0Sdjm 		goto out;
731094c80e0Sdjm 	}
732094c80e0Sdjm 	*sign_response = response;
733094c80e0Sdjm 	response = NULL;
734094c80e0Sdjm 	ret = 0;
735094c80e0Sdjm  out:
736094c80e0Sdjm 	if (response != NULL) {
737094c80e0Sdjm 		free(response->sig_r);
738094c80e0Sdjm 		free(response->sig_s);
739094c80e0Sdjm 		free(response);
740094c80e0Sdjm 	}
741094c80e0Sdjm 	if (dev != NULL) {
742094c80e0Sdjm 		fido_dev_close(dev);
743094c80e0Sdjm 		fido_dev_free(&dev);
744094c80e0Sdjm 	}
745094c80e0Sdjm 	if (assert != NULL) {
746094c80e0Sdjm 		fido_assert_free(&assert);
747094c80e0Sdjm 	}
748094c80e0Sdjm 	return ret;
749094c80e0Sdjm }
7501ac4a90aSdjm 
7511ac4a90aSdjm static int
7521ac4a90aSdjm read_rks(const char *devpath, const char *pin,
7531ac4a90aSdjm     struct sk_resident_key ***rksp, size_t *nrksp)
7541ac4a90aSdjm {
755*480af03fSdjm 	int ret = SSH_SK_ERR_GENERAL, r = -1;
7561ac4a90aSdjm 	fido_dev_t *dev = NULL;
7571ac4a90aSdjm 	fido_credman_metadata_t *metadata = NULL;
7581ac4a90aSdjm 	fido_credman_rp_t *rp = NULL;
7591ac4a90aSdjm 	fido_credman_rk_t *rk = NULL;
7601ac4a90aSdjm 	size_t i, j, nrp, nrk;
7611ac4a90aSdjm 	const fido_cred_t *cred;
7621ac4a90aSdjm 	struct sk_resident_key *srk = NULL, **tmp;
7631ac4a90aSdjm 
7641ac4a90aSdjm 	if ((dev = fido_dev_new()) == NULL) {
7651ac4a90aSdjm 		skdebug(__func__, "fido_dev_new failed");
766*480af03fSdjm 		return ret;
7671ac4a90aSdjm 	}
7681ac4a90aSdjm 	if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) {
7691ac4a90aSdjm 		skdebug(__func__, "fido_dev_open %s failed: %s",
7701ac4a90aSdjm 		    devpath, fido_strerr(r));
7711ac4a90aSdjm 		fido_dev_free(&dev);
772*480af03fSdjm 		return ret;
7731ac4a90aSdjm 	}
7741ac4a90aSdjm 	if ((metadata = fido_credman_metadata_new()) == NULL) {
7751ac4a90aSdjm 		skdebug(__func__, "alloc failed");
7761ac4a90aSdjm 		goto out;
7771ac4a90aSdjm 	}
7781ac4a90aSdjm 
7791ac4a90aSdjm 	if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) {
7801ac4a90aSdjm 		if (r == FIDO_ERR_INVALID_COMMAND) {
7811ac4a90aSdjm 			skdebug(__func__, "device %s does not support "
7821ac4a90aSdjm 			    "resident keys", devpath);
783*480af03fSdjm 			ret = 0;
7841ac4a90aSdjm 			goto out;
7851ac4a90aSdjm 		}
7861ac4a90aSdjm 		skdebug(__func__, "get metadata for %s failed: %s",
7871ac4a90aSdjm 		    devpath, fido_strerr(r));
7881ac4a90aSdjm 		goto out;
7891ac4a90aSdjm 	}
7901ac4a90aSdjm 	skdebug(__func__, "existing %llu, remaining %llu",
7911ac4a90aSdjm 	    (unsigned long long)fido_credman_rk_existing(metadata),
7921ac4a90aSdjm 	    (unsigned long long)fido_credman_rk_remaining(metadata));
7931ac4a90aSdjm 	if ((rp = fido_credman_rp_new()) == NULL) {
7941ac4a90aSdjm 		skdebug(__func__, "alloc rp failed");
7951ac4a90aSdjm 		goto out;
7961ac4a90aSdjm 	}
7971ac4a90aSdjm 	if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) {
7981ac4a90aSdjm 		skdebug(__func__, "get RPs for %s failed: %s",
7991ac4a90aSdjm 		    devpath, fido_strerr(r));
8001ac4a90aSdjm 		goto out;
8011ac4a90aSdjm 	}
8021ac4a90aSdjm 	nrp = fido_credman_rp_count(rp);
8031ac4a90aSdjm 	skdebug(__func__, "Device %s has resident keys for %zu RPs",
8041ac4a90aSdjm 	    devpath, nrp);
8051ac4a90aSdjm 
8061ac4a90aSdjm 	/* Iterate over RP IDs that have resident keys */
8071ac4a90aSdjm 	for (i = 0; i < nrp; i++) {
8081ac4a90aSdjm 		skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
8091ac4a90aSdjm 		    i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i),
8101ac4a90aSdjm 		    fido_credman_rp_id_hash_len(rp, i));
8111ac4a90aSdjm 
8121ac4a90aSdjm 		/* Skip non-SSH RP IDs */
8131ac4a90aSdjm 		if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
8141ac4a90aSdjm 			continue;
8151ac4a90aSdjm 
8161ac4a90aSdjm 		fido_credman_rk_free(&rk);
8171ac4a90aSdjm 		if ((rk = fido_credman_rk_new()) == NULL) {
8181ac4a90aSdjm 			skdebug(__func__, "alloc rk failed");
8191ac4a90aSdjm 			goto out;
8201ac4a90aSdjm 		}
8211ac4a90aSdjm 		if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i),
8221ac4a90aSdjm 		    rk, pin)) != 0) {
8231ac4a90aSdjm 			skdebug(__func__, "get RKs for %s slot %zu failed: %s",
8241ac4a90aSdjm 			    devpath, i, fido_strerr(r));
8251ac4a90aSdjm 			goto out;
8261ac4a90aSdjm 		}
8271ac4a90aSdjm 		nrk = fido_credman_rk_count(rk);
8281ac4a90aSdjm 		skdebug(__func__, "RP \"%s\" has %zu resident keys",
8291ac4a90aSdjm 		    fido_credman_rp_id(rp, i), nrk);
8301ac4a90aSdjm 
8311ac4a90aSdjm 		/* Iterate over resident keys for this RP ID */
8321ac4a90aSdjm 		for (j = 0; j < nrk; j++) {
8331ac4a90aSdjm 			if ((cred = fido_credman_rk(rk, j)) == NULL) {
8341ac4a90aSdjm 				skdebug(__func__, "no RK in slot %zu", j);
8351ac4a90aSdjm 				continue;
8361ac4a90aSdjm 			}
8371ac4a90aSdjm 			skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
8381ac4a90aSdjm 			    "type %d", devpath, fido_credman_rp_id(rp, i), j,
8391ac4a90aSdjm 			    fido_cred_type(cred));
8401ac4a90aSdjm 
8411ac4a90aSdjm 			/* build response entry */
8421ac4a90aSdjm 			if ((srk = calloc(1, sizeof(*srk))) == NULL ||
8431ac4a90aSdjm 			    (srk->key.key_handle = calloc(1,
8441ac4a90aSdjm 			    fido_cred_id_len(cred))) == NULL ||
8451ac4a90aSdjm 			    (srk->application = strdup(fido_credman_rp_id(rp,
8461ac4a90aSdjm 			    i))) == NULL) {
8471ac4a90aSdjm 				skdebug(__func__, "alloc sk_resident_key");
8481ac4a90aSdjm 				goto out;
8491ac4a90aSdjm 			}
8501ac4a90aSdjm 
8511ac4a90aSdjm 			srk->key.key_handle_len = fido_cred_id_len(cred);
8521ac4a90aSdjm 			memcpy(srk->key.key_handle,
8531ac4a90aSdjm 			    fido_cred_id_ptr(cred),
8541ac4a90aSdjm 			    srk->key.key_handle_len);
8551ac4a90aSdjm 
8561ac4a90aSdjm 			switch (fido_cred_type(cred)) {
8571ac4a90aSdjm 			case COSE_ES256:
8581ac4a90aSdjm 				srk->alg = SK_ECDSA;
8591ac4a90aSdjm 				break;
8601ac4a90aSdjm 			case COSE_EDDSA:
8611ac4a90aSdjm 				srk->alg = SK_ED25519;
8621ac4a90aSdjm 				break;
8631ac4a90aSdjm 			default:
8641ac4a90aSdjm 				skdebug(__func__, "unsupported key type %d",
8651ac4a90aSdjm 				    fido_cred_type(cred));
8661ac4a90aSdjm 				goto out;
8671ac4a90aSdjm 			}
8681ac4a90aSdjm 
8691ac4a90aSdjm 			if ((r = pack_public_key(srk->alg, cred,
8701ac4a90aSdjm 			    &srk->key)) != 0) {
8711ac4a90aSdjm 				skdebug(__func__, "pack public key failed");
8721ac4a90aSdjm 				goto out;
8731ac4a90aSdjm 			}
8741ac4a90aSdjm 			/* append */
8751ac4a90aSdjm 			if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
8761ac4a90aSdjm 			    sizeof(**rksp))) == NULL) {
8771ac4a90aSdjm 				skdebug(__func__, "alloc rksp");
8781ac4a90aSdjm 				goto out;
8791ac4a90aSdjm 			}
8801ac4a90aSdjm 			*rksp = tmp;
8811ac4a90aSdjm 			(*rksp)[(*nrksp)++] = srk;
8821ac4a90aSdjm 			srk = NULL;
8831ac4a90aSdjm 		}
8841ac4a90aSdjm 	}
8851ac4a90aSdjm 	/* Success */
886*480af03fSdjm 	ret = 0;
8871ac4a90aSdjm  out:
8881ac4a90aSdjm 	if (srk != NULL) {
8891ac4a90aSdjm 		free(srk->application);
8901ac4a90aSdjm 		freezero(srk->key.public_key, srk->key.public_key_len);
8911ac4a90aSdjm 		freezero(srk->key.key_handle, srk->key.key_handle_len);
8921ac4a90aSdjm 		freezero(srk, sizeof(*srk));
8931ac4a90aSdjm 	}
8941ac4a90aSdjm 	fido_credman_rp_free(&rp);
8951ac4a90aSdjm 	fido_credman_rk_free(&rk);
8961ac4a90aSdjm 	fido_dev_close(dev);
8971ac4a90aSdjm 	fido_dev_free(&dev);
8981ac4a90aSdjm 	fido_credman_metadata_free(&metadata);
899*480af03fSdjm 	return ret;
9001ac4a90aSdjm }
9011ac4a90aSdjm 
9021ac4a90aSdjm int
9031ac4a90aSdjm sk_load_resident_keys(const char *pin,
9041ac4a90aSdjm     struct sk_resident_key ***rksp, size_t *nrksp)
9051ac4a90aSdjm {
906*480af03fSdjm 	int ret = SSH_SK_ERR_GENERAL, r = -1;
9071ac4a90aSdjm 	fido_dev_info_t *devlist = NULL;
9081ac4a90aSdjm 	size_t i, ndev = 0, nrks = 0;
9091ac4a90aSdjm 	const fido_dev_info_t *di;
9101ac4a90aSdjm 	struct sk_resident_key **rks = NULL;
9111ac4a90aSdjm 	*rksp = NULL;
9121ac4a90aSdjm 	*nrksp = 0;
9131ac4a90aSdjm 
9141ac4a90aSdjm 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
9151ac4a90aSdjm 		skdebug(__func__, "fido_dev_info_new failed");
9161ac4a90aSdjm 		goto out;
9171ac4a90aSdjm 	}
9181ac4a90aSdjm 	if ((r = fido_dev_info_manifest(devlist,
9191ac4a90aSdjm 	    MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) {
9201ac4a90aSdjm 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
9211ac4a90aSdjm 		    fido_strerr(r));
9221ac4a90aSdjm 		goto out;
9231ac4a90aSdjm 	}
9241ac4a90aSdjm 	for (i = 0; i < ndev; i++) {
9251ac4a90aSdjm 		if ((di = fido_dev_info_ptr(devlist, i)) == NULL) {
9261ac4a90aSdjm 			skdebug(__func__, "no dev info at %zu", i);
9271ac4a90aSdjm 			continue;
9281ac4a90aSdjm 		}
9291ac4a90aSdjm 		skdebug(__func__, "trying %s", fido_dev_info_path(di));
9301ac4a90aSdjm 		if ((r = read_rks(fido_dev_info_path(di), pin,
9311ac4a90aSdjm 		    &rks, &nrks)) != 0) {
9321ac4a90aSdjm 			skdebug(__func__, "read_rks failed for %s",
9331ac4a90aSdjm 			    fido_dev_info_path(di));
9341ac4a90aSdjm 			continue;
9351ac4a90aSdjm 		}
9361ac4a90aSdjm 	}
9371ac4a90aSdjm 	/* success */
938*480af03fSdjm 	ret = 0;
9391ac4a90aSdjm 	*rksp = rks;
9401ac4a90aSdjm 	*nrksp = nrks;
9411ac4a90aSdjm 	rks = NULL;
9421ac4a90aSdjm 	nrks = 0;
9431ac4a90aSdjm  out:
9441ac4a90aSdjm 	for (i = 0; i < nrks; i++) {
9451ac4a90aSdjm 		free(rks[i]->application);
9461ac4a90aSdjm 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
9471ac4a90aSdjm 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
9481ac4a90aSdjm 		freezero(rks[i], sizeof(*rks[i]));
9491ac4a90aSdjm 	}
9501ac4a90aSdjm 	free(rks);
9511ac4a90aSdjm 	fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
952*480af03fSdjm 	return ret;
9531ac4a90aSdjm }
9541ac4a90aSdjm 
955