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