Annotation of src/usr.bin/ssh/ssh-sk.c, Revision 1.39
1.39 ! djm 1: /* $OpenBSD: ssh-sk.c,v 1.38 2022/01/14 03:35:10 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.39 ! djm 337: case SSH_SK_ERR_CREDENTIAL_EXISTS:
! 338: return SSH_ERR_KEY_BAD_PERMISSIONS;
1.23 djm 339: case SSH_SK_ERR_GENERAL:
340: default:
341: return SSH_ERR_INVALID_FORMAT;
342: }
343: }
344:
1.24 djm 345: static void
346: sshsk_free_options(struct sk_option **opts)
347: {
348: size_t i;
349:
350: if (opts == NULL)
351: return;
352: for (i = 0; opts[i] != NULL; i++) {
353: free(opts[i]->name);
354: free(opts[i]->value);
355: free(opts[i]);
356: }
357: free(opts);
358: }
359:
360: static int
361: sshsk_add_option(struct sk_option ***optsp, size_t *noptsp,
362: const char *name, const char *value, uint8_t required)
363: {
364: struct sk_option **opts = *optsp;
365: size_t nopts = *noptsp;
366:
367: if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */
368: sizeof(*opts))) == NULL) {
1.33 djm 369: error_f("array alloc failed");
1.24 djm 370: return SSH_ERR_ALLOC_FAIL;
371: }
372: *optsp = opts;
373: *noptsp = nopts + 1;
374: if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) {
1.33 djm 375: error_f("alloc failed");
1.24 djm 376: return SSH_ERR_ALLOC_FAIL;
377: }
378: if ((opts[nopts]->name = strdup(name)) == NULL ||
379: (opts[nopts]->value = strdup(value)) == NULL) {
1.33 djm 380: error_f("alloc failed");
1.24 djm 381: return SSH_ERR_ALLOC_FAIL;
382: }
383: opts[nopts]->required = required;
384: return 0;
385: }
386:
387: static int
388: make_options(const char *device, const char *user_id,
389: struct sk_option ***optsp)
390: {
391: struct sk_option **opts = NULL;
392: size_t nopts = 0;
393: int r, ret = SSH_ERR_INTERNAL_ERROR;
394:
395: if (device != NULL &&
396: (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) {
397: ret = r;
398: goto out;
399: }
400: if (user_id != NULL &&
401: (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) {
402: ret = r;
403: goto out;
404: }
405: /* success */
406: *optsp = opts;
407: opts = NULL;
408: nopts = 0;
409: ret = 0;
410: out:
411: sshsk_free_options(opts);
412: return ret;
413: }
414:
1.32 djm 415:
416: static int
417: fill_attestation_blob(const struct sk_enroll_response *resp,
418: struct sshbuf *attest)
419: {
420: int r;
421:
422: if (attest == NULL)
423: return 0; /* nothing to do */
424: if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 ||
425: (r = sshbuf_put_string(attest,
426: resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
427: (r = sshbuf_put_string(attest,
428: resp->signature, resp->signature_len)) != 0 ||
429: (r = sshbuf_put_string(attest,
430: resp->authdata, resp->authdata_len)) != 0 ||
431: (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */
432: (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) {
1.33 djm 433: error_fr(r, "compose");
1.32 djm 434: return r;
435: }
436: /* success */
437: return 0;
438: }
439:
1.1 djm 440: int
1.24 djm 441: sshsk_enroll(int type, const char *provider_path, const char *device,
442: const char *application, const char *userid, uint8_t flags,
443: const char *pin, struct sshbuf *challenge_buf,
1.21 djm 444: struct sshkey **keyp, struct sshbuf *attest)
1.1 djm 445: {
446: struct sshsk_provider *skp = NULL;
447: struct sshkey *key = NULL;
448: u_char randchall[32];
449: const u_char *challenge;
450: size_t challenge_len;
451: struct sk_enroll_response *resp = NULL;
1.24 djm 452: struct sk_option **opts = NULL;
1.1 djm 453: int r = SSH_ERR_INTERNAL_ERROR;
1.7 markus 454: int alg;
1.1 djm 455:
1.33 djm 456: debug_f("provider \"%s\", device \"%s\", application \"%s\", "
457: "userid \"%s\", flags 0x%02x, challenge len %zu%s",
1.24 djm 458: provider_path, device, application, userid, flags,
459: challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
1.21 djm 460: (pin != NULL && *pin != '\0') ? " with-pin" : "");
1.13 djm 461:
1.1 djm 462: *keyp = NULL;
463: if (attest)
464: sshbuf_reset(attest);
1.24 djm 465:
466: if ((r = make_options(device, userid, &opts)) != 0)
467: goto out;
468:
1.6 markus 469: switch (type) {
1.15 naddy 470: #ifdef WITH_OPENSSL
1.6 markus 471: case KEY_ECDSA_SK:
1.7 markus 472: alg = SSH_SK_ECDSA;
473: break;
1.15 naddy 474: #endif /* WITH_OPENSSL */
1.6 markus 475: case KEY_ED25519_SK:
1.7 markus 476: alg = SSH_SK_ED25519;
1.6 markus 477: break;
478: default:
1.33 djm 479: error_f("unsupported key type");
1.6 markus 480: r = SSH_ERR_INVALID_ARGUMENT;
481: goto out;
482: }
1.1 djm 483: if (provider_path == NULL) {
1.33 djm 484: error_f("missing provider");
1.1 djm 485: r = SSH_ERR_INVALID_ARGUMENT;
486: goto out;
487: }
488: if (application == NULL || *application == '\0') {
1.33 djm 489: error_f("missing application");
1.1 djm 490: r = SSH_ERR_INVALID_ARGUMENT;
491: goto out;
492: }
493: if (challenge_buf == NULL) {
1.33 djm 494: debug_f("using random challenge");
1.1 djm 495: arc4random_buf(randchall, sizeof(randchall));
496: challenge = randchall;
497: challenge_len = sizeof(randchall);
498: } else if (sshbuf_len(challenge_buf) == 0) {
499: error("Missing enrollment challenge");
500: r = SSH_ERR_INVALID_ARGUMENT;
501: goto out;
502: } else {
503: challenge = sshbuf_ptr(challenge_buf);
504: challenge_len = sshbuf_len(challenge_buf);
1.33 djm 505: debug3_f("using explicit challenge len=%zd", challenge_len);
1.1 djm 506: }
507: if ((skp = sshsk_open(provider_path)) == NULL) {
508: r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
509: goto out;
510: }
511: /* XXX validate flags? */
512: /* enroll key */
1.7 markus 513: if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
1.24 djm 514: flags, pin, opts, &resp)) != 0) {
1.33 djm 515: debug_f("provider \"%s\" failure %d", provider_path, r);
1.23 djm 516: r = skerr_to_ssherr(r);
1.1 djm 517: goto out;
518: }
1.19 djm 519:
1.37 djm 520: if ((r = sshsk_key_from_response(alg, application, resp->flags,
1.19 djm 521: resp, &key)) != 0)
1.1 djm 522: goto out;
1.19 djm 523:
1.1 djm 524: /* Optionally fill in the attestation information */
1.32 djm 525: if ((r = fill_attestation_blob(resp, attest)) != 0)
526: goto out;
527:
1.1 djm 528: /* success */
529: *keyp = key;
530: key = NULL; /* transferred */
531: r = 0;
532: out:
1.24 djm 533: sshsk_free_options(opts);
1.1 djm 534: sshsk_free(skp);
535: sshkey_free(key);
536: sshsk_free_enroll_response(resp);
537: explicit_bzero(randchall, sizeof(randchall));
538: return r;
539: }
540:
1.15 naddy 541: #ifdef WITH_OPENSSL
1.3 markus 542: static int
1.9 markus 543: sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.3 markus 544: {
545: struct sshbuf *inner_sig = NULL;
546: int r = SSH_ERR_INTERNAL_ERROR;
547:
1.8 markus 548: /* Check response validity */
1.11 markus 549: if (resp->sig_r == NULL || resp->sig_s == NULL) {
1.33 djm 550: error_f("sk_sign response invalid");
1.8 markus 551: r = SSH_ERR_INVALID_FORMAT;
552: goto out;
553: }
1.3 markus 554: if ((inner_sig = sshbuf_new()) == NULL) {
555: r = SSH_ERR_ALLOC_FAIL;
556: goto out;
557: }
1.9 markus 558: /* Prepare and append inner signature object */
1.3 markus 559: if ((r = sshbuf_put_bignum2_bytes(inner_sig,
560: resp->sig_r, resp->sig_r_len)) != 0 ||
561: (r = sshbuf_put_bignum2_bytes(inner_sig,
1.16 djm 562: resp->sig_s, resp->sig_s_len)) != 0) {
1.33 djm 563: error_fr(r, "compose inner");
1.3 markus 564: goto out;
565: }
1.16 djm 566: if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
567: (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
568: (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.33 djm 569: error_fr(r, "compose");
1.9 markus 570: goto out;
571: }
1.3 markus 572: #ifdef DEBUG_SK
573: fprintf(stderr, "%s: sig_r:\n", __func__);
574: sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
575: fprintf(stderr, "%s: sig_s:\n", __func__);
576: sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
1.9 markus 577: fprintf(stderr, "%s: inner:\n", __func__);
578: sshbuf_dump(inner_sig, stderr);
1.5 markus 579: #endif
580: r = 0;
1.9 markus 581: out:
1.5 markus 582: sshbuf_free(inner_sig);
583: return r;
584: }
1.15 naddy 585: #endif /* WITH_OPENSSL */
1.5 markus 586:
587: static int
1.9 markus 588: sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.5 markus 589: {
590: int r = SSH_ERR_INTERNAL_ERROR;
591:
1.8 markus 592: /* Check response validity */
593: if (resp->sig_r == NULL) {
1.33 djm 594: error_f("sk_sign response invalid");
1.8 markus 595: r = SSH_ERR_INVALID_FORMAT;
596: goto out;
597: }
1.9 markus 598: if ((r = sshbuf_put_string(sig,
1.5 markus 599: resp->sig_r, resp->sig_r_len)) != 0 ||
1.9 markus 600: (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
601: (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.33 djm 602: error_fr(r, "compose");
1.5 markus 603: goto out;
604: }
605: #ifdef DEBUG_SK
606: fprintf(stderr, "%s: sig_r:\n", __func__);
607: sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
1.3 markus 608: #endif
609: r = 0;
1.9 markus 610: out:
1.29 markus 611: return r;
1.3 markus 612: }
613:
1.1 djm 614: int
1.18 djm 615: sshsk_sign(const char *provider_path, struct sshkey *key,
1.1 djm 616: u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
1.21 djm 617: u_int compat, const char *pin)
1.1 djm 618: {
619: struct sshsk_provider *skp = NULL;
620: int r = SSH_ERR_INTERNAL_ERROR;
1.7 markus 621: int type, alg;
1.1 djm 622: struct sk_sign_response *resp = NULL;
623: struct sshbuf *inner_sig = NULL, *sig = NULL;
1.24 djm 624: struct sk_option **opts = NULL;
1.13 djm 625:
1.33 djm 626: debug_f("provider \"%s\", key %s, flags 0x%02x%s",
1.21 djm 627: provider_path, sshkey_type(key), key->sk_flags,
628: (pin != NULL && *pin != '\0') ? " with-pin" : "");
1.1 djm 629:
630: if (sigp != NULL)
631: *sigp = NULL;
632: if (lenp != NULL)
633: *lenp = 0;
1.5 markus 634: type = sshkey_type_plain(key->type);
635: switch (type) {
1.15 naddy 636: #ifdef WITH_OPENSSL
1.5 markus 637: case KEY_ECDSA_SK:
1.7 markus 638: alg = SSH_SK_ECDSA;
639: break;
1.15 naddy 640: #endif /* WITH_OPENSSL */
1.5 markus 641: case KEY_ED25519_SK:
1.7 markus 642: alg = SSH_SK_ED25519;
1.5 markus 643: break;
644: default:
645: return SSH_ERR_INVALID_ARGUMENT;
646: }
1.1 djm 647: if (provider_path == NULL ||
648: key->sk_key_handle == NULL ||
649: key->sk_application == NULL || *key->sk_application == '\0') {
650: r = SSH_ERR_INVALID_ARGUMENT;
651: goto out;
652: }
653: if ((skp = sshsk_open(provider_path)) == NULL) {
654: r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
655: goto out;
656: }
1.35 djm 657: #ifdef DEBUG_SK
658: fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n",
659: __func__, key->sk_flags, key->sk_application);
660: fprintf(stderr, "%s: sk_key_handle:\n", __func__);
661: sshbuf_dump(key->sk_key_handle, stderr);
662: #endif
1.30 djm 663: if ((r = skp->sk_sign(alg, data, datalen, key->sk_application,
1.1 djm 664: sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
1.24 djm 665: key->sk_flags, pin, opts, &resp)) != 0) {
1.33 djm 666: debug_f("sk_sign failed with code %d", r);
1.23 djm 667: r = skerr_to_ssherr(r);
1.1 djm 668: goto out;
669: }
1.9 markus 670: /* Assemble signature */
671: if ((sig = sshbuf_new()) == NULL) {
672: r = SSH_ERR_ALLOC_FAIL;
673: goto out;
674: }
675: if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
1.33 djm 676: error_fr(r, "compose outer");
1.9 markus 677: goto out;
678: }
1.5 markus 679: switch (type) {
1.15 naddy 680: #ifdef WITH_OPENSSL
1.5 markus 681: case KEY_ECDSA_SK:
1.9 markus 682: if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
1.5 markus 683: goto out;
684: break;
1.15 naddy 685: #endif /* WITH_OPENSSL */
1.5 markus 686: case KEY_ED25519_SK:
1.9 markus 687: if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
1.5 markus 688: goto out;
689: break;
690: }
1.1 djm 691: #ifdef DEBUG_SK
1.5 markus 692: fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
693: __func__, resp->flags, resp->counter);
1.34 djm 694: fprintf(stderr, "%s: data to sign:\n", __func__);
695: sshbuf_dump_data(data, datalen, stderr);
1.1 djm 696: fprintf(stderr, "%s: sigbuf:\n", __func__);
697: sshbuf_dump(sig, stderr);
698: #endif
699: if (sigp != NULL) {
700: if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
701: r = SSH_ERR_ALLOC_FAIL;
702: goto out;
703: }
704: memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
705: }
706: if (lenp != NULL)
707: *lenp = sshbuf_len(sig);
708: /* success */
709: r = 0;
710: out:
1.24 djm 711: sshsk_free_options(opts);
1.1 djm 712: sshsk_free(skp);
713: sshsk_free_sign_response(resp);
714: sshbuf_free(sig);
715: sshbuf_free(inner_sig);
716: return r;
717: }
1.20 djm 718:
719: static void
720: sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
721: {
722: size_t i;
723:
724: if (nrks == 0 || rks == NULL)
725: return;
726: for (i = 0; i < nrks; i++) {
727: free(rks[i]->application);
1.38 djm 728: freezero(rks[i]->user_id, rks[i]->user_id_len);
1.20 djm 729: freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
730: freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
731: freezero(rks[i]->key.signature, rks[i]->key.signature_len);
732: freezero(rks[i]->key.attestation_cert,
733: rks[i]->key.attestation_cert_len);
734: freezero(rks[i], sizeof(**rks));
735: }
736: free(rks);
737: }
738:
1.36 djm 739: static void
740: sshsk_free_resident_key(struct sshsk_resident_key *srk)
741: {
742: if (srk == NULL)
743: return;
744: sshkey_free(srk->key);
745: freezero(srk->user_id, srk->user_id_len);
746: free(srk);
747: }
748:
749:
750: void
751: sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks)
752: {
753: size_t i;
754:
755: if (srks == NULL || nsrks == 0)
756: return;
757:
758: for (i = 0; i < nsrks; i++)
759: sshsk_free_resident_key(srks[i]);
760: free(srks);
761: }
762:
1.20 djm 763: int
1.24 djm 764: sshsk_load_resident(const char *provider_path, const char *device,
1.36 djm 765: const char *pin, u_int flags, struct sshsk_resident_key ***srksp,
766: size_t *nsrksp)
1.20 djm 767: {
768: struct sshsk_provider *skp = NULL;
769: int r = SSH_ERR_INTERNAL_ERROR;
770: struct sk_resident_key **rks = NULL;
1.36 djm 771: size_t i, nrks = 0, nsrks = 0;
772: struct sshkey *key = NULL;
773: struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp;
774: uint8_t sk_flags;
1.24 djm 775: struct sk_option **opts = NULL;
1.20 djm 776:
1.33 djm 777: debug_f("provider \"%s\"%s", provider_path,
1.20 djm 778: (pin != NULL && *pin != '\0') ? ", have-pin": "");
779:
1.36 djm 780: if (srksp == NULL || nsrksp == NULL)
1.20 djm 781: return SSH_ERR_INVALID_ARGUMENT;
1.36 djm 782: *srksp = NULL;
783: *nsrksp = 0;
1.20 djm 784:
1.24 djm 785: if ((r = make_options(device, NULL, &opts)) != 0)
786: goto out;
1.20 djm 787: if ((skp = sshsk_open(provider_path)) == NULL) {
788: r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
789: goto out;
790: }
1.24 djm 791: if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) {
1.27 naddy 792: error("Provider \"%s\" returned failure %d", provider_path, r);
1.23 djm 793: r = skerr_to_ssherr(r);
1.20 djm 794: goto out;
795: }
796: for (i = 0; i < nrks; i++) {
1.36 djm 797: debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu",
798: i, rks[i]->slot, rks[i]->alg, rks[i]->application,
799: rks[i]->user_id_len);
1.20 djm 800: /* XXX need better filter here */
801: if (strncmp(rks[i]->application, "ssh:", 4) != 0)
802: continue;
803: switch (rks[i]->alg) {
804: case SSH_SK_ECDSA:
805: case SSH_SK_ED25519:
806: break;
807: default:
808: continue;
809: }
1.36 djm 810: sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY;
1.31 djm 811: if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD))
1.36 djm 812: sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
1.20 djm 813: if ((r = sshsk_key_from_response(rks[i]->alg,
1.36 djm 814: rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0)
815: goto out;
816: if ((srk = calloc(1, sizeof(*srk))) == NULL) {
817: error_f("calloc failed");
818: r = SSH_ERR_ALLOC_FAIL;
1.20 djm 819: goto out;
1.36 djm 820: }
821: srk->key = key;
822: key = NULL; /* transferred */
823: if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) {
824: error_f("calloc failed");
825: r = SSH_ERR_ALLOC_FAIL;
826: goto out;
827: }
828: memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len);
829: srk->user_id_len = rks[i]->user_id_len;
830: if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
1.20 djm 831: sizeof(*tmp))) == NULL) {
1.33 djm 832: error_f("recallocarray failed");
1.20 djm 833: r = SSH_ERR_ALLOC_FAIL;
834: goto out;
835: }
1.36 djm 836: srks = tmp;
837: srks[nsrks++] = srk;
838: srk = NULL;
1.20 djm 839: /* XXX synthesise comment */
840: }
841: /* success */
1.36 djm 842: *srksp = srks;
843: *nsrksp = nsrks;
844: srks = NULL;
845: nsrks = 0;
1.20 djm 846: r = 0;
847: out:
1.24 djm 848: sshsk_free_options(opts);
1.20 djm 849: sshsk_free(skp);
850: sshsk_free_sk_resident_keys(rks, nrks);
851: sshkey_free(key);
1.36 djm 852: sshsk_free_resident_key(srk);
853: sshsk_free_resident_keys(srks, nsrks);
1.20 djm 854: return r;
855: }
856: