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