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