xref: /freebsd/crypto/openssh/ssh-sk.c (revision 643ac419fafba89f5adda0e0ea75b538727453fb)
1 /* $OpenBSD: ssh-sk.c,v 1.38 2022/01/14 03:35:10 djm Exp $ */
2 /*
3  * Copyright (c) 2019 Google LLC
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /* #define DEBUG_SK 1 */
19 
20 #include "includes.h"
21 
22 #ifdef ENABLE_SK
23 
24 #include <dlfcn.h>
25 #include <stddef.h>
26 #ifdef HAVE_STDINT_H
27 # include <stdint.h>
28 #endif
29 #include <string.h>
30 #include <stdio.h>
31 
32 #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
33 #include <openssl/objects.h>
34 #include <openssl/ec.h>
35 #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
36 
37 #include "log.h"
38 #include "misc.h"
39 #include "sshbuf.h"
40 #include "sshkey.h"
41 #include "ssherr.h"
42 #include "digest.h"
43 
44 #include "ssh-sk.h"
45 #include "sk-api.h"
46 #include "crypto_api.h"
47 
48 /*
49  * Almost every use of OpenSSL in this file is for ECDSA-NISTP256.
50  * This is strictly a larger hammer than necessary, but it reduces changes
51  * with upstream.
52  */
53 #ifndef OPENSSL_HAS_ECC
54 # undef WITH_OPENSSL
55 #endif
56 
57 struct sshsk_provider {
58 	char *path;
59 	void *dlhandle;
60 
61 	/* Return the version of the middleware API */
62 	uint32_t (*sk_api_version)(void);
63 
64 	/* Enroll a U2F key (private key generation) */
65 	int (*sk_enroll)(int alg, const uint8_t *challenge,
66 	    size_t challenge_len, const char *application, uint8_t flags,
67 	    const char *pin, struct sk_option **opts,
68 	    struct sk_enroll_response **enroll_response);
69 
70 	/* Sign a challenge */
71 	int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
72 	    const char *application,
73 	    const uint8_t *key_handle, size_t key_handle_len,
74 	    uint8_t flags, const char *pin, struct sk_option **opts,
75 	    struct sk_sign_response **sign_response);
76 
77 	/* Enumerate resident keys */
78 	int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts,
79 	    struct sk_resident_key ***rks, size_t *nrks);
80 };
81 
82 /* Built-in version */
83 int ssh_sk_enroll(int alg, const uint8_t *challenge,
84     size_t challenge_len, const char *application, uint8_t flags,
85     const char *pin, struct sk_option **opts,
86     struct sk_enroll_response **enroll_response);
87 int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
88     const char *application,
89     const uint8_t *key_handle, size_t key_handle_len,
90     uint8_t flags, const char *pin, struct sk_option **opts,
91     struct sk_sign_response **sign_response);
92 int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts,
93     struct sk_resident_key ***rks, size_t *nrks);
94 
95 static void
96 sshsk_free(struct sshsk_provider *p)
97 {
98 	if (p == NULL)
99 		return;
100 	free(p->path);
101 	if (p->dlhandle != NULL)
102 		dlclose(p->dlhandle);
103 	free(p);
104 }
105 
106 static struct sshsk_provider *
107 sshsk_open(const char *path)
108 {
109 	struct sshsk_provider *ret = NULL;
110 	uint32_t version;
111 
112 	if (path == NULL || *path == '\0') {
113 		error("No FIDO SecurityKeyProvider specified");
114 		return NULL;
115 	}
116 	if ((ret = calloc(1, sizeof(*ret))) == NULL) {
117 		error_f("calloc failed");
118 		return NULL;
119 	}
120 	if ((ret->path = strdup(path)) == NULL) {
121 		error_f("strdup failed");
122 		goto fail;
123 	}
124 	/* Skip the rest if we're using the linked in middleware */
125 	if (strcasecmp(ret->path, "internal") == 0) {
126 #ifdef ENABLE_SK_INTERNAL
127 		ret->sk_enroll = ssh_sk_enroll;
128 		ret->sk_sign = ssh_sk_sign;
129 		ret->sk_load_resident_keys = ssh_sk_load_resident_keys;
130 #else
131 		error("internal security key support not enabled");
132 #endif
133 		return ret;
134 	}
135 	if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
136 		error("Provider \"%s\" dlopen failed: %s", path, dlerror());
137 		goto fail;
138 	}
139 	if ((ret->sk_api_version = dlsym(ret->dlhandle,
140 	    "sk_api_version")) == NULL) {
141 		error("Provider \"%s\" dlsym(sk_api_version) failed: %s",
142 		    path, dlerror());
143 		goto fail;
144 	}
145 	version = ret->sk_api_version();
146 	debug_f("provider %s implements version 0x%08lx", ret->path,
147 	    (u_long)version);
148 	if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
149 		error("Provider \"%s\" implements unsupported "
150 		    "version 0x%08lx (supported: 0x%08lx)",
151 		    path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR);
152 		goto fail;
153 	}
154 	if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
155 		error("Provider %s dlsym(sk_enroll) failed: %s",
156 		    path, dlerror());
157 		goto fail;
158 	}
159 	if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
160 		error("Provider \"%s\" dlsym(sk_sign) failed: %s",
161 		    path, dlerror());
162 		goto fail;
163 	}
164 	if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle,
165 	    "sk_load_resident_keys")) == NULL) {
166 		error("Provider \"%s\" dlsym(sk_load_resident_keys) "
167 		    "failed: %s", path, dlerror());
168 		goto fail;
169 	}
170 	/* success */
171 	return ret;
172 fail:
173 	sshsk_free(ret);
174 	return NULL;
175 }
176 
177 static void
178 sshsk_free_enroll_response(struct sk_enroll_response *r)
179 {
180 	if (r == NULL)
181 		return;
182 	freezero(r->key_handle, r->key_handle_len);
183 	freezero(r->public_key, r->public_key_len);
184 	freezero(r->signature, r->signature_len);
185 	freezero(r->attestation_cert, r->attestation_cert_len);
186 	freezero(r->authdata, r->authdata_len);
187 	freezero(r, sizeof(*r));
188 }
189 
190 static void
191 sshsk_free_sign_response(struct sk_sign_response *r)
192 {
193 	if (r == NULL)
194 		return;
195 	freezero(r->sig_r, r->sig_r_len);
196 	freezero(r->sig_s, r->sig_s_len);
197 	freezero(r, sizeof(*r));
198 }
199 
200 #ifdef WITH_OPENSSL
201 /* Assemble key from response */
202 static int
203 sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
204 {
205 	struct sshkey *key = NULL;
206 	struct sshbuf *b = NULL;
207 	EC_POINT *q = NULL;
208 	int r;
209 
210 	*keyp = NULL;
211 	if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
212 		error_f("sshkey_new failed");
213 		r = SSH_ERR_ALLOC_FAIL;
214 		goto out;
215 	}
216 	key->ecdsa_nid = NID_X9_62_prime256v1;
217 	if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
218 	    (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
219 	    (b = sshbuf_new()) == NULL) {
220 		error_f("allocation failed");
221 		r = SSH_ERR_ALLOC_FAIL;
222 		goto out;
223 	}
224 	if ((r = sshbuf_put_string(b,
225 	    resp->public_key, resp->public_key_len)) != 0) {
226 		error_fr(r, "sshbuf_put_string");
227 		goto out;
228 	}
229 	if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
230 		error_fr(r, "parse");
231 		r = SSH_ERR_INVALID_FORMAT;
232 		goto out;
233 	}
234 	if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
235 		error("Authenticator returned invalid ECDSA key");
236 		r = SSH_ERR_KEY_INVALID_EC_VALUE;
237 		goto out;
238 	}
239 	if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
240 		/* XXX assume it is a allocation error */
241 		error_f("allocation failed");
242 		r = SSH_ERR_ALLOC_FAIL;
243 		goto out;
244 	}
245 	/* success */
246 	*keyp = key;
247 	key = NULL; /* transferred */
248 	r = 0;
249  out:
250 	EC_POINT_free(q);
251 	sshkey_free(key);
252 	sshbuf_free(b);
253 	return r;
254 }
255 #endif /* WITH_OPENSSL */
256 
257 static int
258 sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
259 {
260 	struct sshkey *key = NULL;
261 	int r;
262 
263 	*keyp = NULL;
264 	if (resp->public_key_len != ED25519_PK_SZ) {
265 		error_f("invalid size: %zu", resp->public_key_len);
266 		r = SSH_ERR_INVALID_FORMAT;
267 		goto out;
268 	}
269 	if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
270 		error_f("sshkey_new failed");
271 		r = SSH_ERR_ALLOC_FAIL;
272 		goto out;
273 	}
274 	if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
275 		error_f("malloc failed");
276 		r = SSH_ERR_ALLOC_FAIL;
277 		goto out;
278 	}
279 	memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
280 	/* success */
281 	*keyp = key;
282 	key = NULL; /* transferred */
283 	r = 0;
284  out:
285 	sshkey_free(key);
286 	return r;
287 }
288 
289 static int
290 sshsk_key_from_response(int alg, const char *application, uint8_t flags,
291     struct sk_enroll_response *resp, struct sshkey **keyp)
292 {
293 	struct sshkey *key = NULL;
294 	int r = SSH_ERR_INTERNAL_ERROR;
295 
296 	*keyp = NULL;
297 
298 	/* Check response validity */
299 	if (resp->public_key == NULL || resp->key_handle == NULL) {
300 		error_f("sk_enroll response invalid");
301 		r = SSH_ERR_INVALID_FORMAT;
302 		goto out;
303 	}
304 	switch (alg) {
305 #ifdef WITH_OPENSSL
306 	case SSH_SK_ECDSA:
307 		if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
308 			goto out;
309 		break;
310 #endif /* WITH_OPENSSL */
311 	case SSH_SK_ED25519:
312 		if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
313 			goto out;
314 		break;
315 	default:
316 		error_f("unsupported algorithm %d", alg);
317 		r = SSH_ERR_INVALID_ARGUMENT;
318 		goto out;
319 	}
320 	key->sk_flags = flags;
321 	if ((key->sk_key_handle = sshbuf_new()) == NULL ||
322 	    (key->sk_reserved = sshbuf_new()) == NULL) {
323 		error_f("allocation failed");
324 		r = SSH_ERR_ALLOC_FAIL;
325 		goto out;
326 	}
327 	if ((key->sk_application = strdup(application)) == NULL) {
328 		error_f("strdup application failed");
329 		r = SSH_ERR_ALLOC_FAIL;
330 		goto out;
331 	}
332 	if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
333 	    resp->key_handle_len)) != 0) {
334 		error_fr(r, "put key handle");
335 		goto out;
336 	}
337 	/* success */
338 	r = 0;
339 	*keyp = key;
340 	key = NULL;
341  out:
342 	sshkey_free(key);
343 	return r;
344 }
345 
346 static int
347 skerr_to_ssherr(int skerr)
348 {
349 	switch (skerr) {
350 	case SSH_SK_ERR_UNSUPPORTED:
351 		return SSH_ERR_FEATURE_UNSUPPORTED;
352 	case SSH_SK_ERR_PIN_REQUIRED:
353 		return SSH_ERR_KEY_WRONG_PASSPHRASE;
354 	case SSH_SK_ERR_DEVICE_NOT_FOUND:
355 		return SSH_ERR_DEVICE_NOT_FOUND;
356 	case SSH_SK_ERR_GENERAL:
357 	default:
358 		return SSH_ERR_INVALID_FORMAT;
359 	}
360 }
361 
362 static void
363 sshsk_free_options(struct sk_option **opts)
364 {
365 	size_t i;
366 
367 	if (opts == NULL)
368 		return;
369 	for (i = 0; opts[i] != NULL; i++) {
370 		free(opts[i]->name);
371 		free(opts[i]->value);
372 		free(opts[i]);
373 	}
374 	free(opts);
375 }
376 
377 static int
378 sshsk_add_option(struct sk_option ***optsp, size_t *noptsp,
379     const char *name, const char *value, uint8_t required)
380 {
381 	struct sk_option **opts = *optsp;
382 	size_t nopts = *noptsp;
383 
384 	if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */
385 	    sizeof(*opts))) == NULL) {
386 		error_f("array alloc failed");
387 		return SSH_ERR_ALLOC_FAIL;
388 	}
389 	*optsp = opts;
390 	*noptsp = nopts + 1;
391 	if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) {
392 		error_f("alloc failed");
393 		return SSH_ERR_ALLOC_FAIL;
394 	}
395 	if ((opts[nopts]->name = strdup(name)) == NULL ||
396 	    (opts[nopts]->value = strdup(value)) == NULL) {
397 		error_f("alloc failed");
398 		return SSH_ERR_ALLOC_FAIL;
399 	}
400 	opts[nopts]->required = required;
401 	return 0;
402 }
403 
404 static int
405 make_options(const char *device, const char *user_id,
406     struct sk_option ***optsp)
407 {
408 	struct sk_option **opts = NULL;
409 	size_t nopts = 0;
410 	int r, ret = SSH_ERR_INTERNAL_ERROR;
411 
412 	if (device != NULL &&
413 	    (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) {
414 		ret = r;
415 		goto out;
416 	}
417 	if (user_id != NULL &&
418 	    (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) {
419 		ret = r;
420 		goto out;
421 	}
422 	/* success */
423 	*optsp = opts;
424 	opts = NULL;
425 	nopts = 0;
426 	ret = 0;
427  out:
428 	sshsk_free_options(opts);
429 	return ret;
430 }
431 
432 
433 static int
434 fill_attestation_blob(const struct sk_enroll_response *resp,
435     struct sshbuf *attest)
436 {
437 	int r;
438 
439 	if (attest == NULL)
440 		return 0; /* nothing to do */
441 	if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 ||
442 	    (r = sshbuf_put_string(attest,
443 	    resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
444 	    (r = sshbuf_put_string(attest,
445 	    resp->signature, resp->signature_len)) != 0 ||
446 	    (r = sshbuf_put_string(attest,
447 	    resp->authdata, resp->authdata_len)) != 0 ||
448 	    (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */
449 	    (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) {
450 		error_fr(r, "compose");
451 		return r;
452 	}
453 	/* success */
454 	return 0;
455 }
456 
457 int
458 sshsk_enroll(int type, const char *provider_path, const char *device,
459     const char *application, const char *userid, uint8_t flags,
460     const char *pin, struct sshbuf *challenge_buf,
461     struct sshkey **keyp, struct sshbuf *attest)
462 {
463 	struct sshsk_provider *skp = NULL;
464 	struct sshkey *key = NULL;
465 	u_char randchall[32];
466 	const u_char *challenge;
467 	size_t challenge_len;
468 	struct sk_enroll_response *resp = NULL;
469 	struct sk_option **opts = NULL;
470 	int r = SSH_ERR_INTERNAL_ERROR;
471 	int alg;
472 
473 	debug_f("provider \"%s\", device \"%s\", application \"%s\", "
474 	    "userid \"%s\", flags 0x%02x, challenge len %zu%s",
475 	    provider_path, device, application, userid, flags,
476 	    challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
477 	    (pin != NULL && *pin != '\0') ? " with-pin" : "");
478 
479 	*keyp = NULL;
480 	if (attest)
481 		sshbuf_reset(attest);
482 
483 	if ((r = make_options(device, userid, &opts)) != 0)
484 		goto out;
485 
486 	switch (type) {
487 #ifdef WITH_OPENSSL
488 	case KEY_ECDSA_SK:
489 		alg = SSH_SK_ECDSA;
490 		break;
491 #endif /* WITH_OPENSSL */
492 	case KEY_ED25519_SK:
493 		alg = SSH_SK_ED25519;
494 		break;
495 	default:
496 		error_f("unsupported key type");
497 		r = SSH_ERR_INVALID_ARGUMENT;
498 		goto out;
499 	}
500 	if (provider_path == NULL) {
501 		error_f("missing provider");
502 		r = SSH_ERR_INVALID_ARGUMENT;
503 		goto out;
504 	}
505 	if (application == NULL || *application == '\0') {
506 		error_f("missing application");
507 		r = SSH_ERR_INVALID_ARGUMENT;
508 		goto out;
509 	}
510 	if (challenge_buf == NULL) {
511 		debug_f("using random challenge");
512 		arc4random_buf(randchall, sizeof(randchall));
513 		challenge = randchall;
514 		challenge_len = sizeof(randchall);
515 	} else if (sshbuf_len(challenge_buf) == 0) {
516 		error("Missing enrollment challenge");
517 		r = SSH_ERR_INVALID_ARGUMENT;
518 		goto out;
519 	} else {
520 		challenge = sshbuf_ptr(challenge_buf);
521 		challenge_len = sshbuf_len(challenge_buf);
522 		debug3_f("using explicit challenge len=%zd", challenge_len);
523 	}
524 	if ((skp = sshsk_open(provider_path)) == NULL) {
525 		r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
526 		goto out;
527 	}
528 	/* XXX validate flags? */
529 	/* enroll key */
530 	if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
531 	    flags, pin, opts, &resp)) != 0) {
532 		debug_f("provider \"%s\" failure %d", provider_path, r);
533 		r = skerr_to_ssherr(r);
534 		goto out;
535 	}
536 
537 	if ((r = sshsk_key_from_response(alg, application, resp->flags,
538 	    resp, &key)) != 0)
539 		goto out;
540 
541 	/* Optionally fill in the attestation information */
542 	if ((r = fill_attestation_blob(resp, attest)) != 0)
543 		goto out;
544 
545 	/* success */
546 	*keyp = key;
547 	key = NULL; /* transferred */
548 	r = 0;
549  out:
550 	sshsk_free_options(opts);
551 	sshsk_free(skp);
552 	sshkey_free(key);
553 	sshsk_free_enroll_response(resp);
554 	explicit_bzero(randchall, sizeof(randchall));
555 	return r;
556 }
557 
558 #ifdef WITH_OPENSSL
559 static int
560 sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
561 {
562 	struct sshbuf *inner_sig = NULL;
563 	int r = SSH_ERR_INTERNAL_ERROR;
564 
565 	/* Check response validity */
566 	if (resp->sig_r == NULL || resp->sig_s == NULL) {
567 		error_f("sk_sign response invalid");
568 		r = SSH_ERR_INVALID_FORMAT;
569 		goto out;
570 	}
571 	if ((inner_sig = sshbuf_new()) == NULL) {
572 		r = SSH_ERR_ALLOC_FAIL;
573 		goto out;
574 	}
575 	/* Prepare and append inner signature object */
576 	if ((r = sshbuf_put_bignum2_bytes(inner_sig,
577 	    resp->sig_r, resp->sig_r_len)) != 0 ||
578 	    (r = sshbuf_put_bignum2_bytes(inner_sig,
579 	    resp->sig_s, resp->sig_s_len)) != 0) {
580 		error_fr(r, "compose inner");
581 		goto out;
582 	}
583 	if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
584 	    (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
585 	    (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
586 		error_fr(r, "compose");
587 		goto out;
588 	}
589 #ifdef DEBUG_SK
590 	fprintf(stderr, "%s: sig_r:\n", __func__);
591 	sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
592 	fprintf(stderr, "%s: sig_s:\n", __func__);
593 	sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
594 	fprintf(stderr, "%s: inner:\n", __func__);
595 	sshbuf_dump(inner_sig, stderr);
596 #endif
597 	r = 0;
598  out:
599 	sshbuf_free(inner_sig);
600 	return r;
601 }
602 #endif /* WITH_OPENSSL */
603 
604 static int
605 sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
606 {
607 	int r = SSH_ERR_INTERNAL_ERROR;
608 
609 	/* Check response validity */
610 	if (resp->sig_r == NULL) {
611 		error_f("sk_sign response invalid");
612 		r = SSH_ERR_INVALID_FORMAT;
613 		goto out;
614 	}
615 	if ((r = sshbuf_put_string(sig,
616 	    resp->sig_r, resp->sig_r_len)) != 0 ||
617 	    (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
618 	    (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
619 		error_fr(r, "compose");
620 		goto out;
621 	}
622 #ifdef DEBUG_SK
623 	fprintf(stderr, "%s: sig_r:\n", __func__);
624 	sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
625 #endif
626 	r = 0;
627  out:
628 	return r;
629 }
630 
631 int
632 sshsk_sign(const char *provider_path, struct sshkey *key,
633     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
634     u_int compat, const char *pin)
635 {
636 	struct sshsk_provider *skp = NULL;
637 	int r = SSH_ERR_INTERNAL_ERROR;
638 	int type, alg;
639 	struct sk_sign_response *resp = NULL;
640 	struct sshbuf *inner_sig = NULL, *sig = NULL;
641 	struct sk_option **opts = NULL;
642 
643 	debug_f("provider \"%s\", key %s, flags 0x%02x%s",
644 	    provider_path, sshkey_type(key), key->sk_flags,
645 	    (pin != NULL && *pin != '\0') ? " with-pin" : "");
646 
647 	if (sigp != NULL)
648 		*sigp = NULL;
649 	if (lenp != NULL)
650 		*lenp = 0;
651 	type = sshkey_type_plain(key->type);
652 	switch (type) {
653 #ifdef WITH_OPENSSL
654 	case KEY_ECDSA_SK:
655 		alg = SSH_SK_ECDSA;
656 		break;
657 #endif /* WITH_OPENSSL */
658 	case KEY_ED25519_SK:
659 		alg = SSH_SK_ED25519;
660 		break;
661 	default:
662 		return SSH_ERR_INVALID_ARGUMENT;
663 	}
664 	if (provider_path == NULL ||
665 	    key->sk_key_handle == NULL ||
666 	    key->sk_application == NULL || *key->sk_application == '\0') {
667 		r = SSH_ERR_INVALID_ARGUMENT;
668 		goto out;
669 	}
670 	if ((skp = sshsk_open(provider_path)) == NULL) {
671 		r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
672 		goto out;
673 	}
674 #ifdef DEBUG_SK
675 	fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n",
676 	    __func__, key->sk_flags, key->sk_application);
677 	fprintf(stderr, "%s: sk_key_handle:\n", __func__);
678 	sshbuf_dump(key->sk_key_handle, stderr);
679 #endif
680 	if ((r = skp->sk_sign(alg, data, datalen, key->sk_application,
681 	    sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
682 	    key->sk_flags, pin, opts, &resp)) != 0) {
683 		debug_f("sk_sign failed with code %d", r);
684 		r = skerr_to_ssherr(r);
685 		goto out;
686 	}
687 	/* Assemble signature */
688 	if ((sig = sshbuf_new()) == NULL) {
689 		r = SSH_ERR_ALLOC_FAIL;
690 		goto out;
691 	}
692 	if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
693 		error_fr(r, "compose outer");
694 		goto out;
695 	}
696 	switch (type) {
697 #ifdef WITH_OPENSSL
698 	case KEY_ECDSA_SK:
699 		if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
700 			goto out;
701 		break;
702 #endif /* WITH_OPENSSL */
703 	case KEY_ED25519_SK:
704 		if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
705 			goto out;
706 		break;
707 	}
708 #ifdef DEBUG_SK
709 	fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
710 	    __func__, resp->flags, resp->counter);
711 	fprintf(stderr, "%s: data to sign:\n", __func__);
712 	sshbuf_dump_data(data, datalen, stderr);
713 	fprintf(stderr, "%s: sigbuf:\n", __func__);
714 	sshbuf_dump(sig, stderr);
715 #endif
716 	if (sigp != NULL) {
717 		if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
718 			r = SSH_ERR_ALLOC_FAIL;
719 			goto out;
720 		}
721 		memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
722 	}
723 	if (lenp != NULL)
724 		*lenp = sshbuf_len(sig);
725 	/* success */
726 	r = 0;
727  out:
728 	sshsk_free_options(opts);
729 	sshsk_free(skp);
730 	sshsk_free_sign_response(resp);
731 	sshbuf_free(sig);
732 	sshbuf_free(inner_sig);
733 	return r;
734 }
735 
736 static void
737 sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
738 {
739 	size_t i;
740 
741 	if (nrks == 0 || rks == NULL)
742 		return;
743 	for (i = 0; i < nrks; i++) {
744 		free(rks[i]->application);
745 		freezero(rks[i]->user_id, rks[i]->user_id_len);
746 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
747 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
748 		freezero(rks[i]->key.signature, rks[i]->key.signature_len);
749 		freezero(rks[i]->key.attestation_cert,
750 		    rks[i]->key.attestation_cert_len);
751 		freezero(rks[i], sizeof(**rks));
752 	}
753 	free(rks);
754 }
755 
756 static void
757 sshsk_free_resident_key(struct sshsk_resident_key *srk)
758 {
759 	if (srk == NULL)
760 		return;
761 	sshkey_free(srk->key);
762 	freezero(srk->user_id, srk->user_id_len);
763 	free(srk);
764 }
765 
766 
767 void
768 sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks)
769 {
770 	size_t i;
771 
772 	if (srks == NULL || nsrks == 0)
773 		return;
774 
775 	for (i = 0; i < nsrks; i++)
776 		sshsk_free_resident_key(srks[i]);
777 	free(srks);
778 }
779 
780 int
781 sshsk_load_resident(const char *provider_path, const char *device,
782     const char *pin, u_int flags, struct sshsk_resident_key ***srksp,
783     size_t *nsrksp)
784 {
785 	struct sshsk_provider *skp = NULL;
786 	int r = SSH_ERR_INTERNAL_ERROR;
787 	struct sk_resident_key **rks = NULL;
788 	size_t i, nrks = 0, nsrks = 0;
789 	struct sshkey *key = NULL;
790 	struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp;
791 	uint8_t sk_flags;
792 	struct sk_option **opts = NULL;
793 
794 	debug_f("provider \"%s\"%s", provider_path,
795 	    (pin != NULL && *pin != '\0') ? ", have-pin": "");
796 
797 	if (srksp == NULL || nsrksp == NULL)
798 		return SSH_ERR_INVALID_ARGUMENT;
799 	*srksp = NULL;
800 	*nsrksp = 0;
801 
802 	if ((r = make_options(device, NULL, &opts)) != 0)
803 		goto out;
804 	if ((skp = sshsk_open(provider_path)) == NULL) {
805 		r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
806 		goto out;
807 	}
808 	if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) {
809 		error("Provider \"%s\" returned failure %d", provider_path, r);
810 		r = skerr_to_ssherr(r);
811 		goto out;
812 	}
813 	for (i = 0; i < nrks; i++) {
814 		debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu",
815 		    i, rks[i]->slot, rks[i]->alg, rks[i]->application,
816 		    rks[i]->user_id_len);
817 		/* XXX need better filter here */
818 		if (strncmp(rks[i]->application, "ssh:", 4) != 0)
819 			continue;
820 		switch (rks[i]->alg) {
821 		case SSH_SK_ECDSA:
822 		case SSH_SK_ED25519:
823 			break;
824 		default:
825 			continue;
826 		}
827 		sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY;
828 		if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD))
829 			sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
830 		if ((r = sshsk_key_from_response(rks[i]->alg,
831 		    rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0)
832 			goto out;
833 		if ((srk = calloc(1, sizeof(*srk))) == NULL) {
834 			error_f("calloc failed");
835 			r = SSH_ERR_ALLOC_FAIL;
836 			goto out;
837 		}
838 		srk->key = key;
839 		key = NULL; /* transferred */
840 		if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) {
841 			error_f("calloc failed");
842 			r = SSH_ERR_ALLOC_FAIL;
843 			goto out;
844 		}
845 		memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len);
846 		srk->user_id_len = rks[i]->user_id_len;
847 		if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
848 		    sizeof(*tmp))) == NULL) {
849 			error_f("recallocarray failed");
850 			r = SSH_ERR_ALLOC_FAIL;
851 			goto out;
852 		}
853 		srks = tmp;
854 		srks[nsrks++] = srk;
855 		srk = NULL;
856 		/* XXX synthesise comment */
857 	}
858 	/* success */
859 	*srksp = srks;
860 	*nsrksp = nsrks;
861 	srks = NULL;
862 	nsrks = 0;
863 	r = 0;
864  out:
865 	sshsk_free_options(opts);
866 	sshsk_free(skp);
867 	sshsk_free_sk_resident_keys(rks, nrks);
868 	sshkey_free(key);
869 	sshsk_free_resident_key(srk);
870 	sshsk_free_resident_keys(srks, nsrks);
871 	return r;
872 }
873 
874 #endif /* ENABLE_SK */
875