xref: /openbsd/usr.bin/ssh/sk-usbhid.c (revision 7cf8e58d)
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 
24094c80e0Sdjm #include <openssl/opensslv.h>
25094c80e0Sdjm #include <openssl/crypto.h>
26094c80e0Sdjm #include <openssl/bn.h>
27094c80e0Sdjm #include <openssl/ec.h>
28094c80e0Sdjm #include <openssl/ecdsa.h>
29094c80e0Sdjm 
30094c80e0Sdjm #include <fido.h>
31094c80e0Sdjm 
32094c80e0Sdjm #ifndef SK_STANDALONE
33094c80e0Sdjm #include "log.h"
34094c80e0Sdjm #include "xmalloc.h"
35094c80e0Sdjm #endif
36094c80e0Sdjm 
37094c80e0Sdjm /* #define SK_DEBUG 1 */
38094c80e0Sdjm 
39094c80e0Sdjm #define MAX_FIDO_DEVICES	256
40094c80e0Sdjm 
41094c80e0Sdjm /* Compatibility with OpenSSH 1.0.x */
42094c80e0Sdjm #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
43094c80e0Sdjm #define ECDSA_SIG_get0(sig, pr, ps) \
44094c80e0Sdjm 	do { \
45094c80e0Sdjm 		(*pr) = sig->r; \
46094c80e0Sdjm 		(*ps) = sig->s; \
47094c80e0Sdjm 	} while (0)
48094c80e0Sdjm #endif
49094c80e0Sdjm 
50094c80e0Sdjm #define SK_VERSION_MAJOR	0x00020000 /* current API version */
51094c80e0Sdjm 
52094c80e0Sdjm /* Flags */
53094c80e0Sdjm #define SK_USER_PRESENCE_REQD	0x01
54094c80e0Sdjm 
55094c80e0Sdjm /* Algs */
56094c80e0Sdjm #define	SK_ECDSA		0x00
57094c80e0Sdjm #define	SK_ED25519		0x01
58094c80e0Sdjm 
59094c80e0Sdjm struct sk_enroll_response {
60094c80e0Sdjm 	uint8_t *public_key;
61094c80e0Sdjm 	size_t public_key_len;
62094c80e0Sdjm 	uint8_t *key_handle;
63094c80e0Sdjm 	size_t key_handle_len;
64094c80e0Sdjm 	uint8_t *signature;
65094c80e0Sdjm 	size_t signature_len;
66094c80e0Sdjm 	uint8_t *attestation_cert;
67094c80e0Sdjm 	size_t attestation_cert_len;
68094c80e0Sdjm };
69094c80e0Sdjm 
70094c80e0Sdjm struct sk_sign_response {
71094c80e0Sdjm 	uint8_t flags;
72094c80e0Sdjm 	uint32_t counter;
73094c80e0Sdjm 	uint8_t *sig_r;
74094c80e0Sdjm 	size_t sig_r_len;
75094c80e0Sdjm 	uint8_t *sig_s;
76094c80e0Sdjm 	size_t sig_s_len;
77094c80e0Sdjm };
78094c80e0Sdjm 
79094c80e0Sdjm /* If building as part of OpenSSH, then rename exported functions */
80094c80e0Sdjm #if !defined(SK_STANDALONE)
81094c80e0Sdjm #define sk_api_version	ssh_sk_api_version
82094c80e0Sdjm #define sk_enroll	ssh_sk_enroll
83094c80e0Sdjm #define sk_sign		ssh_sk_sign
84094c80e0Sdjm #endif
85094c80e0Sdjm 
86094c80e0Sdjm /* Return the version of the middleware API */
87094c80e0Sdjm uint32_t sk_api_version(void);
88094c80e0Sdjm 
89094c80e0Sdjm /* Enroll a U2F key (private key generation) */
90094c80e0Sdjm int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
91094c80e0Sdjm     const char *application, uint8_t flags,
92094c80e0Sdjm     struct sk_enroll_response **enroll_response);
93094c80e0Sdjm 
94094c80e0Sdjm /* Sign a challenge */
95094c80e0Sdjm int sk_sign(int alg, const uint8_t *message, size_t message_len,
96094c80e0Sdjm     const char *application, const uint8_t *key_handle, size_t key_handle_len,
97094c80e0Sdjm     uint8_t flags, struct sk_sign_response **sign_response);
98094c80e0Sdjm 
99094c80e0Sdjm static void skdebug(const char *func, const char *fmt, ...)
100094c80e0Sdjm     __attribute__((__format__ (printf, 2, 3)));
101094c80e0Sdjm 
102094c80e0Sdjm static void
103094c80e0Sdjm skdebug(const char *func, const char *fmt, ...)
104094c80e0Sdjm {
105094c80e0Sdjm #if !defined(SK_STANDALONE)
106094c80e0Sdjm 	char *msg;
107094c80e0Sdjm 	va_list ap;
108094c80e0Sdjm 
109094c80e0Sdjm 	va_start(ap, fmt);
110094c80e0Sdjm 	xvasprintf(&msg, fmt, ap);
111094c80e0Sdjm 	va_end(ap);
11259959935Sdjm 	debug("%s: %s", func, msg);
113094c80e0Sdjm 	free(msg);
114094c80e0Sdjm #elif defined(SK_DEBUG)
115094c80e0Sdjm 	va_list ap;
116094c80e0Sdjm 
117094c80e0Sdjm 	va_start(ap, fmt);
118094c80e0Sdjm 	fprintf(stderr, "%s: ", func);
119094c80e0Sdjm 	vfprintf(stderr, fmt, ap);
120094c80e0Sdjm 	fputc('\n', stderr);
121094c80e0Sdjm 	va_end(ap);
122094c80e0Sdjm #else
123094c80e0Sdjm 	(void)func; /* XXX */
124094c80e0Sdjm 	(void)fmt; /* XXX */
125094c80e0Sdjm #endif
126094c80e0Sdjm }
127094c80e0Sdjm 
128094c80e0Sdjm uint32_t
129094c80e0Sdjm sk_api_version(void)
130094c80e0Sdjm {
131094c80e0Sdjm 	return SK_VERSION_MAJOR;
132094c80e0Sdjm }
133094c80e0Sdjm 
134094c80e0Sdjm /* Select the first identified FIDO device attached to the system */
135094c80e0Sdjm static char *
136094c80e0Sdjm pick_first_device(void)
137094c80e0Sdjm {
138094c80e0Sdjm 	char *ret = NULL;
139094c80e0Sdjm 	fido_dev_info_t *devlist = NULL;
140094c80e0Sdjm 	size_t olen = 0;
141094c80e0Sdjm 	int r;
142094c80e0Sdjm 	const fido_dev_info_t *di;
143094c80e0Sdjm 
144094c80e0Sdjm 	if ((devlist = fido_dev_info_new(1)) == NULL) {
145094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_new failed");
146094c80e0Sdjm 		goto out;
147094c80e0Sdjm 	}
148094c80e0Sdjm 	if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) {
149094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
150094c80e0Sdjm 		    fido_strerr(r));
151094c80e0Sdjm 		goto out;
152094c80e0Sdjm 	}
153094c80e0Sdjm 	if (olen != 1) {
154094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen);
155094c80e0Sdjm 		goto out;
156094c80e0Sdjm 	}
157094c80e0Sdjm 	di = fido_dev_info_ptr(devlist, 0);
158094c80e0Sdjm 	if ((ret = strdup(fido_dev_info_path(di))) == NULL) {
159094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_path failed");
160094c80e0Sdjm 		goto out;
161094c80e0Sdjm 	}
162094c80e0Sdjm  out:
163094c80e0Sdjm 	fido_dev_info_free(&devlist, 1);
164094c80e0Sdjm 	return ret;
165094c80e0Sdjm }
166094c80e0Sdjm 
167094c80e0Sdjm /* Check if the specified key handle exists on a given device. */
168094c80e0Sdjm static int
169094c80e0Sdjm try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
170094c80e0Sdjm     const char *application, const uint8_t *key_handle, size_t key_handle_len)
171094c80e0Sdjm {
172094c80e0Sdjm 	fido_assert_t *assert = NULL;
173094c80e0Sdjm 	int r = FIDO_ERR_INTERNAL;
174094c80e0Sdjm 
175094c80e0Sdjm 	if ((assert = fido_assert_new()) == NULL) {
176094c80e0Sdjm 		skdebug(__func__, "fido_assert_new failed");
177094c80e0Sdjm 		goto out;
178094c80e0Sdjm 	}
179094c80e0Sdjm 	if ((r = fido_assert_set_clientdata_hash(assert, message,
180094c80e0Sdjm 	    message_len)) != FIDO_OK) {
181094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
182094c80e0Sdjm 		    fido_strerr(r));
183094c80e0Sdjm 		goto out;
184094c80e0Sdjm 	}
185094c80e0Sdjm 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
186094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
187094c80e0Sdjm 		goto out;
188094c80e0Sdjm 	}
189094c80e0Sdjm 	if ((r = fido_assert_allow_cred(assert, key_handle,
190094c80e0Sdjm 	    key_handle_len)) != FIDO_OK) {
191094c80e0Sdjm 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
192094c80e0Sdjm 		goto out;
193094c80e0Sdjm 	}
194094c80e0Sdjm 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
195094c80e0Sdjm 		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
196094c80e0Sdjm 		goto out;
197094c80e0Sdjm 	}
198094c80e0Sdjm 	r = fido_dev_get_assert(dev, assert, NULL);
199094c80e0Sdjm 	skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
200*7cf8e58dSdjm 	if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
201*7cf8e58dSdjm 		/* U2F tokens may return this */
202*7cf8e58dSdjm 		r = FIDO_OK;
203*7cf8e58dSdjm 	}
204094c80e0Sdjm  out:
205094c80e0Sdjm 	fido_assert_free(&assert);
206094c80e0Sdjm 
207094c80e0Sdjm 	return r != FIDO_OK ? -1 : 0;
208094c80e0Sdjm }
209094c80e0Sdjm 
210094c80e0Sdjm /* Iterate over configured devices looking for a specific key handle */
211094c80e0Sdjm static fido_dev_t *
212094c80e0Sdjm find_device(const uint8_t *message, size_t message_len, const char *application,
213094c80e0Sdjm     const uint8_t *key_handle, size_t key_handle_len)
214094c80e0Sdjm {
215094c80e0Sdjm 	fido_dev_info_t *devlist = NULL;
216094c80e0Sdjm 	fido_dev_t *dev = NULL;
217094c80e0Sdjm 	size_t devlist_len = 0;
218094c80e0Sdjm 	const char *path;
219094c80e0Sdjm 	int r;
220094c80e0Sdjm 
221094c80e0Sdjm 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
222094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_new failed");
223094c80e0Sdjm 		goto out;
224094c80e0Sdjm 	}
225094c80e0Sdjm 	if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
226094c80e0Sdjm 	    &devlist_len)) != FIDO_OK) {
227094c80e0Sdjm 		skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r));
228094c80e0Sdjm 		goto out;
229094c80e0Sdjm 	}
230094c80e0Sdjm 
231094c80e0Sdjm 	skdebug(__func__, "found %zu device(s)", devlist_len);
232094c80e0Sdjm 
233094c80e0Sdjm 	for (size_t i = 0; i < devlist_len; i++) {
234094c80e0Sdjm 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
235094c80e0Sdjm 
236094c80e0Sdjm 		if (di == NULL) {
237094c80e0Sdjm 			skdebug(__func__, "fido_dev_info_ptr %zu failed", i);
238094c80e0Sdjm 			continue;
239094c80e0Sdjm 		}
240094c80e0Sdjm 		if ((path = fido_dev_info_path(di)) == NULL) {
241094c80e0Sdjm 			skdebug(__func__, "fido_dev_info_path %zu failed", i);
242094c80e0Sdjm 			continue;
243094c80e0Sdjm 		}
244094c80e0Sdjm 		skdebug(__func__, "trying device %zu: %s", i, path);
245094c80e0Sdjm 		if ((dev = fido_dev_new()) == NULL) {
246094c80e0Sdjm 			skdebug(__func__, "fido_dev_new failed");
247094c80e0Sdjm 			continue;
248094c80e0Sdjm 		}
249094c80e0Sdjm 		if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
250094c80e0Sdjm 			skdebug(__func__, "fido_dev_open failed");
251094c80e0Sdjm 			fido_dev_free(&dev);
252094c80e0Sdjm 			continue;
253094c80e0Sdjm 		}
254094c80e0Sdjm 		if (try_device(dev, message, message_len, application,
255094c80e0Sdjm 		    key_handle, key_handle_len) == 0) {
256094c80e0Sdjm 			skdebug(__func__, "found key");
257094c80e0Sdjm 			break;
258094c80e0Sdjm 		}
259094c80e0Sdjm 		fido_dev_close(dev);
260094c80e0Sdjm 		fido_dev_free(&dev);
261094c80e0Sdjm 	}
262094c80e0Sdjm 
263094c80e0Sdjm  out:
264094c80e0Sdjm 	if (devlist != NULL)
265094c80e0Sdjm 		fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
266094c80e0Sdjm 
267094c80e0Sdjm 	return dev;
268094c80e0Sdjm }
269094c80e0Sdjm 
270094c80e0Sdjm /*
271094c80e0Sdjm  * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
272094c80e0Sdjm  * but the API expects a SEC1 octet string.
273094c80e0Sdjm  */
274094c80e0Sdjm static int
275094c80e0Sdjm pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response)
276094c80e0Sdjm {
277094c80e0Sdjm 	const uint8_t *ptr;
278094c80e0Sdjm 	BIGNUM *x = NULL, *y = NULL;
279094c80e0Sdjm 	EC_POINT *q = NULL;
280094c80e0Sdjm 	EC_GROUP *g = NULL;
281094c80e0Sdjm 	BN_CTX *bn_ctx = NULL;
282094c80e0Sdjm 	int ret = -1;
283094c80e0Sdjm 
284094c80e0Sdjm 	response->public_key = NULL;
285094c80e0Sdjm 	response->public_key_len = 0;
286094c80e0Sdjm 
287094c80e0Sdjm 	if ((bn_ctx = BN_CTX_new()) == NULL ||
288094c80e0Sdjm 	    (x = BN_CTX_get(bn_ctx)) == NULL ||
289094c80e0Sdjm 	    (y = BN_CTX_get(bn_ctx)) == NULL ||
290094c80e0Sdjm 	    (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
291094c80e0Sdjm 	    (q = EC_POINT_new(g)) == NULL) {
292094c80e0Sdjm 		skdebug(__func__, "libcrypto setup failed");
293094c80e0Sdjm 		goto out;
294094c80e0Sdjm 	}
295094c80e0Sdjm 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
296094c80e0Sdjm 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
297094c80e0Sdjm 		goto out;
298094c80e0Sdjm 	}
299094c80e0Sdjm 	if (fido_cred_pubkey_len(cred) != 64) {
300094c80e0Sdjm 		skdebug(__func__, "bad fido_cred_pubkey_len %zu",
301094c80e0Sdjm 		    fido_cred_pubkey_len(cred));
302094c80e0Sdjm 		goto out;
303094c80e0Sdjm 	}
304094c80e0Sdjm 
305094c80e0Sdjm 	if (BN_bin2bn(ptr, 32, x) == NULL ||
306094c80e0Sdjm 	    BN_bin2bn(ptr + 32, 32, y) == NULL) {
307094c80e0Sdjm 		skdebug(__func__, "BN_bin2bn failed");
308094c80e0Sdjm 		goto out;
309094c80e0Sdjm 	}
310094c80e0Sdjm 	if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bn_ctx) != 1) {
311094c80e0Sdjm 		skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
312094c80e0Sdjm 		goto out;
313094c80e0Sdjm 	}
314094c80e0Sdjm 	response->public_key_len = EC_POINT_point2oct(g, q,
315094c80e0Sdjm 	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx);
316094c80e0Sdjm 	if (response->public_key_len == 0 || response->public_key_len > 2048) {
317094c80e0Sdjm 		skdebug(__func__, "bad pubkey length %zu",
318094c80e0Sdjm 		    response->public_key_len);
319094c80e0Sdjm 		goto out;
320094c80e0Sdjm 	}
321094c80e0Sdjm 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
322094c80e0Sdjm 		skdebug(__func__, "malloc pubkey failed");
323094c80e0Sdjm 		goto out;
324094c80e0Sdjm 	}
325094c80e0Sdjm 	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
326094c80e0Sdjm 	    response->public_key, response->public_key_len, bn_ctx) == 0) {
327094c80e0Sdjm 		skdebug(__func__, "EC_POINT_point2oct failed");
328094c80e0Sdjm 		goto out;
329094c80e0Sdjm 	}
330094c80e0Sdjm 	/* success */
331094c80e0Sdjm 	ret = 0;
332094c80e0Sdjm  out:
333094c80e0Sdjm 	if (ret != 0 && response->public_key != NULL) {
334094c80e0Sdjm 		memset(response->public_key, 0, response->public_key_len);
335094c80e0Sdjm 		free(response->public_key);
336094c80e0Sdjm 		response->public_key = NULL;
337094c80e0Sdjm 	}
338094c80e0Sdjm 	EC_POINT_free(q);
339094c80e0Sdjm 	EC_GROUP_free(g);
340094c80e0Sdjm 	BN_CTX_free(bn_ctx);
341094c80e0Sdjm 	return ret;
342094c80e0Sdjm }
343094c80e0Sdjm 
344094c80e0Sdjm static int
345094c80e0Sdjm pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response)
346094c80e0Sdjm {
347094c80e0Sdjm 	const uint8_t *ptr;
348094c80e0Sdjm 	size_t len;
349094c80e0Sdjm 	int ret = -1;
350094c80e0Sdjm 
351094c80e0Sdjm 	response->public_key = NULL;
352094c80e0Sdjm 	response->public_key_len = 0;
353094c80e0Sdjm 
354094c80e0Sdjm 	if ((len = fido_cred_pubkey_len(cred)) != 32) {
355094c80e0Sdjm 		skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
356094c80e0Sdjm 		goto out;
357094c80e0Sdjm 	}
358094c80e0Sdjm 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
359094c80e0Sdjm 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
360094c80e0Sdjm 		goto out;
361094c80e0Sdjm 	}
362094c80e0Sdjm 	response->public_key_len = len;
363094c80e0Sdjm 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
364094c80e0Sdjm 		skdebug(__func__, "malloc pubkey failed");
365094c80e0Sdjm 		goto out;
366094c80e0Sdjm 	}
367094c80e0Sdjm 	memcpy(response->public_key, ptr, len);
368094c80e0Sdjm 	ret = 0;
369094c80e0Sdjm  out:
370094c80e0Sdjm 	if (ret != 0)
371094c80e0Sdjm 		free(response->public_key);
372094c80e0Sdjm 	return ret;
373094c80e0Sdjm }
374094c80e0Sdjm 
375094c80e0Sdjm static int
376094c80e0Sdjm pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response)
377094c80e0Sdjm {
378094c80e0Sdjm 	switch(alg) {
379094c80e0Sdjm 	case SK_ECDSA:
380094c80e0Sdjm 		return pack_public_key_ecdsa(cred, response);
381094c80e0Sdjm 	case SK_ED25519:
382094c80e0Sdjm 		return pack_public_key_ed25519(cred, response);
383094c80e0Sdjm 	default:
384094c80e0Sdjm 		return -1;
385094c80e0Sdjm 	}
386094c80e0Sdjm }
387094c80e0Sdjm 
388094c80e0Sdjm int
389094c80e0Sdjm sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
390094c80e0Sdjm     const char *application, uint8_t flags,
391094c80e0Sdjm     struct sk_enroll_response **enroll_reponse)
392094c80e0Sdjm {
393094c80e0Sdjm 	fido_cred_t *cred = NULL;
394094c80e0Sdjm 	fido_dev_t *dev = NULL;
395094c80e0Sdjm 	const uint8_t *ptr;
396094c80e0Sdjm 	uint8_t user_id[32];
397094c80e0Sdjm 	struct sk_enroll_response *response = NULL;
398094c80e0Sdjm 	size_t len;
399094c80e0Sdjm 	int cose_alg;
400094c80e0Sdjm 	int ret = -1;
401094c80e0Sdjm 	int r;
402094c80e0Sdjm 	char *device = NULL;
403094c80e0Sdjm 
404094c80e0Sdjm 	(void)flags; /* XXX; unused */
405094c80e0Sdjm #ifdef SK_DEBUG
406094c80e0Sdjm 	fido_init(FIDO_DEBUG);
407094c80e0Sdjm #endif
408094c80e0Sdjm 	if (enroll_reponse == NULL) {
409094c80e0Sdjm 		skdebug(__func__, "enroll_reponse == NULL");
410094c80e0Sdjm 		goto out;
411094c80e0Sdjm 	}
412094c80e0Sdjm 	*enroll_reponse = NULL;
413094c80e0Sdjm 	switch(alg) {
414094c80e0Sdjm 	case SK_ECDSA:
415094c80e0Sdjm 		cose_alg = COSE_ES256;
416094c80e0Sdjm 		break;
417094c80e0Sdjm 	case SK_ED25519:
418094c80e0Sdjm 		cose_alg = COSE_EDDSA;
419094c80e0Sdjm 		break;
420094c80e0Sdjm 	default:
421094c80e0Sdjm 		skdebug(__func__, "unsupported key type %d", alg);
422094c80e0Sdjm 		goto out;
423094c80e0Sdjm 	}
424094c80e0Sdjm 	if ((device = pick_first_device()) == NULL) {
425094c80e0Sdjm 		skdebug(__func__, "pick_first_device failed");
426094c80e0Sdjm 		goto out;
427094c80e0Sdjm 	}
428094c80e0Sdjm 	skdebug(__func__, "using device %s", device);
429094c80e0Sdjm 	if ((cred = fido_cred_new()) == NULL) {
430094c80e0Sdjm 		skdebug(__func__, "fido_cred_new failed");
431094c80e0Sdjm 		goto out;
432094c80e0Sdjm 	}
433094c80e0Sdjm 	memset(user_id, 0, sizeof(user_id));
434094c80e0Sdjm 	if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
435094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
436094c80e0Sdjm 		goto out;
437094c80e0Sdjm 	}
438094c80e0Sdjm 	if ((r = fido_cred_set_clientdata_hash(cred, challenge,
439094c80e0Sdjm 	    challenge_len)) != FIDO_OK) {
440094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
441094c80e0Sdjm 		    fido_strerr(r));
442094c80e0Sdjm 		goto out;
443094c80e0Sdjm 	}
444094c80e0Sdjm 	if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
445094c80e0Sdjm 	    "openssh", "openssh", NULL)) != FIDO_OK) {
446094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
447094c80e0Sdjm 		goto out;
448094c80e0Sdjm 	}
449094c80e0Sdjm 	if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
450094c80e0Sdjm 		skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
451094c80e0Sdjm 		goto out;
452094c80e0Sdjm 	}
453094c80e0Sdjm 	if ((dev = fido_dev_new()) == NULL) {
454094c80e0Sdjm 		skdebug(__func__, "fido_dev_new failed");
455094c80e0Sdjm 		goto out;
456094c80e0Sdjm 	}
457094c80e0Sdjm 	if ((r = fido_dev_open(dev, device)) != FIDO_OK) {
458094c80e0Sdjm 		skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));
459094c80e0Sdjm 		goto out;
460094c80e0Sdjm 	}
461094c80e0Sdjm 	if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) {
462094c80e0Sdjm 		skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
463094c80e0Sdjm 		goto out;
464094c80e0Sdjm 	}
465094c80e0Sdjm 	if (fido_cred_x5c_ptr(cred) != NULL) {
466094c80e0Sdjm 		if ((r = fido_cred_verify(cred)) != FIDO_OK) {
467094c80e0Sdjm 			skdebug(__func__, "fido_cred_verify: %s",
468094c80e0Sdjm 			    fido_strerr(r));
469094c80e0Sdjm 			goto out;
470094c80e0Sdjm 		}
471094c80e0Sdjm 	} else {
472094c80e0Sdjm 		skdebug(__func__, "self-attested credential");
473094c80e0Sdjm 		if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
474094c80e0Sdjm 			skdebug(__func__, "fido_cred_verify_self: %s",
475094c80e0Sdjm 			    fido_strerr(r));
476094c80e0Sdjm 			goto out;
477094c80e0Sdjm 		}
478094c80e0Sdjm 	}
479094c80e0Sdjm 	if ((response = calloc(1, sizeof(*response))) == NULL) {
480094c80e0Sdjm 		skdebug(__func__, "calloc response failed");
481094c80e0Sdjm 		goto out;
482094c80e0Sdjm 	}
483094c80e0Sdjm 	if (pack_public_key(alg, cred, response) != 0) {
484094c80e0Sdjm 		skdebug(__func__, "pack_public_key failed");
485094c80e0Sdjm 		goto out;
486094c80e0Sdjm 	}
487094c80e0Sdjm 	if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
488094c80e0Sdjm 		len = fido_cred_id_len(cred);
489094c80e0Sdjm 		if ((response->key_handle = calloc(1, len)) == NULL) {
490094c80e0Sdjm 			skdebug(__func__, "calloc key handle failed");
491094c80e0Sdjm 			goto out;
492094c80e0Sdjm 		}
493094c80e0Sdjm 		memcpy(response->key_handle, ptr, len);
494094c80e0Sdjm 		response->key_handle_len = len;
495094c80e0Sdjm 	}
496094c80e0Sdjm 	if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
497094c80e0Sdjm 		len = fido_cred_sig_len(cred);
498094c80e0Sdjm 		if ((response->signature = calloc(1, len)) == NULL) {
499094c80e0Sdjm 			skdebug(__func__, "calloc signature failed");
500094c80e0Sdjm 			goto out;
501094c80e0Sdjm 		}
502094c80e0Sdjm 		memcpy(response->signature, ptr, len);
503094c80e0Sdjm 		response->signature_len = len;
504094c80e0Sdjm 	}
505094c80e0Sdjm 	if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
506094c80e0Sdjm 		len = fido_cred_x5c_len(cred);
507094c80e0Sdjm 		if ((response->attestation_cert = calloc(1, len)) == NULL) {
508094c80e0Sdjm 			skdebug(__func__, "calloc attestation cert failed");
509094c80e0Sdjm 			goto out;
510094c80e0Sdjm 		}
511094c80e0Sdjm 		memcpy(response->attestation_cert, ptr, len);
512094c80e0Sdjm 		response->attestation_cert_len = len;
513094c80e0Sdjm 	}
514094c80e0Sdjm 	*enroll_reponse = response;
515094c80e0Sdjm 	response = NULL;
516094c80e0Sdjm 	ret = 0;
517094c80e0Sdjm  out:
518094c80e0Sdjm 	free(device);
519094c80e0Sdjm 	if (response != NULL) {
520094c80e0Sdjm 		free(response->public_key);
521094c80e0Sdjm 		free(response->key_handle);
522094c80e0Sdjm 		free(response->signature);
523094c80e0Sdjm 		free(response->attestation_cert);
524094c80e0Sdjm 		free(response);
525094c80e0Sdjm 	}
526094c80e0Sdjm 	if (dev != NULL) {
527094c80e0Sdjm 		fido_dev_close(dev);
528094c80e0Sdjm 		fido_dev_free(&dev);
529094c80e0Sdjm 	}
530094c80e0Sdjm 	if (cred != NULL) {
531094c80e0Sdjm 		fido_cred_free(&cred);
532094c80e0Sdjm 	}
533094c80e0Sdjm 	return ret;
534094c80e0Sdjm }
535094c80e0Sdjm 
536094c80e0Sdjm static int
537094c80e0Sdjm pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
538094c80e0Sdjm {
539094c80e0Sdjm 	ECDSA_SIG *sig = NULL;
540094c80e0Sdjm 	const BIGNUM *sig_r, *sig_s;
541094c80e0Sdjm 	const unsigned char *cp;
542094c80e0Sdjm 	size_t sig_len;
543094c80e0Sdjm 	int ret = -1;
544094c80e0Sdjm 
545094c80e0Sdjm 	cp = fido_assert_sig_ptr(assert, 0);
546094c80e0Sdjm 	sig_len = fido_assert_sig_len(assert, 0);
547094c80e0Sdjm 	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
548094c80e0Sdjm 		skdebug(__func__, "d2i_ECDSA_SIG failed");
549094c80e0Sdjm 		goto out;
550094c80e0Sdjm 	}
551094c80e0Sdjm 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
552094c80e0Sdjm 	response->sig_r_len = BN_num_bytes(sig_r);
553094c80e0Sdjm 	response->sig_s_len = BN_num_bytes(sig_s);
554094c80e0Sdjm 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
555094c80e0Sdjm 	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
556094c80e0Sdjm 		skdebug(__func__, "calloc signature failed");
557094c80e0Sdjm 		goto out;
558094c80e0Sdjm 	}
559094c80e0Sdjm 	BN_bn2bin(sig_r, response->sig_r);
560094c80e0Sdjm 	BN_bn2bin(sig_s, response->sig_s);
561094c80e0Sdjm 	ret = 0;
562094c80e0Sdjm  out:
563094c80e0Sdjm 	ECDSA_SIG_free(sig);
564094c80e0Sdjm 	if (ret != 0) {
565094c80e0Sdjm 		free(response->sig_r);
566094c80e0Sdjm 		free(response->sig_s);
567094c80e0Sdjm 		response->sig_r = NULL;
568094c80e0Sdjm 		response->sig_s = NULL;
569094c80e0Sdjm 	}
570094c80e0Sdjm 	return ret;
571094c80e0Sdjm }
572094c80e0Sdjm 
573094c80e0Sdjm static int
574094c80e0Sdjm pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
575094c80e0Sdjm {
576094c80e0Sdjm 	const unsigned char *ptr;
577094c80e0Sdjm 	size_t len;
578094c80e0Sdjm 	int ret = -1;
579094c80e0Sdjm 
580094c80e0Sdjm 	ptr = fido_assert_sig_ptr(assert, 0);
581094c80e0Sdjm 	len = fido_assert_sig_len(assert, 0);
582094c80e0Sdjm 	if (len != 64) {
583094c80e0Sdjm 		skdebug(__func__, "bad length %zu", len);
584094c80e0Sdjm 		goto out;
585094c80e0Sdjm 	}
586094c80e0Sdjm 	response->sig_r_len = len;
587094c80e0Sdjm 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
588094c80e0Sdjm 		skdebug(__func__, "calloc signature failed");
589094c80e0Sdjm 		goto out;
590094c80e0Sdjm 	}
591094c80e0Sdjm 	memcpy(response->sig_r, ptr, len);
592094c80e0Sdjm 	ret = 0;
593094c80e0Sdjm  out:
594094c80e0Sdjm 	if (ret != 0) {
595094c80e0Sdjm 		free(response->sig_r);
596094c80e0Sdjm 		response->sig_r = NULL;
597094c80e0Sdjm 	}
598094c80e0Sdjm 	return ret;
599094c80e0Sdjm }
600094c80e0Sdjm 
601094c80e0Sdjm static int
602094c80e0Sdjm pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response)
603094c80e0Sdjm {
604094c80e0Sdjm 	switch(alg) {
605094c80e0Sdjm 	case SK_ECDSA:
606094c80e0Sdjm 		return pack_sig_ecdsa(assert, response);
607094c80e0Sdjm 	case SK_ED25519:
608094c80e0Sdjm 		return pack_sig_ed25519(assert, response);
609094c80e0Sdjm 	default:
610094c80e0Sdjm 		return -1;
611094c80e0Sdjm 	}
612094c80e0Sdjm }
613094c80e0Sdjm 
614094c80e0Sdjm int
615094c80e0Sdjm sk_sign(int alg, const uint8_t *message, size_t message_len,
616094c80e0Sdjm     const char *application,
617094c80e0Sdjm     const uint8_t *key_handle, size_t key_handle_len,
618094c80e0Sdjm     uint8_t flags, struct sk_sign_response **sign_response)
619094c80e0Sdjm {
620094c80e0Sdjm 	fido_assert_t *assert = NULL;
621094c80e0Sdjm 	fido_dev_t *dev = NULL;
622094c80e0Sdjm 	struct sk_sign_response *response = NULL;
623094c80e0Sdjm 	int ret = -1;
624094c80e0Sdjm 	int r;
625094c80e0Sdjm 
626094c80e0Sdjm #ifdef SK_DEBUG
627094c80e0Sdjm 	fido_init(FIDO_DEBUG);
628094c80e0Sdjm #endif
629094c80e0Sdjm 
630094c80e0Sdjm 	if (sign_response == NULL) {
631094c80e0Sdjm 		skdebug(__func__, "sign_response == NULL");
632094c80e0Sdjm 		goto out;
633094c80e0Sdjm 	}
634094c80e0Sdjm 	*sign_response = NULL;
635094c80e0Sdjm 	if ((dev = find_device(message, message_len, application, key_handle,
636094c80e0Sdjm 	    key_handle_len)) == NULL) {
637094c80e0Sdjm 		skdebug(__func__, "couldn't find device for key handle");
638094c80e0Sdjm 		goto out;
639094c80e0Sdjm 	}
640094c80e0Sdjm 	if ((assert = fido_assert_new()) == NULL) {
641094c80e0Sdjm 		skdebug(__func__, "fido_assert_new failed");
642094c80e0Sdjm 		goto out;
643094c80e0Sdjm 	}
644094c80e0Sdjm 	if ((r = fido_assert_set_clientdata_hash(assert, message,
645094c80e0Sdjm 	    message_len)) != FIDO_OK) {
646094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
647094c80e0Sdjm 		    fido_strerr(r));
648094c80e0Sdjm 		goto out;
649094c80e0Sdjm 	}
650094c80e0Sdjm 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
651094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
652094c80e0Sdjm 		goto out;
653094c80e0Sdjm 	}
654094c80e0Sdjm 	if ((r = fido_assert_allow_cred(assert, key_handle,
655094c80e0Sdjm 	    key_handle_len)) != FIDO_OK) {
656094c80e0Sdjm 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
657094c80e0Sdjm 		goto out;
658094c80e0Sdjm 	}
659094c80e0Sdjm 	if ((r = fido_assert_set_up(assert,
660094c80e0Sdjm 	    (flags & SK_USER_PRESENCE_REQD) ?
661094c80e0Sdjm 	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
662094c80e0Sdjm 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
663094c80e0Sdjm 		goto out;
664094c80e0Sdjm 	}
665094c80e0Sdjm 	if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
666094c80e0Sdjm 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
667094c80e0Sdjm 		goto out;
668094c80e0Sdjm 	}
669094c80e0Sdjm 	if ((response = calloc(1, sizeof(*response))) == NULL) {
670094c80e0Sdjm 		skdebug(__func__, "calloc response failed");
671094c80e0Sdjm 		goto out;
672094c80e0Sdjm 	}
673094c80e0Sdjm 	response->flags = fido_assert_flags(assert, 0);
674094c80e0Sdjm 	response->counter = fido_assert_sigcount(assert, 0);
675094c80e0Sdjm 	if (pack_sig(alg, assert, response) != 0) {
676094c80e0Sdjm 		skdebug(__func__, "pack_sig failed");
677094c80e0Sdjm 		goto out;
678094c80e0Sdjm 	}
679094c80e0Sdjm 	*sign_response = response;
680094c80e0Sdjm 	response = NULL;
681094c80e0Sdjm 	ret = 0;
682094c80e0Sdjm  out:
683094c80e0Sdjm 	if (response != NULL) {
684094c80e0Sdjm 		free(response->sig_r);
685094c80e0Sdjm 		free(response->sig_s);
686094c80e0Sdjm 		free(response);
687094c80e0Sdjm 	}
688094c80e0Sdjm 	if (dev != NULL) {
689094c80e0Sdjm 		fido_dev_close(dev);
690094c80e0Sdjm 		fido_dev_free(&dev);
691094c80e0Sdjm 	}
692094c80e0Sdjm 	if (assert != NULL) {
693094c80e0Sdjm 		fido_assert_free(&assert);
694094c80e0Sdjm 	}
695094c80e0Sdjm 	return ret;
696094c80e0Sdjm }
697