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