1*b8914b02Sdjm /* $OpenBSD: sk-usbhid.c,v 1.36 2021/12/02 23:23:13 djm Exp $ */ 2094c80e0Sdjm /* 3094c80e0Sdjm * Copyright (c) 2019 Markus Friedl 401bb7af0Sdjm * Copyright (c) 2020 Pedro Martelletto 5094c80e0Sdjm * 6094c80e0Sdjm * Permission to use, copy, modify, and distribute this software for any 7094c80e0Sdjm * purpose with or without fee is hereby granted, provided that the above 8094c80e0Sdjm * copyright notice and this permission notice appear in all copies. 9094c80e0Sdjm * 10094c80e0Sdjm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11094c80e0Sdjm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12094c80e0Sdjm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13094c80e0Sdjm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14094c80e0Sdjm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15094c80e0Sdjm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16094c80e0Sdjm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17094c80e0Sdjm */ 18094c80e0Sdjm 19094c80e0Sdjm #include <stdint.h> 20094c80e0Sdjm #include <stdlib.h> 21094c80e0Sdjm #include <string.h> 22094c80e0Sdjm #include <stdio.h> 23094c80e0Sdjm #include <stddef.h> 24094c80e0Sdjm #include <stdarg.h> 253ce2af41Sdjm #include <sha2.h> 26719d3f82Sdjm #include <time.h> 27094c80e0Sdjm 28f8cd6cb1Snaddy #ifdef WITH_OPENSSL 29094c80e0Sdjm #include <openssl/opensslv.h> 30094c80e0Sdjm #include <openssl/crypto.h> 31094c80e0Sdjm #include <openssl/bn.h> 32094c80e0Sdjm #include <openssl/ec.h> 33094c80e0Sdjm #include <openssl/ecdsa.h> 343ce2af41Sdjm #include <openssl/evp.h> 35f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 36094c80e0Sdjm 37094c80e0Sdjm #include <fido.h> 381ac4a90aSdjm #include <fido/credman.h> 39094c80e0Sdjm 40094c80e0Sdjm #ifndef SK_STANDALONE 41094c80e0Sdjm # include "log.h" 42094c80e0Sdjm # include "xmalloc.h" 4301bb7af0Sdjm # include "misc.h" 44b0297854Sdjm /* 45b0297854Sdjm * If building as part of OpenSSH, then rename exported functions. 46b0297854Sdjm * This must be done before including sk-api.h. 47b0297854Sdjm */ 48b0297854Sdjm # define sk_api_version ssh_sk_api_version 49b0297854Sdjm # define sk_enroll ssh_sk_enroll 50b0297854Sdjm # define sk_sign ssh_sk_sign 51b0297854Sdjm # define sk_load_resident_keys ssh_sk_load_resident_keys 52b0297854Sdjm #endif /* !SK_STANDALONE */ 53b0297854Sdjm 54b0297854Sdjm #include "sk-api.h" 55094c80e0Sdjm 56094c80e0Sdjm /* #define SK_DEBUG 1 */ 57094c80e0Sdjm 58fb0a6bcaSdjm #ifdef SK_DEBUG 59fb0a6bcaSdjm #define SSH_FIDO_INIT_ARG FIDO_DEBUG 60fb0a6bcaSdjm #else 61fb0a6bcaSdjm #define SSH_FIDO_INIT_ARG 0 62fb0a6bcaSdjm #endif 63fb0a6bcaSdjm 6401bb7af0Sdjm #define MAX_FIDO_DEVICES 8 6501bb7af0Sdjm #define FIDO_POLL_MS 50 6601bb7af0Sdjm #define SELECT_MS 15000 6701bb7af0Sdjm #define POLL_SLEEP_NS 200000000 68094c80e0Sdjm 69094c80e0Sdjm /* Compatibility with OpenSSH 1.0.x */ 70094c80e0Sdjm #if (OPENSSL_VERSION_NUMBER < 0x10100000L) 71094c80e0Sdjm #define ECDSA_SIG_get0(sig, pr, ps) \ 72094c80e0Sdjm do { \ 73094c80e0Sdjm (*pr) = sig->r; \ 74094c80e0Sdjm (*ps) = sig->s; \ 75094c80e0Sdjm } while (0) 76094c80e0Sdjm #endif 7718b3d906Sdjm #ifndef FIDO_ERR_OPERATION_DENIED 7818b3d906Sdjm #define FIDO_ERR_OPERATION_DENIED 0x27 7918b3d906Sdjm #endif 80094c80e0Sdjm 8101bb7af0Sdjm struct sk_usbhid { 8201bb7af0Sdjm fido_dev_t *dev; 8301bb7af0Sdjm char *path; 8401bb7af0Sdjm }; 8501bb7af0Sdjm 86094c80e0Sdjm /* Return the version of the middleware API */ 87094c80e0Sdjm uint32_t sk_api_version(void); 88094c80e0Sdjm 89094c80e0Sdjm /* Enroll a U2F key (private key generation) */ 90a0caf565Sdjm int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, 912db06755Sdjm const char *application, uint8_t flags, const char *pin, 92a0caf565Sdjm struct sk_option **options, struct sk_enroll_response **enroll_response); 93094c80e0Sdjm 94094c80e0Sdjm /* Sign a challenge */ 9574d7b7bdSdjm int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len, 96094c80e0Sdjm const char *application, const uint8_t *key_handle, size_t key_handle_len, 97a0caf565Sdjm uint8_t flags, const char *pin, struct sk_option **options, 98a0caf565Sdjm struct sk_sign_response **sign_response); 99094c80e0Sdjm 1001ac4a90aSdjm /* Load resident keys */ 101a0caf565Sdjm int sk_load_resident_keys(const char *pin, struct sk_option **options, 1021ac4a90aSdjm struct sk_resident_key ***rks, size_t *nrks); 1031ac4a90aSdjm 104094c80e0Sdjm static void skdebug(const char *func, const char *fmt, ...) 105094c80e0Sdjm __attribute__((__format__ (printf, 2, 3))); 106094c80e0Sdjm 107094c80e0Sdjm static void 108094c80e0Sdjm skdebug(const char *func, const char *fmt, ...) 109094c80e0Sdjm { 110094c80e0Sdjm #if !defined(SK_STANDALONE) 111094c80e0Sdjm char *msg; 112094c80e0Sdjm va_list ap; 113094c80e0Sdjm 114094c80e0Sdjm va_start(ap, fmt); 115094c80e0Sdjm xvasprintf(&msg, fmt, ap); 116094c80e0Sdjm va_end(ap); 11759959935Sdjm debug("%s: %s", func, msg); 118094c80e0Sdjm free(msg); 119094c80e0Sdjm #elif defined(SK_DEBUG) 120094c80e0Sdjm va_list ap; 121094c80e0Sdjm 122094c80e0Sdjm va_start(ap, fmt); 123094c80e0Sdjm fprintf(stderr, "%s: ", func); 124094c80e0Sdjm vfprintf(stderr, fmt, ap); 125094c80e0Sdjm fputc('\n', stderr); 126094c80e0Sdjm va_end(ap); 127094c80e0Sdjm #else 128094c80e0Sdjm (void)func; /* XXX */ 129094c80e0Sdjm (void)fmt; /* XXX */ 130094c80e0Sdjm #endif 131094c80e0Sdjm } 132094c80e0Sdjm 133094c80e0Sdjm uint32_t 134094c80e0Sdjm sk_api_version(void) 135094c80e0Sdjm { 136b0297854Sdjm return SSH_SK_VERSION_MAJOR; 137094c80e0Sdjm } 138094c80e0Sdjm 13901bb7af0Sdjm static struct sk_usbhid * 14001bb7af0Sdjm sk_open(const char *path) 141094c80e0Sdjm { 14201bb7af0Sdjm struct sk_usbhid *sk; 143094c80e0Sdjm int r; 144094c80e0Sdjm 14501bb7af0Sdjm if (path == NULL) { 14601bb7af0Sdjm skdebug(__func__, "path == NULL"); 14701bb7af0Sdjm return NULL; 148094c80e0Sdjm } 14901bb7af0Sdjm if ((sk = calloc(1, sizeof(*sk))) == NULL) { 15001bb7af0Sdjm skdebug(__func__, "calloc sk failed"); 15101bb7af0Sdjm return NULL; 15201bb7af0Sdjm } 15301bb7af0Sdjm if ((sk->path = strdup(path)) == NULL) { 15401bb7af0Sdjm skdebug(__func__, "strdup path failed"); 15501bb7af0Sdjm free(sk); 15601bb7af0Sdjm return NULL; 15701bb7af0Sdjm } 15801bb7af0Sdjm if ((sk->dev = fido_dev_new()) == NULL) { 15901bb7af0Sdjm skdebug(__func__, "fido_dev_new failed"); 16001bb7af0Sdjm free(sk->path); 16101bb7af0Sdjm free(sk); 16201bb7af0Sdjm return NULL; 16301bb7af0Sdjm } 16401bb7af0Sdjm if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) { 16501bb7af0Sdjm skdebug(__func__, "fido_dev_open %s failed: %s", sk->path, 166094c80e0Sdjm fido_strerr(r)); 16701bb7af0Sdjm fido_dev_free(&sk->dev); 16801bb7af0Sdjm free(sk->path); 16901bb7af0Sdjm free(sk); 17001bb7af0Sdjm return NULL; 171094c80e0Sdjm } 17201bb7af0Sdjm return sk; 173094c80e0Sdjm } 174094c80e0Sdjm 17501bb7af0Sdjm static void 17601bb7af0Sdjm sk_close(struct sk_usbhid *sk) 17701bb7af0Sdjm { 17801bb7af0Sdjm if (sk == NULL) 17901bb7af0Sdjm return; 18001bb7af0Sdjm fido_dev_cancel(sk->dev); /* cancel any pending operation */ 18101bb7af0Sdjm fido_dev_close(sk->dev); 18201bb7af0Sdjm fido_dev_free(&sk->dev); 18301bb7af0Sdjm free(sk->path); 18401bb7af0Sdjm free(sk); 18501bb7af0Sdjm } 18601bb7af0Sdjm 18701bb7af0Sdjm static struct sk_usbhid ** 18801bb7af0Sdjm sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen) 18901bb7af0Sdjm { 19001bb7af0Sdjm const fido_dev_info_t *di; 19101bb7af0Sdjm struct sk_usbhid **skv; 19201bb7af0Sdjm size_t i; 19301bb7af0Sdjm 19401bb7af0Sdjm *nopen = 0; 19501bb7af0Sdjm if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) { 19601bb7af0Sdjm skdebug(__func__, "calloc skv failed"); 19701bb7af0Sdjm return NULL; 19801bb7af0Sdjm } 19901bb7af0Sdjm for (i = 0; i < ndevs; i++) { 20001bb7af0Sdjm if ((di = fido_dev_info_ptr(devlist, i)) == NULL) 20101bb7af0Sdjm skdebug(__func__, "fido_dev_info_ptr failed"); 20201bb7af0Sdjm else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL) 20301bb7af0Sdjm skdebug(__func__, "sk_open failed"); 20401bb7af0Sdjm else 20501bb7af0Sdjm (*nopen)++; 20601bb7af0Sdjm } 20701bb7af0Sdjm if (*nopen == 0) { 20801bb7af0Sdjm for (i = 0; i < ndevs; i++) 20901bb7af0Sdjm sk_close(skv[i]); 21001bb7af0Sdjm free(skv); 21101bb7af0Sdjm skv = NULL; 21201bb7af0Sdjm } 21301bb7af0Sdjm 21401bb7af0Sdjm return skv; 21501bb7af0Sdjm } 21601bb7af0Sdjm 21701bb7af0Sdjm static void 21801bb7af0Sdjm sk_closev(struct sk_usbhid **skv, size_t nsk) 21901bb7af0Sdjm { 22001bb7af0Sdjm size_t i; 22101bb7af0Sdjm 22201bb7af0Sdjm for (i = 0; i < nsk; i++) 22301bb7af0Sdjm sk_close(skv[i]); 22401bb7af0Sdjm free(skv); 22501bb7af0Sdjm } 22601bb7af0Sdjm 227094c80e0Sdjm static int 22801bb7af0Sdjm sk_touch_begin(struct sk_usbhid **skv, size_t nsk) 22901bb7af0Sdjm { 23001bb7af0Sdjm size_t i, ok = 0; 23101bb7af0Sdjm int r; 23201bb7af0Sdjm 23301bb7af0Sdjm for (i = 0; i < nsk; i++) 23401bb7af0Sdjm if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK) 23501bb7af0Sdjm skdebug(__func__, "fido_dev_get_touch_begin %s failed:" 23601bb7af0Sdjm " %s", skv[i]->path, fido_strerr(r)); 23701bb7af0Sdjm else 23801bb7af0Sdjm ok++; 23901bb7af0Sdjm 24001bb7af0Sdjm return ok ? 0 : -1; 24101bb7af0Sdjm } 24201bb7af0Sdjm 24301bb7af0Sdjm static int 24401bb7af0Sdjm sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx) 24501bb7af0Sdjm { 24601bb7af0Sdjm struct timespec ts_pause; 24701bb7af0Sdjm size_t npoll, i; 24801bb7af0Sdjm int r; 24901bb7af0Sdjm 25001bb7af0Sdjm ts_pause.tv_sec = 0; 25101bb7af0Sdjm ts_pause.tv_nsec = POLL_SLEEP_NS; 25201bb7af0Sdjm nanosleep(&ts_pause, NULL); 25301bb7af0Sdjm npoll = nsk; 25401bb7af0Sdjm for (i = 0; i < nsk; i++) { 25501bb7af0Sdjm if (skv[i] == NULL) 25601bb7af0Sdjm continue; /* device discarded */ 25701bb7af0Sdjm skdebug(__func__, "polling %s", skv[i]->path); 25801bb7af0Sdjm if ((r = fido_dev_get_touch_status(skv[i]->dev, touch, 25901bb7af0Sdjm FIDO_POLL_MS)) != FIDO_OK) { 26001bb7af0Sdjm skdebug(__func__, "fido_dev_get_touch_status %s: %s", 26101bb7af0Sdjm skv[i]->path, fido_strerr(r)); 26201bb7af0Sdjm sk_close(skv[i]); /* discard device */ 26301bb7af0Sdjm skv[i] = NULL; 26401bb7af0Sdjm if (--npoll == 0) { 26501bb7af0Sdjm skdebug(__func__, "no device left to poll"); 26601bb7af0Sdjm return -1; 26701bb7af0Sdjm } 26801bb7af0Sdjm } else if (*touch) { 26901bb7af0Sdjm *idx = i; 27001bb7af0Sdjm return 0; 27101bb7af0Sdjm } 27201bb7af0Sdjm } 27301bb7af0Sdjm *touch = 0; 27401bb7af0Sdjm return 0; 27501bb7af0Sdjm } 27601bb7af0Sdjm 27701bb7af0Sdjm /* Calculate SHA256(m) */ 27801bb7af0Sdjm static int 27901bb7af0Sdjm sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen) 28001bb7af0Sdjm { 28101bb7af0Sdjm #ifdef WITH_OPENSSL 28201bb7af0Sdjm u_int mdlen; 2839424a71bSdjm #else 2849424a71bSdjm SHA2_CTX ctx; 28501bb7af0Sdjm #endif 28601bb7af0Sdjm 28701bb7af0Sdjm if (dlen != 32) 28801bb7af0Sdjm return -1; 28901bb7af0Sdjm #ifdef WITH_OPENSSL 29001bb7af0Sdjm mdlen = dlen; 29101bb7af0Sdjm if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL)) 29201bb7af0Sdjm return -1; 29301bb7af0Sdjm #else 2949424a71bSdjm SHA256Init(&ctx); 2959424a71bSdjm SHA256Update(&ctx, (const uint8_t *)m, mlen); 2969424a71bSdjm SHA256Final(d, &ctx); 29701bb7af0Sdjm #endif 29801bb7af0Sdjm return 0; 29901bb7af0Sdjm } 30001bb7af0Sdjm 30101bb7af0Sdjm /* Check if the specified key handle exists on a given sk. */ 30201bb7af0Sdjm static int 30301bb7af0Sdjm sk_try(const struct sk_usbhid *sk, const char *application, 30401bb7af0Sdjm const uint8_t *key_handle, size_t key_handle_len) 305094c80e0Sdjm { 306094c80e0Sdjm fido_assert_t *assert = NULL; 30701bb7af0Sdjm /* generate an invalid signature on FIDO2 tokens */ 30801bb7af0Sdjm const char *data = ""; 30901bb7af0Sdjm uint8_t message[32]; 310094c80e0Sdjm int r = FIDO_ERR_INTERNAL; 311094c80e0Sdjm 31201bb7af0Sdjm if (sha256_mem(data, strlen(data), message, sizeof(message)) != 0) { 31301bb7af0Sdjm skdebug(__func__, "hash message failed"); 31401bb7af0Sdjm goto out; 31501bb7af0Sdjm } 316094c80e0Sdjm if ((assert = fido_assert_new()) == NULL) { 317094c80e0Sdjm skdebug(__func__, "fido_assert_new failed"); 318094c80e0Sdjm goto out; 319094c80e0Sdjm } 320094c80e0Sdjm if ((r = fido_assert_set_clientdata_hash(assert, message, 32101bb7af0Sdjm sizeof(message))) != FIDO_OK) { 322094c80e0Sdjm skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 323094c80e0Sdjm fido_strerr(r)); 324094c80e0Sdjm goto out; 325094c80e0Sdjm } 326094c80e0Sdjm if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 327094c80e0Sdjm skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 328094c80e0Sdjm goto out; 329094c80e0Sdjm } 330094c80e0Sdjm if ((r = fido_assert_allow_cred(assert, key_handle, 331094c80e0Sdjm key_handle_len)) != FIDO_OK) { 332094c80e0Sdjm skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 333094c80e0Sdjm goto out; 334094c80e0Sdjm } 335094c80e0Sdjm if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { 336094c80e0Sdjm skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); 337094c80e0Sdjm goto out; 338094c80e0Sdjm } 33901bb7af0Sdjm r = fido_dev_get_assert(sk->dev, assert, NULL); 340094c80e0Sdjm skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 3417cf8e58dSdjm if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { 3427cf8e58dSdjm /* U2F tokens may return this */ 3437cf8e58dSdjm r = FIDO_OK; 3447cf8e58dSdjm } 345094c80e0Sdjm out: 346094c80e0Sdjm fido_assert_free(&assert); 347094c80e0Sdjm 348094c80e0Sdjm return r != FIDO_OK ? -1 : 0; 349094c80e0Sdjm } 350094c80e0Sdjm 351c5365f21Sdjm static int 352c5365f21Sdjm check_sk_options(fido_dev_t *dev, const char *opt, int *ret) 353c5365f21Sdjm { 354c5365f21Sdjm fido_cbor_info_t *info; 355c5365f21Sdjm char * const *name; 356c5365f21Sdjm const bool *value; 357c5365f21Sdjm size_t len, i; 358c5365f21Sdjm int r; 359c5365f21Sdjm 360c5365f21Sdjm *ret = -1; 361c5365f21Sdjm 362c5365f21Sdjm if (!fido_dev_is_fido2(dev)) { 363c5365f21Sdjm skdebug(__func__, "device is not fido2"); 364c5365f21Sdjm return 0; 365c5365f21Sdjm } 366c5365f21Sdjm if ((info = fido_cbor_info_new()) == NULL) { 367c5365f21Sdjm skdebug(__func__, "fido_cbor_info_new failed"); 368c5365f21Sdjm return -1; 369c5365f21Sdjm } 370c5365f21Sdjm if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) { 371c5365f21Sdjm skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r)); 372c5365f21Sdjm fido_cbor_info_free(&info); 373c5365f21Sdjm return -1; 374c5365f21Sdjm } 375c5365f21Sdjm name = fido_cbor_info_options_name_ptr(info); 376c5365f21Sdjm value = fido_cbor_info_options_value_ptr(info); 377c5365f21Sdjm len = fido_cbor_info_options_len(info); 378c5365f21Sdjm for (i = 0; i < len; i++) { 379c5365f21Sdjm if (!strcmp(name[i], opt)) { 380c5365f21Sdjm *ret = value[i]; 381c5365f21Sdjm break; 382c5365f21Sdjm } 383c5365f21Sdjm } 384c5365f21Sdjm fido_cbor_info_free(&info); 385c5365f21Sdjm if (*ret == -1) 386c5365f21Sdjm skdebug(__func__, "option %s is unknown", opt); 387c5365f21Sdjm else 388c5365f21Sdjm skdebug(__func__, "option %s is %s", opt, *ret ? "on" : "off"); 389c5365f21Sdjm 390c5365f21Sdjm return 0; 391c5365f21Sdjm } 392c5365f21Sdjm 39301bb7af0Sdjm static struct sk_usbhid * 39401bb7af0Sdjm sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs, 39501bb7af0Sdjm const char *application, const uint8_t *key_handle, size_t key_handle_len) 396094c80e0Sdjm { 39701bb7af0Sdjm struct sk_usbhid **skv, *sk; 39801bb7af0Sdjm size_t skvcnt, i; 399*b8914b02Sdjm int internal_uv; 400094c80e0Sdjm 40101bb7af0Sdjm if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) { 40201bb7af0Sdjm skdebug(__func__, "sk_openv failed"); 403a0caf565Sdjm return NULL; 404a0caf565Sdjm } 405*b8914b02Sdjm if (skvcnt == 1 && check_sk_options(skv[0]->dev, "uv", 406*b8914b02Sdjm &internal_uv) == 0 && internal_uv != -1) { 4078c966c57Sdjm sk = skv[0]; 4088c966c57Sdjm skv[0] = NULL; 4098c966c57Sdjm goto out; 4108c966c57Sdjm } 41101bb7af0Sdjm sk = NULL; 4128c966c57Sdjm for (i = 0; i < skvcnt; i++) { 41301bb7af0Sdjm if (sk_try(skv[i], application, key_handle, 41401bb7af0Sdjm key_handle_len) == 0) { 41501bb7af0Sdjm sk = skv[i]; 41601bb7af0Sdjm skv[i] = NULL; 41701bb7af0Sdjm skdebug(__func__, "found key in %s", sk->path); 41801bb7af0Sdjm break; 41901bb7af0Sdjm } 4208c966c57Sdjm } 4218c966c57Sdjm out: 42201bb7af0Sdjm sk_closev(skv, skvcnt); 42301bb7af0Sdjm return sk; 42401bb7af0Sdjm } 42501bb7af0Sdjm 42601bb7af0Sdjm static struct sk_usbhid * 42701bb7af0Sdjm sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs) 42801bb7af0Sdjm { 42901bb7af0Sdjm struct sk_usbhid **skv, *sk; 43001bb7af0Sdjm struct timeval tv_start, tv_now, tv_delta; 43101bb7af0Sdjm size_t skvcnt, idx; 43201bb7af0Sdjm int touch, ms_remain; 43301bb7af0Sdjm 43401bb7af0Sdjm if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) { 43501bb7af0Sdjm skdebug(__func__, "sk_openv failed"); 436a0caf565Sdjm return NULL; 437a0caf565Sdjm } 43801bb7af0Sdjm sk = NULL; 43901bb7af0Sdjm if (skvcnt < 2) { 44001bb7af0Sdjm if (skvcnt == 1) { 44101bb7af0Sdjm /* single candidate */ 44201bb7af0Sdjm sk = skv[0]; 44301bb7af0Sdjm skv[0] = NULL; 444a0caf565Sdjm } 44501bb7af0Sdjm goto out; 44601bb7af0Sdjm } 44701bb7af0Sdjm if (sk_touch_begin(skv, skvcnt) == -1) { 44801bb7af0Sdjm skdebug(__func__, "sk_touch_begin failed"); 44901bb7af0Sdjm goto out; 45001bb7af0Sdjm } 45101bb7af0Sdjm monotime_tv(&tv_start); 45201bb7af0Sdjm do { 45301bb7af0Sdjm if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) { 45401bb7af0Sdjm skdebug(__func__, "sk_touch_poll failed"); 45501bb7af0Sdjm goto out; 45601bb7af0Sdjm } 45701bb7af0Sdjm if (touch) { 45801bb7af0Sdjm sk = skv[idx]; 45901bb7af0Sdjm skv[idx] = NULL; 46001bb7af0Sdjm goto out; 46101bb7af0Sdjm } 46201bb7af0Sdjm monotime_tv(&tv_now); 46301bb7af0Sdjm timersub(&tv_now, &tv_start, &tv_delta); 46401bb7af0Sdjm ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 - 46501bb7af0Sdjm tv_delta.tv_usec / 1000; 46601bb7af0Sdjm } while (ms_remain >= FIDO_POLL_MS); 46701bb7af0Sdjm skdebug(__func__, "timeout"); 46801bb7af0Sdjm out: 46901bb7af0Sdjm sk_closev(skv, skvcnt); 47001bb7af0Sdjm return sk; 47101bb7af0Sdjm } 47201bb7af0Sdjm 47301bb7af0Sdjm static struct sk_usbhid * 47401bb7af0Sdjm sk_probe(const char *application, const uint8_t *key_handle, 47501bb7af0Sdjm size_t key_handle_len) 47601bb7af0Sdjm { 47701bb7af0Sdjm struct sk_usbhid *sk; 47801bb7af0Sdjm fido_dev_info_t *devlist; 47901bb7af0Sdjm size_t ndevs; 48001bb7af0Sdjm int r; 481a0caf565Sdjm 482094c80e0Sdjm if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { 483094c80e0Sdjm skdebug(__func__, "fido_dev_info_new failed"); 48401bb7af0Sdjm return NULL; 485094c80e0Sdjm } 486094c80e0Sdjm if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, 48701bb7af0Sdjm &ndevs)) != FIDO_OK) { 48801bb7af0Sdjm skdebug(__func__, "fido_dev_info_manifest failed: %s", 48901bb7af0Sdjm fido_strerr(r)); 490094c80e0Sdjm fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 49101bb7af0Sdjm return NULL; 49201bb7af0Sdjm } 49301bb7af0Sdjm skdebug(__func__, "%zu device(s) detected", ndevs); 49401bb7af0Sdjm if (ndevs == 0) { 49501bb7af0Sdjm sk = NULL; 49601bb7af0Sdjm } else if (application != NULL && key_handle != NULL) { 49701bb7af0Sdjm skdebug(__func__, "selecting sk by cred"); 49801bb7af0Sdjm sk = sk_select_by_cred(devlist, ndevs, application, key_handle, 49901bb7af0Sdjm key_handle_len); 50001bb7af0Sdjm } else { 50101bb7af0Sdjm skdebug(__func__, "selecting sk by touch"); 50201bb7af0Sdjm sk = sk_select_by_touch(devlist, ndevs); 50301bb7af0Sdjm } 50401bb7af0Sdjm fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 50501bb7af0Sdjm return sk; 506094c80e0Sdjm } 507094c80e0Sdjm 508f8cd6cb1Snaddy #ifdef WITH_OPENSSL 509094c80e0Sdjm /* 510094c80e0Sdjm * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, 511094c80e0Sdjm * but the API expects a SEC1 octet string. 512094c80e0Sdjm */ 513094c80e0Sdjm static int 5141ac4a90aSdjm pack_public_key_ecdsa(const fido_cred_t *cred, 5151ac4a90aSdjm struct sk_enroll_response *response) 516094c80e0Sdjm { 517094c80e0Sdjm const uint8_t *ptr; 518094c80e0Sdjm BIGNUM *x = NULL, *y = NULL; 519094c80e0Sdjm EC_POINT *q = NULL; 520094c80e0Sdjm EC_GROUP *g = NULL; 521094c80e0Sdjm int ret = -1; 522094c80e0Sdjm 523094c80e0Sdjm response->public_key = NULL; 524094c80e0Sdjm response->public_key_len = 0; 525094c80e0Sdjm 52607b718edSdjm if ((x = BN_new()) == NULL || 52707b718edSdjm (y = BN_new()) == NULL || 528094c80e0Sdjm (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || 529094c80e0Sdjm (q = EC_POINT_new(g)) == NULL) { 530094c80e0Sdjm skdebug(__func__, "libcrypto setup failed"); 531094c80e0Sdjm goto out; 532094c80e0Sdjm } 533094c80e0Sdjm if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 534094c80e0Sdjm skdebug(__func__, "fido_cred_pubkey_ptr failed"); 535094c80e0Sdjm goto out; 536094c80e0Sdjm } 537094c80e0Sdjm if (fido_cred_pubkey_len(cred) != 64) { 538094c80e0Sdjm skdebug(__func__, "bad fido_cred_pubkey_len %zu", 539094c80e0Sdjm fido_cred_pubkey_len(cred)); 540094c80e0Sdjm goto out; 541094c80e0Sdjm } 542094c80e0Sdjm 543094c80e0Sdjm if (BN_bin2bn(ptr, 32, x) == NULL || 544094c80e0Sdjm BN_bin2bn(ptr + 32, 32, y) == NULL) { 545094c80e0Sdjm skdebug(__func__, "BN_bin2bn failed"); 546094c80e0Sdjm goto out; 547094c80e0Sdjm } 54807b718edSdjm if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { 549094c80e0Sdjm skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); 550094c80e0Sdjm goto out; 551094c80e0Sdjm } 552094c80e0Sdjm response->public_key_len = EC_POINT_point2oct(g, q, 55307b718edSdjm POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); 554094c80e0Sdjm if (response->public_key_len == 0 || response->public_key_len > 2048) { 555094c80e0Sdjm skdebug(__func__, "bad pubkey length %zu", 556094c80e0Sdjm response->public_key_len); 557094c80e0Sdjm goto out; 558094c80e0Sdjm } 559094c80e0Sdjm if ((response->public_key = malloc(response->public_key_len)) == NULL) { 560094c80e0Sdjm skdebug(__func__, "malloc pubkey failed"); 561094c80e0Sdjm goto out; 562094c80e0Sdjm } 563094c80e0Sdjm if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, 56407b718edSdjm response->public_key, response->public_key_len, NULL) == 0) { 565094c80e0Sdjm skdebug(__func__, "EC_POINT_point2oct failed"); 566094c80e0Sdjm goto out; 567094c80e0Sdjm } 568094c80e0Sdjm /* success */ 569094c80e0Sdjm ret = 0; 570094c80e0Sdjm out: 571094c80e0Sdjm if (ret != 0 && response->public_key != NULL) { 572094c80e0Sdjm memset(response->public_key, 0, response->public_key_len); 573094c80e0Sdjm free(response->public_key); 574094c80e0Sdjm response->public_key = NULL; 575094c80e0Sdjm } 576094c80e0Sdjm EC_POINT_free(q); 577094c80e0Sdjm EC_GROUP_free(g); 57807b718edSdjm BN_clear_free(x); 57907b718edSdjm BN_clear_free(y); 580094c80e0Sdjm return ret; 581094c80e0Sdjm } 582f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 583094c80e0Sdjm 584094c80e0Sdjm static int 5851ac4a90aSdjm pack_public_key_ed25519(const fido_cred_t *cred, 5861ac4a90aSdjm struct sk_enroll_response *response) 587094c80e0Sdjm { 588094c80e0Sdjm const uint8_t *ptr; 589094c80e0Sdjm size_t len; 590094c80e0Sdjm int ret = -1; 591094c80e0Sdjm 592094c80e0Sdjm response->public_key = NULL; 593094c80e0Sdjm response->public_key_len = 0; 594094c80e0Sdjm 595094c80e0Sdjm if ((len = fido_cred_pubkey_len(cred)) != 32) { 596094c80e0Sdjm skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); 597094c80e0Sdjm goto out; 598094c80e0Sdjm } 599094c80e0Sdjm if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 600094c80e0Sdjm skdebug(__func__, "fido_cred_pubkey_ptr failed"); 601094c80e0Sdjm goto out; 602094c80e0Sdjm } 603094c80e0Sdjm response->public_key_len = len; 604094c80e0Sdjm if ((response->public_key = malloc(response->public_key_len)) == NULL) { 605094c80e0Sdjm skdebug(__func__, "malloc pubkey failed"); 606094c80e0Sdjm goto out; 607094c80e0Sdjm } 608094c80e0Sdjm memcpy(response->public_key, ptr, len); 609094c80e0Sdjm ret = 0; 610094c80e0Sdjm out: 611094c80e0Sdjm if (ret != 0) 612094c80e0Sdjm free(response->public_key); 613094c80e0Sdjm return ret; 614094c80e0Sdjm } 615094c80e0Sdjm 616094c80e0Sdjm static int 617a0caf565Sdjm pack_public_key(uint32_t alg, const fido_cred_t *cred, 6181ac4a90aSdjm struct sk_enroll_response *response) 619094c80e0Sdjm { 620094c80e0Sdjm switch(alg) { 621f8cd6cb1Snaddy #ifdef WITH_OPENSSL 622b0297854Sdjm case SSH_SK_ECDSA: 623094c80e0Sdjm return pack_public_key_ecdsa(cred, response); 624f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 625b0297854Sdjm case SSH_SK_ED25519: 626094c80e0Sdjm return pack_public_key_ed25519(cred, response); 627094c80e0Sdjm default: 628094c80e0Sdjm return -1; 629094c80e0Sdjm } 630094c80e0Sdjm } 631094c80e0Sdjm 632480af03fSdjm static int 633480af03fSdjm fidoerr_to_skerr(int fidoerr) 634480af03fSdjm { 635480af03fSdjm switch (fidoerr) { 636480af03fSdjm case FIDO_ERR_UNSUPPORTED_OPTION: 637b0297854Sdjm case FIDO_ERR_UNSUPPORTED_ALGORITHM: 638480af03fSdjm return SSH_SK_ERR_UNSUPPORTED; 639480af03fSdjm case FIDO_ERR_PIN_REQUIRED: 640480af03fSdjm case FIDO_ERR_PIN_INVALID: 64118b3d906Sdjm case FIDO_ERR_OPERATION_DENIED: 642480af03fSdjm return SSH_SK_ERR_PIN_REQUIRED; 643480af03fSdjm default: 644480af03fSdjm return -1; 645480af03fSdjm } 646480af03fSdjm } 647480af03fSdjm 648a0caf565Sdjm static int 649a0caf565Sdjm check_enroll_options(struct sk_option **options, char **devicep, 650a0caf565Sdjm uint8_t *user_id, size_t user_id_len) 651a0caf565Sdjm { 652a0caf565Sdjm size_t i; 653a0caf565Sdjm 654a0caf565Sdjm if (options == NULL) 655a0caf565Sdjm return 0; 656a0caf565Sdjm for (i = 0; options[i] != NULL; i++) { 657a0caf565Sdjm if (strcmp(options[i]->name, "device") == 0) { 658a0caf565Sdjm if ((*devicep = strdup(options[i]->value)) == NULL) { 659a0caf565Sdjm skdebug(__func__, "strdup device failed"); 660a0caf565Sdjm return -1; 661a0caf565Sdjm } 662a0caf565Sdjm skdebug(__func__, "requested device %s", *devicep); 6631a9bfc1cSdjm } else if (strcmp(options[i]->name, "user") == 0) { 664a0caf565Sdjm if (strlcpy(user_id, options[i]->value, user_id_len) >= 665a0caf565Sdjm user_id_len) { 666a0caf565Sdjm skdebug(__func__, "user too long"); 667a0caf565Sdjm return -1; 668a0caf565Sdjm } 669a0caf565Sdjm skdebug(__func__, "requested user %s", 670a0caf565Sdjm (char *)user_id); 671a0caf565Sdjm } else { 672a0caf565Sdjm skdebug(__func__, "requested unsupported option %s", 673a0caf565Sdjm options[i]->name); 674a0caf565Sdjm if (options[i]->required) { 675a0caf565Sdjm skdebug(__func__, "unknown required option"); 676a0caf565Sdjm return -1; 677a0caf565Sdjm } 678a0caf565Sdjm } 679a0caf565Sdjm } 680a0caf565Sdjm return 0; 681a0caf565Sdjm } 682a0caf565Sdjm 683094c80e0Sdjm int 684a0caf565Sdjm sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, 6852db06755Sdjm const char *application, uint8_t flags, const char *pin, 686a0caf565Sdjm struct sk_option **options, struct sk_enroll_response **enroll_response) 687094c80e0Sdjm { 688094c80e0Sdjm fido_cred_t *cred = NULL; 689094c80e0Sdjm const uint8_t *ptr; 6905e70647bSdjm uint8_t user_id[32], chall_hash[32]; 69101bb7af0Sdjm struct sk_usbhid *sk = NULL; 692094c80e0Sdjm struct sk_enroll_response *response = NULL; 693094c80e0Sdjm size_t len; 6944a0fa473Sdjm int credprot; 69518b3d906Sdjm int internal_uv; 696094c80e0Sdjm int cose_alg; 697480af03fSdjm int ret = SSH_SK_ERR_GENERAL; 698094c80e0Sdjm int r; 699094c80e0Sdjm char *device = NULL; 700094c80e0Sdjm 701fb0a6bcaSdjm fido_init(SSH_FIDO_INIT_ARG); 702fb0a6bcaSdjm 70335f0234cSmarkus if (enroll_response == NULL) { 70435f0234cSmarkus skdebug(__func__, "enroll_response == NULL"); 705094c80e0Sdjm goto out; 706094c80e0Sdjm } 70701bb7af0Sdjm *enroll_response = NULL; 708a0caf565Sdjm memset(user_id, 0, sizeof(user_id)); 70901bb7af0Sdjm if (check_enroll_options(options, &device, user_id, 71001bb7af0Sdjm sizeof(user_id)) != 0) 711a0caf565Sdjm goto out; /* error already logged */ 712a0caf565Sdjm 713094c80e0Sdjm switch(alg) { 714f8cd6cb1Snaddy #ifdef WITH_OPENSSL 715b0297854Sdjm case SSH_SK_ECDSA: 716094c80e0Sdjm cose_alg = COSE_ES256; 717094c80e0Sdjm break; 718f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 719b0297854Sdjm case SSH_SK_ED25519: 720094c80e0Sdjm cose_alg = COSE_EDDSA; 721094c80e0Sdjm break; 722094c80e0Sdjm default: 723094c80e0Sdjm skdebug(__func__, "unsupported key type %d", alg); 724094c80e0Sdjm goto out; 725094c80e0Sdjm } 72601bb7af0Sdjm if (device != NULL) 72701bb7af0Sdjm sk = sk_open(device); 72801bb7af0Sdjm else 72901bb7af0Sdjm sk = sk_probe(NULL, NULL, 0); 73001bb7af0Sdjm if (sk == NULL) { 73101bb7af0Sdjm skdebug(__func__, "failed to find sk"); 732094c80e0Sdjm goto out; 733094c80e0Sdjm } 73401bb7af0Sdjm skdebug(__func__, "using device %s", sk->path); 735094c80e0Sdjm if ((cred = fido_cred_new()) == NULL) { 736094c80e0Sdjm skdebug(__func__, "fido_cred_new failed"); 737094c80e0Sdjm goto out; 738094c80e0Sdjm } 739094c80e0Sdjm if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { 740094c80e0Sdjm skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); 741094c80e0Sdjm goto out; 742094c80e0Sdjm } 7435e70647bSdjm if (sha256_mem(challenge, challenge_len, 7445e70647bSdjm chall_hash, sizeof(chall_hash)) != 0) { 7455e70647bSdjm skdebug(__func__, "hash challenge failed"); 7465e70647bSdjm goto out; 7475e70647bSdjm } 7485e70647bSdjm if ((r = fido_cred_set_clientdata_hash(cred, chall_hash, 7495e70647bSdjm sizeof(chall_hash))) != FIDO_OK) { 750094c80e0Sdjm skdebug(__func__, "fido_cred_set_clientdata_hash: %s", 751094c80e0Sdjm fido_strerr(r)); 752094c80e0Sdjm goto out; 753094c80e0Sdjm } 754b0297854Sdjm if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ? 7558908bc36Sdjm FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) { 7568908bc36Sdjm skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r)); 7578908bc36Sdjm goto out; 7588908bc36Sdjm } 759094c80e0Sdjm if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), 760094c80e0Sdjm "openssh", "openssh", NULL)) != FIDO_OK) { 761094c80e0Sdjm skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); 762094c80e0Sdjm goto out; 763094c80e0Sdjm } 764094c80e0Sdjm if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { 765094c80e0Sdjm skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); 766094c80e0Sdjm goto out; 767094c80e0Sdjm } 7681f63d3c4Sdjm if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) { 76901bb7af0Sdjm if (!fido_dev_supports_cred_prot(sk->dev)) { 77001bb7af0Sdjm skdebug(__func__, "%s does not support credprot, " 77101bb7af0Sdjm "refusing to create unprotected " 77201bb7af0Sdjm "resident/verify-required key", sk->path); 7734a0fa473Sdjm ret = SSH_SK_ERR_UNSUPPORTED; 7744a0fa473Sdjm goto out; 7754a0fa473Sdjm } 7761f63d3c4Sdjm if ((flags & SSH_SK_USER_VERIFICATION_REQD)) 7771f63d3c4Sdjm credprot = FIDO_CRED_PROT_UV_REQUIRED; 7781f63d3c4Sdjm else 7791f63d3c4Sdjm credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID; 7801f63d3c4Sdjm 7811f63d3c4Sdjm if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) { 7824a0fa473Sdjm skdebug(__func__, "fido_cred_set_prot: %s", 7834a0fa473Sdjm fido_strerr(r)); 7844a0fa473Sdjm ret = fidoerr_to_skerr(r); 7854a0fa473Sdjm goto out; 7864a0fa473Sdjm } 7874a0fa473Sdjm } 78801bb7af0Sdjm if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) { 789094c80e0Sdjm skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); 790480af03fSdjm ret = fidoerr_to_skerr(r); 791094c80e0Sdjm goto out; 792094c80e0Sdjm } 793094c80e0Sdjm if (fido_cred_x5c_ptr(cred) != NULL) { 794094c80e0Sdjm if ((r = fido_cred_verify(cred)) != FIDO_OK) { 795094c80e0Sdjm skdebug(__func__, "fido_cred_verify: %s", 796094c80e0Sdjm fido_strerr(r)); 797094c80e0Sdjm goto out; 798094c80e0Sdjm } 799094c80e0Sdjm } else { 800094c80e0Sdjm skdebug(__func__, "self-attested credential"); 801094c80e0Sdjm if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { 802094c80e0Sdjm skdebug(__func__, "fido_cred_verify_self: %s", 803094c80e0Sdjm fido_strerr(r)); 804094c80e0Sdjm goto out; 805094c80e0Sdjm } 806094c80e0Sdjm } 807094c80e0Sdjm if ((response = calloc(1, sizeof(*response))) == NULL) { 808094c80e0Sdjm skdebug(__func__, "calloc response failed"); 809094c80e0Sdjm goto out; 810094c80e0Sdjm } 81118b3d906Sdjm response->flags = flags; 81218b3d906Sdjm if ((flags & SSH_SK_USER_VERIFICATION_REQD)) { 81318b3d906Sdjm if (check_sk_options(sk->dev, "uv", &internal_uv) == 0 && 81418b3d906Sdjm internal_uv != -1) { 81518b3d906Sdjm /* user verification handled by token */ 81618b3d906Sdjm response->flags &= ~SSH_SK_USER_VERIFICATION_REQD; 81718b3d906Sdjm } 81818b3d906Sdjm } 819094c80e0Sdjm if (pack_public_key(alg, cred, response) != 0) { 820094c80e0Sdjm skdebug(__func__, "pack_public_key failed"); 821094c80e0Sdjm goto out; 822094c80e0Sdjm } 823094c80e0Sdjm if ((ptr = fido_cred_id_ptr(cred)) != NULL) { 824094c80e0Sdjm len = fido_cred_id_len(cred); 825094c80e0Sdjm if ((response->key_handle = calloc(1, len)) == NULL) { 826094c80e0Sdjm skdebug(__func__, "calloc key handle failed"); 827094c80e0Sdjm goto out; 828094c80e0Sdjm } 829094c80e0Sdjm memcpy(response->key_handle, ptr, len); 830094c80e0Sdjm response->key_handle_len = len; 831094c80e0Sdjm } 832094c80e0Sdjm if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { 833094c80e0Sdjm len = fido_cred_sig_len(cred); 834094c80e0Sdjm if ((response->signature = calloc(1, len)) == NULL) { 835094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 836094c80e0Sdjm goto out; 837094c80e0Sdjm } 838094c80e0Sdjm memcpy(response->signature, ptr, len); 839094c80e0Sdjm response->signature_len = len; 840094c80e0Sdjm } 841094c80e0Sdjm if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { 842094c80e0Sdjm len = fido_cred_x5c_len(cred); 84348e6b99dSdjm skdebug(__func__, "attestation cert len=%zu", len); 844094c80e0Sdjm if ((response->attestation_cert = calloc(1, len)) == NULL) { 845094c80e0Sdjm skdebug(__func__, "calloc attestation cert failed"); 846094c80e0Sdjm goto out; 847094c80e0Sdjm } 848094c80e0Sdjm memcpy(response->attestation_cert, ptr, len); 849094c80e0Sdjm response->attestation_cert_len = len; 850094c80e0Sdjm } 851ee0a8761Sdjm if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) { 852ee0a8761Sdjm len = fido_cred_authdata_len(cred); 85348e6b99dSdjm skdebug(__func__, "authdata len=%zu", len); 854ee0a8761Sdjm if ((response->authdata = calloc(1, len)) == NULL) { 855ee0a8761Sdjm skdebug(__func__, "calloc authdata failed"); 856ee0a8761Sdjm goto out; 857ee0a8761Sdjm } 858ee0a8761Sdjm memcpy(response->authdata, ptr, len); 859ee0a8761Sdjm response->authdata_len = len; 860ee0a8761Sdjm } 86135f0234cSmarkus *enroll_response = response; 862094c80e0Sdjm response = NULL; 863094c80e0Sdjm ret = 0; 864094c80e0Sdjm out: 865094c80e0Sdjm free(device); 866094c80e0Sdjm if (response != NULL) { 867094c80e0Sdjm free(response->public_key); 868094c80e0Sdjm free(response->key_handle); 869094c80e0Sdjm free(response->signature); 870094c80e0Sdjm free(response->attestation_cert); 871ee0a8761Sdjm free(response->authdata); 872094c80e0Sdjm free(response); 873094c80e0Sdjm } 87401bb7af0Sdjm sk_close(sk); 875094c80e0Sdjm fido_cred_free(&cred); 876094c80e0Sdjm return ret; 877094c80e0Sdjm } 878094c80e0Sdjm 879f8cd6cb1Snaddy #ifdef WITH_OPENSSL 880094c80e0Sdjm static int 881094c80e0Sdjm pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) 882094c80e0Sdjm { 883094c80e0Sdjm ECDSA_SIG *sig = NULL; 884094c80e0Sdjm const BIGNUM *sig_r, *sig_s; 885094c80e0Sdjm const unsigned char *cp; 886094c80e0Sdjm size_t sig_len; 887094c80e0Sdjm int ret = -1; 888094c80e0Sdjm 889094c80e0Sdjm cp = fido_assert_sig_ptr(assert, 0); 890094c80e0Sdjm sig_len = fido_assert_sig_len(assert, 0); 891094c80e0Sdjm if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { 892094c80e0Sdjm skdebug(__func__, "d2i_ECDSA_SIG failed"); 893094c80e0Sdjm goto out; 894094c80e0Sdjm } 895094c80e0Sdjm ECDSA_SIG_get0(sig, &sig_r, &sig_s); 896094c80e0Sdjm response->sig_r_len = BN_num_bytes(sig_r); 897094c80e0Sdjm response->sig_s_len = BN_num_bytes(sig_s); 898094c80e0Sdjm if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || 899094c80e0Sdjm (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { 900094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 901094c80e0Sdjm goto out; 902094c80e0Sdjm } 903094c80e0Sdjm BN_bn2bin(sig_r, response->sig_r); 904094c80e0Sdjm BN_bn2bin(sig_s, response->sig_s); 905094c80e0Sdjm ret = 0; 906094c80e0Sdjm out: 907094c80e0Sdjm ECDSA_SIG_free(sig); 908094c80e0Sdjm if (ret != 0) { 909094c80e0Sdjm free(response->sig_r); 910094c80e0Sdjm free(response->sig_s); 911094c80e0Sdjm response->sig_r = NULL; 912094c80e0Sdjm response->sig_s = NULL; 913094c80e0Sdjm } 914094c80e0Sdjm return ret; 915094c80e0Sdjm } 916f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 917094c80e0Sdjm 918094c80e0Sdjm static int 919094c80e0Sdjm pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) 920094c80e0Sdjm { 921094c80e0Sdjm const unsigned char *ptr; 922094c80e0Sdjm size_t len; 923094c80e0Sdjm int ret = -1; 924094c80e0Sdjm 925094c80e0Sdjm ptr = fido_assert_sig_ptr(assert, 0); 926094c80e0Sdjm len = fido_assert_sig_len(assert, 0); 927094c80e0Sdjm if (len != 64) { 928094c80e0Sdjm skdebug(__func__, "bad length %zu", len); 929094c80e0Sdjm goto out; 930094c80e0Sdjm } 931094c80e0Sdjm response->sig_r_len = len; 932094c80e0Sdjm if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { 933094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 934094c80e0Sdjm goto out; 935094c80e0Sdjm } 936094c80e0Sdjm memcpy(response->sig_r, ptr, len); 937094c80e0Sdjm ret = 0; 938094c80e0Sdjm out: 939094c80e0Sdjm if (ret != 0) { 940094c80e0Sdjm free(response->sig_r); 941094c80e0Sdjm response->sig_r = NULL; 942094c80e0Sdjm } 943094c80e0Sdjm return ret; 944094c80e0Sdjm } 945094c80e0Sdjm 946094c80e0Sdjm static int 947a0caf565Sdjm pack_sig(uint32_t alg, fido_assert_t *assert, 948a0caf565Sdjm struct sk_sign_response *response) 949094c80e0Sdjm { 950094c80e0Sdjm switch(alg) { 951f8cd6cb1Snaddy #ifdef WITH_OPENSSL 952b0297854Sdjm case SSH_SK_ECDSA: 953094c80e0Sdjm return pack_sig_ecdsa(assert, response); 954f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 955b0297854Sdjm case SSH_SK_ED25519: 956094c80e0Sdjm return pack_sig_ed25519(assert, response); 957094c80e0Sdjm default: 958094c80e0Sdjm return -1; 959094c80e0Sdjm } 960094c80e0Sdjm } 961094c80e0Sdjm 962a0caf565Sdjm /* Checks sk_options for sk_sign() and sk_load_resident_keys() */ 963a0caf565Sdjm static int 964a0caf565Sdjm check_sign_load_resident_options(struct sk_option **options, char **devicep) 965a0caf565Sdjm { 966a0caf565Sdjm size_t i; 967a0caf565Sdjm 968a0caf565Sdjm if (options == NULL) 969a0caf565Sdjm return 0; 970a0caf565Sdjm for (i = 0; options[i] != NULL; i++) { 971a0caf565Sdjm if (strcmp(options[i]->name, "device") == 0) { 972a0caf565Sdjm if ((*devicep = strdup(options[i]->value)) == NULL) { 973a0caf565Sdjm skdebug(__func__, "strdup device failed"); 974a0caf565Sdjm return -1; 975a0caf565Sdjm } 976a0caf565Sdjm skdebug(__func__, "requested device %s", *devicep); 977a0caf565Sdjm } else { 978a0caf565Sdjm skdebug(__func__, "requested unsupported option %s", 979a0caf565Sdjm options[i]->name); 980a0caf565Sdjm if (options[i]->required) { 981a0caf565Sdjm skdebug(__func__, "unknown required option"); 982a0caf565Sdjm return -1; 983a0caf565Sdjm } 984a0caf565Sdjm } 985a0caf565Sdjm } 986a0caf565Sdjm return 0; 987a0caf565Sdjm } 988a0caf565Sdjm 989094c80e0Sdjm int 9903ce2af41Sdjm sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, 991094c80e0Sdjm const char *application, 992094c80e0Sdjm const uint8_t *key_handle, size_t key_handle_len, 993a0caf565Sdjm uint8_t flags, const char *pin, struct sk_option **options, 994a0caf565Sdjm struct sk_sign_response **sign_response) 995094c80e0Sdjm { 996094c80e0Sdjm fido_assert_t *assert = NULL; 997a0caf565Sdjm char *device = NULL; 99801bb7af0Sdjm struct sk_usbhid *sk = NULL; 999094c80e0Sdjm struct sk_sign_response *response = NULL; 10003ce2af41Sdjm uint8_t message[32]; 100118b3d906Sdjm int ret = SSH_SK_ERR_GENERAL, internal_uv; 1002094c80e0Sdjm int r; 1003094c80e0Sdjm 1004fb0a6bcaSdjm fido_init(SSH_FIDO_INIT_ARG); 1005094c80e0Sdjm 1006094c80e0Sdjm if (sign_response == NULL) { 1007094c80e0Sdjm skdebug(__func__, "sign_response == NULL"); 1008094c80e0Sdjm goto out; 1009094c80e0Sdjm } 1010094c80e0Sdjm *sign_response = NULL; 1011a0caf565Sdjm if (check_sign_load_resident_options(options, &device) != 0) 1012a0caf565Sdjm goto out; /* error already logged */ 10133ce2af41Sdjm /* hash data to be signed before it goes to the security key */ 10143ce2af41Sdjm if ((r = sha256_mem(data, datalen, message, sizeof(message))) != 0) { 10153ce2af41Sdjm skdebug(__func__, "hash message failed"); 10163ce2af41Sdjm goto out; 10173ce2af41Sdjm } 101801bb7af0Sdjm if (device != NULL) 101901bb7af0Sdjm sk = sk_open(device); 102001bb7af0Sdjm else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD)) 102101bb7af0Sdjm sk = sk_probe(NULL, NULL, 0); 102201bb7af0Sdjm else 102301bb7af0Sdjm sk = sk_probe(application, key_handle, key_handle_len); 102401bb7af0Sdjm if (sk == NULL) { 102501bb7af0Sdjm skdebug(__func__, "failed to find sk"); 1026094c80e0Sdjm goto out; 1027094c80e0Sdjm } 1028094c80e0Sdjm if ((assert = fido_assert_new()) == NULL) { 1029094c80e0Sdjm skdebug(__func__, "fido_assert_new failed"); 1030094c80e0Sdjm goto out; 1031094c80e0Sdjm } 1032094c80e0Sdjm if ((r = fido_assert_set_clientdata_hash(assert, message, 10333ce2af41Sdjm sizeof(message))) != FIDO_OK) { 1034094c80e0Sdjm skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 1035094c80e0Sdjm fido_strerr(r)); 1036094c80e0Sdjm goto out; 1037094c80e0Sdjm } 1038094c80e0Sdjm if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 1039094c80e0Sdjm skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 1040094c80e0Sdjm goto out; 1041094c80e0Sdjm } 1042094c80e0Sdjm if ((r = fido_assert_allow_cred(assert, key_handle, 1043094c80e0Sdjm key_handle_len)) != FIDO_OK) { 1044094c80e0Sdjm skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 1045094c80e0Sdjm goto out; 1046094c80e0Sdjm } 1047094c80e0Sdjm if ((r = fido_assert_set_up(assert, 1048b0297854Sdjm (flags & SSH_SK_USER_PRESENCE_REQD) ? 1049094c80e0Sdjm FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { 1050094c80e0Sdjm skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); 1051094c80e0Sdjm goto out; 1052094c80e0Sdjm } 105318b3d906Sdjm if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) { 105418b3d906Sdjm if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 || 105518b3d906Sdjm internal_uv != 1) { 105618b3d906Sdjm skdebug(__func__, "check_sk_options uv"); 105718b3d906Sdjm ret = SSH_SK_ERR_PIN_REQUIRED; 10581f63d3c4Sdjm goto out; 10591f63d3c4Sdjm } 106018b3d906Sdjm if ((r = fido_assert_set_uv(assert, 106118b3d906Sdjm FIDO_OPT_TRUE)) != FIDO_OK) { 106218b3d906Sdjm skdebug(__func__, "fido_assert_set_uv: %s", 106318b3d906Sdjm fido_strerr(r)); 106418b3d906Sdjm ret = fidoerr_to_skerr(r); 106518b3d906Sdjm goto out; 106618b3d906Sdjm } 106718b3d906Sdjm } 106801bb7af0Sdjm if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) { 1069094c80e0Sdjm skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 10701f63d3c4Sdjm ret = fidoerr_to_skerr(r); 1071094c80e0Sdjm goto out; 1072094c80e0Sdjm } 1073094c80e0Sdjm if ((response = calloc(1, sizeof(*response))) == NULL) { 1074094c80e0Sdjm skdebug(__func__, "calloc response failed"); 1075094c80e0Sdjm goto out; 1076094c80e0Sdjm } 1077094c80e0Sdjm response->flags = fido_assert_flags(assert, 0); 1078094c80e0Sdjm response->counter = fido_assert_sigcount(assert, 0); 1079094c80e0Sdjm if (pack_sig(alg, assert, response) != 0) { 1080094c80e0Sdjm skdebug(__func__, "pack_sig failed"); 1081094c80e0Sdjm goto out; 1082094c80e0Sdjm } 1083094c80e0Sdjm *sign_response = response; 1084094c80e0Sdjm response = NULL; 1085094c80e0Sdjm ret = 0; 1086094c80e0Sdjm out: 10873ce2af41Sdjm explicit_bzero(message, sizeof(message)); 1088a0caf565Sdjm free(device); 1089094c80e0Sdjm if (response != NULL) { 1090094c80e0Sdjm free(response->sig_r); 1091094c80e0Sdjm free(response->sig_s); 1092094c80e0Sdjm free(response); 1093094c80e0Sdjm } 109401bb7af0Sdjm sk_close(sk); 1095094c80e0Sdjm fido_assert_free(&assert); 1096094c80e0Sdjm return ret; 1097094c80e0Sdjm } 10981ac4a90aSdjm 10991ac4a90aSdjm static int 110001bb7af0Sdjm read_rks(struct sk_usbhid *sk, const char *pin, 11011ac4a90aSdjm struct sk_resident_key ***rksp, size_t *nrksp) 11021ac4a90aSdjm { 110318b3d906Sdjm int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv; 11041ac4a90aSdjm fido_credman_metadata_t *metadata = NULL; 11051ac4a90aSdjm fido_credman_rp_t *rp = NULL; 11061ac4a90aSdjm fido_credman_rk_t *rk = NULL; 1107991d5a20Sdjm size_t i, j, nrp, nrk, user_id_len; 11081ac4a90aSdjm const fido_cred_t *cred; 1109991d5a20Sdjm const char *rp_id, *rp_name, *user_name; 11101ac4a90aSdjm struct sk_resident_key *srk = NULL, **tmp; 1111991d5a20Sdjm const u_char *user_id; 11121ac4a90aSdjm 111301bb7af0Sdjm if (pin == NULL) { 111401bb7af0Sdjm skdebug(__func__, "no PIN specified"); 111501bb7af0Sdjm ret = SSH_SK_ERR_PIN_REQUIRED; 111601bb7af0Sdjm goto out; 11171ac4a90aSdjm } 11181ac4a90aSdjm if ((metadata = fido_credman_metadata_new()) == NULL) { 11191ac4a90aSdjm skdebug(__func__, "alloc failed"); 11201ac4a90aSdjm goto out; 11211ac4a90aSdjm } 112218b3d906Sdjm if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) { 112318b3d906Sdjm skdebug(__func__, "check_sk_options failed"); 112418b3d906Sdjm goto out; 112518b3d906Sdjm } 11261ac4a90aSdjm 112701bb7af0Sdjm if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) { 11281ac4a90aSdjm if (r == FIDO_ERR_INVALID_COMMAND) { 11291ac4a90aSdjm skdebug(__func__, "device %s does not support " 113001bb7af0Sdjm "resident keys", sk->path); 1131480af03fSdjm ret = 0; 11321ac4a90aSdjm goto out; 11331ac4a90aSdjm } 11341ac4a90aSdjm skdebug(__func__, "get metadata for %s failed: %s", 113501bb7af0Sdjm sk->path, fido_strerr(r)); 1136a0caf565Sdjm ret = fidoerr_to_skerr(r); 11371ac4a90aSdjm goto out; 11381ac4a90aSdjm } 11391ac4a90aSdjm skdebug(__func__, "existing %llu, remaining %llu", 11401ac4a90aSdjm (unsigned long long)fido_credman_rk_existing(metadata), 11411ac4a90aSdjm (unsigned long long)fido_credman_rk_remaining(metadata)); 11421ac4a90aSdjm if ((rp = fido_credman_rp_new()) == NULL) { 11431ac4a90aSdjm skdebug(__func__, "alloc rp failed"); 11441ac4a90aSdjm goto out; 11451ac4a90aSdjm } 114601bb7af0Sdjm if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) { 11471ac4a90aSdjm skdebug(__func__, "get RPs for %s failed: %s", 114801bb7af0Sdjm sk->path, fido_strerr(r)); 11491ac4a90aSdjm goto out; 11501ac4a90aSdjm } 11511ac4a90aSdjm nrp = fido_credman_rp_count(rp); 11521ac4a90aSdjm skdebug(__func__, "Device %s has resident keys for %zu RPs", 115301bb7af0Sdjm sk->path, nrp); 11541ac4a90aSdjm 11551ac4a90aSdjm /* Iterate over RP IDs that have resident keys */ 11561ac4a90aSdjm for (i = 0; i < nrp; i++) { 1157991d5a20Sdjm rp_id = fido_credman_rp_id(rp, i); 1158991d5a20Sdjm rp_name = fido_credman_rp_name(rp, i); 11591ac4a90aSdjm skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu", 1160991d5a20Sdjm i, rp_name == NULL ? "(none)" : rp_name, 1161991d5a20Sdjm rp_id == NULL ? "(none)" : rp_id, 11621ac4a90aSdjm fido_credman_rp_id_hash_len(rp, i)); 11631ac4a90aSdjm 11641ac4a90aSdjm /* Skip non-SSH RP IDs */ 1165991d5a20Sdjm if (rp_id == NULL || 1166991d5a20Sdjm strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0) 11671ac4a90aSdjm continue; 11681ac4a90aSdjm 11691ac4a90aSdjm fido_credman_rk_free(&rk); 11701ac4a90aSdjm if ((rk = fido_credman_rk_new()) == NULL) { 11711ac4a90aSdjm skdebug(__func__, "alloc rk failed"); 11721ac4a90aSdjm goto out; 11731ac4a90aSdjm } 117401bb7af0Sdjm if ((r = fido_credman_get_dev_rk(sk->dev, 117501bb7af0Sdjm fido_credman_rp_id(rp, i), rk, pin)) != 0) { 11761ac4a90aSdjm skdebug(__func__, "get RKs for %s slot %zu failed: %s", 117701bb7af0Sdjm sk->path, i, fido_strerr(r)); 11781ac4a90aSdjm goto out; 11791ac4a90aSdjm } 11801ac4a90aSdjm nrk = fido_credman_rk_count(rk); 11811ac4a90aSdjm skdebug(__func__, "RP \"%s\" has %zu resident keys", 11821ac4a90aSdjm fido_credman_rp_id(rp, i), nrk); 11831ac4a90aSdjm 11841ac4a90aSdjm /* Iterate over resident keys for this RP ID */ 11851ac4a90aSdjm for (j = 0; j < nrk; j++) { 11861ac4a90aSdjm if ((cred = fido_credman_rk(rk, j)) == NULL) { 11871ac4a90aSdjm skdebug(__func__, "no RK in slot %zu", j); 11881ac4a90aSdjm continue; 11891ac4a90aSdjm } 1190991d5a20Sdjm if ((user_name = fido_cred_user_name(cred)) == NULL) 1191991d5a20Sdjm user_name = ""; 1192991d5a20Sdjm user_id = fido_cred_user_id_ptr(cred); 1193991d5a20Sdjm user_id_len = fido_cred_user_id_len(cred); 1194991d5a20Sdjm skdebug(__func__, "Device %s RP \"%s\" user \"%s\" " 1195991d5a20Sdjm "uidlen %zu slot %zu: type %d flags 0x%02x " 1196991d5a20Sdjm "prot 0x%02x", sk->path, rp_id, user_name, 1197991d5a20Sdjm user_id_len, j, fido_cred_type(cred), 11981f63d3c4Sdjm fido_cred_flags(cred), fido_cred_prot(cred)); 11991ac4a90aSdjm 12001ac4a90aSdjm /* build response entry */ 12011ac4a90aSdjm if ((srk = calloc(1, sizeof(*srk))) == NULL || 12021ac4a90aSdjm (srk->key.key_handle = calloc(1, 12031ac4a90aSdjm fido_cred_id_len(cred))) == NULL || 1204991d5a20Sdjm (srk->application = strdup(rp_id)) == NULL || 1205991d5a20Sdjm (user_id_len > 0 && 1206991d5a20Sdjm (srk->user_id = calloc(1, user_id_len)) == NULL)) { 12071ac4a90aSdjm skdebug(__func__, "alloc sk_resident_key"); 12081ac4a90aSdjm goto out; 12091ac4a90aSdjm } 12101ac4a90aSdjm 12111ac4a90aSdjm srk->key.key_handle_len = fido_cred_id_len(cred); 121215a2cdb6Sdjm memcpy(srk->key.key_handle, fido_cred_id_ptr(cred), 12131ac4a90aSdjm srk->key.key_handle_len); 1214991d5a20Sdjm srk->user_id_len = user_id_len; 1215991d5a20Sdjm if (srk->user_id_len != 0) 1216991d5a20Sdjm memcpy(srk->user_id, user_id, srk->user_id_len); 12171ac4a90aSdjm 12181ac4a90aSdjm switch (fido_cred_type(cred)) { 12191ac4a90aSdjm case COSE_ES256: 1220b0297854Sdjm srk->alg = SSH_SK_ECDSA; 12211ac4a90aSdjm break; 12221ac4a90aSdjm case COSE_EDDSA: 1223b0297854Sdjm srk->alg = SSH_SK_ED25519; 12241ac4a90aSdjm break; 12251ac4a90aSdjm default: 12261ac4a90aSdjm skdebug(__func__, "unsupported key type %d", 12271ac4a90aSdjm fido_cred_type(cred)); 1228b0297854Sdjm goto out; /* XXX free rk and continue */ 12291ac4a90aSdjm } 12301ac4a90aSdjm 123118b3d906Sdjm if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED 123218b3d906Sdjm && internal_uv == -1) 123315a2cdb6Sdjm srk->flags |= SSH_SK_USER_VERIFICATION_REQD; 123415a2cdb6Sdjm 12351ac4a90aSdjm if ((r = pack_public_key(srk->alg, cred, 12361ac4a90aSdjm &srk->key)) != 0) { 12371ac4a90aSdjm skdebug(__func__, "pack public key failed"); 12381ac4a90aSdjm goto out; 12391ac4a90aSdjm } 12401ac4a90aSdjm /* append */ 12411ac4a90aSdjm if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1, 12421ac4a90aSdjm sizeof(**rksp))) == NULL) { 12431ac4a90aSdjm skdebug(__func__, "alloc rksp"); 12441ac4a90aSdjm goto out; 12451ac4a90aSdjm } 12461ac4a90aSdjm *rksp = tmp; 12471ac4a90aSdjm (*rksp)[(*nrksp)++] = srk; 12481ac4a90aSdjm srk = NULL; 12491ac4a90aSdjm } 12501ac4a90aSdjm } 12511ac4a90aSdjm /* Success */ 1252480af03fSdjm ret = 0; 12531ac4a90aSdjm out: 12541ac4a90aSdjm if (srk != NULL) { 12551ac4a90aSdjm free(srk->application); 12561ac4a90aSdjm freezero(srk->key.public_key, srk->key.public_key_len); 12571ac4a90aSdjm freezero(srk->key.key_handle, srk->key.key_handle_len); 1258991d5a20Sdjm freezero(srk->user_id, srk->user_id_len); 12591ac4a90aSdjm freezero(srk, sizeof(*srk)); 12601ac4a90aSdjm } 12611ac4a90aSdjm fido_credman_rp_free(&rp); 12621ac4a90aSdjm fido_credman_rk_free(&rk); 12631ac4a90aSdjm fido_credman_metadata_free(&metadata); 1264480af03fSdjm return ret; 12651ac4a90aSdjm } 12661ac4a90aSdjm 12671ac4a90aSdjm int 1268a0caf565Sdjm sk_load_resident_keys(const char *pin, struct sk_option **options, 12691ac4a90aSdjm struct sk_resident_key ***rksp, size_t *nrksp) 12701ac4a90aSdjm { 1271480af03fSdjm int ret = SSH_SK_ERR_GENERAL, r = -1; 127201bb7af0Sdjm size_t i, nrks = 0; 12731ac4a90aSdjm struct sk_resident_key **rks = NULL; 127401bb7af0Sdjm struct sk_usbhid *sk = NULL; 1275a0caf565Sdjm char *device = NULL; 127601bb7af0Sdjm 12771ac4a90aSdjm *rksp = NULL; 12781ac4a90aSdjm *nrksp = 0; 12791ac4a90aSdjm 1280fb0a6bcaSdjm fido_init(SSH_FIDO_INIT_ARG); 1281fb0a6bcaSdjm 1282a0caf565Sdjm if (check_sign_load_resident_options(options, &device) != 0) 1283a0caf565Sdjm goto out; /* error already logged */ 128401bb7af0Sdjm if (device != NULL) 128501bb7af0Sdjm sk = sk_open(device); 128601bb7af0Sdjm else 128701bb7af0Sdjm sk = sk_probe(NULL, NULL, 0); 128801bb7af0Sdjm if (sk == NULL) { 128901bb7af0Sdjm skdebug(__func__, "failed to find sk"); 129001bb7af0Sdjm goto out; 129101bb7af0Sdjm } 129201bb7af0Sdjm skdebug(__func__, "trying %s", sk->path); 129301bb7af0Sdjm if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) { 129401bb7af0Sdjm skdebug(__func__, "read_rks failed for %s", sk->path); 1295a0caf565Sdjm ret = r; 1296a0caf565Sdjm goto out; 1297a0caf565Sdjm } 1298a0caf565Sdjm /* success, unless we have no keys but a specific error */ 1299a0caf565Sdjm if (nrks > 0 || ret == SSH_SK_ERR_GENERAL) 1300480af03fSdjm ret = 0; 13011ac4a90aSdjm *rksp = rks; 13021ac4a90aSdjm *nrksp = nrks; 13031ac4a90aSdjm rks = NULL; 13041ac4a90aSdjm nrks = 0; 13051ac4a90aSdjm out: 130601bb7af0Sdjm sk_close(sk); 13071ac4a90aSdjm for (i = 0; i < nrks; i++) { 13081ac4a90aSdjm free(rks[i]->application); 13091ac4a90aSdjm freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); 13101ac4a90aSdjm freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); 1311991d5a20Sdjm freezero(rks[i]->user_id, rks[i]->user_id_len); 13121ac4a90aSdjm freezero(rks[i], sizeof(*rks[i])); 13131ac4a90aSdjm } 13141ac4a90aSdjm free(rks); 1315480af03fSdjm return ret; 13161ac4a90aSdjm } 13171ac4a90aSdjm 1318