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