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