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