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