1*991d5a20Sdjm /* $OpenBSD: sk-usbhid.c,v 1.32 2021/10/28 02:54:18 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 77094c80e0Sdjm 7801bb7af0Sdjm struct sk_usbhid { 7901bb7af0Sdjm fido_dev_t *dev; 8001bb7af0Sdjm char *path; 8101bb7af0Sdjm }; 8201bb7af0Sdjm 83094c80e0Sdjm /* Return the version of the middleware API */ 84094c80e0Sdjm uint32_t sk_api_version(void); 85094c80e0Sdjm 86094c80e0Sdjm /* Enroll a U2F key (private key generation) */ 87a0caf565Sdjm int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, 882db06755Sdjm const char *application, uint8_t flags, const char *pin, 89a0caf565Sdjm struct sk_option **options, struct sk_enroll_response **enroll_response); 90094c80e0Sdjm 91094c80e0Sdjm /* Sign a challenge */ 9274d7b7bdSdjm int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len, 93094c80e0Sdjm const char *application, const uint8_t *key_handle, size_t key_handle_len, 94a0caf565Sdjm uint8_t flags, const char *pin, struct sk_option **options, 95a0caf565Sdjm struct sk_sign_response **sign_response); 96094c80e0Sdjm 971ac4a90aSdjm /* Load resident keys */ 98a0caf565Sdjm int sk_load_resident_keys(const char *pin, struct sk_option **options, 991ac4a90aSdjm struct sk_resident_key ***rks, size_t *nrks); 1001ac4a90aSdjm 101094c80e0Sdjm static void skdebug(const char *func, const char *fmt, ...) 102094c80e0Sdjm __attribute__((__format__ (printf, 2, 3))); 103094c80e0Sdjm 104094c80e0Sdjm static void 105094c80e0Sdjm skdebug(const char *func, const char *fmt, ...) 106094c80e0Sdjm { 107094c80e0Sdjm #if !defined(SK_STANDALONE) 108094c80e0Sdjm char *msg; 109094c80e0Sdjm va_list ap; 110094c80e0Sdjm 111094c80e0Sdjm va_start(ap, fmt); 112094c80e0Sdjm xvasprintf(&msg, fmt, ap); 113094c80e0Sdjm va_end(ap); 11459959935Sdjm debug("%s: %s", func, msg); 115094c80e0Sdjm free(msg); 116094c80e0Sdjm #elif defined(SK_DEBUG) 117094c80e0Sdjm va_list ap; 118094c80e0Sdjm 119094c80e0Sdjm va_start(ap, fmt); 120094c80e0Sdjm fprintf(stderr, "%s: ", func); 121094c80e0Sdjm vfprintf(stderr, fmt, ap); 122094c80e0Sdjm fputc('\n', stderr); 123094c80e0Sdjm va_end(ap); 124094c80e0Sdjm #else 125094c80e0Sdjm (void)func; /* XXX */ 126094c80e0Sdjm (void)fmt; /* XXX */ 127094c80e0Sdjm #endif 128094c80e0Sdjm } 129094c80e0Sdjm 130094c80e0Sdjm uint32_t 131094c80e0Sdjm sk_api_version(void) 132094c80e0Sdjm { 133b0297854Sdjm return SSH_SK_VERSION_MAJOR; 134094c80e0Sdjm } 135094c80e0Sdjm 13601bb7af0Sdjm static struct sk_usbhid * 13701bb7af0Sdjm sk_open(const char *path) 138094c80e0Sdjm { 13901bb7af0Sdjm struct sk_usbhid *sk; 140094c80e0Sdjm int r; 141094c80e0Sdjm 14201bb7af0Sdjm if (path == NULL) { 14301bb7af0Sdjm skdebug(__func__, "path == NULL"); 14401bb7af0Sdjm return NULL; 145094c80e0Sdjm } 14601bb7af0Sdjm if ((sk = calloc(1, sizeof(*sk))) == NULL) { 14701bb7af0Sdjm skdebug(__func__, "calloc sk failed"); 14801bb7af0Sdjm return NULL; 14901bb7af0Sdjm } 15001bb7af0Sdjm if ((sk->path = strdup(path)) == NULL) { 15101bb7af0Sdjm skdebug(__func__, "strdup path failed"); 15201bb7af0Sdjm free(sk); 15301bb7af0Sdjm return NULL; 15401bb7af0Sdjm } 15501bb7af0Sdjm if ((sk->dev = fido_dev_new()) == NULL) { 15601bb7af0Sdjm skdebug(__func__, "fido_dev_new failed"); 15701bb7af0Sdjm free(sk->path); 15801bb7af0Sdjm free(sk); 15901bb7af0Sdjm return NULL; 16001bb7af0Sdjm } 16101bb7af0Sdjm if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) { 16201bb7af0Sdjm skdebug(__func__, "fido_dev_open %s failed: %s", sk->path, 163094c80e0Sdjm fido_strerr(r)); 16401bb7af0Sdjm fido_dev_free(&sk->dev); 16501bb7af0Sdjm free(sk->path); 16601bb7af0Sdjm free(sk); 16701bb7af0Sdjm return NULL; 168094c80e0Sdjm } 16901bb7af0Sdjm return sk; 170094c80e0Sdjm } 171094c80e0Sdjm 17201bb7af0Sdjm static void 17301bb7af0Sdjm sk_close(struct sk_usbhid *sk) 17401bb7af0Sdjm { 17501bb7af0Sdjm if (sk == NULL) 17601bb7af0Sdjm return; 17701bb7af0Sdjm fido_dev_cancel(sk->dev); /* cancel any pending operation */ 17801bb7af0Sdjm fido_dev_close(sk->dev); 17901bb7af0Sdjm fido_dev_free(&sk->dev); 18001bb7af0Sdjm free(sk->path); 18101bb7af0Sdjm free(sk); 18201bb7af0Sdjm } 18301bb7af0Sdjm 18401bb7af0Sdjm static struct sk_usbhid ** 18501bb7af0Sdjm sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen) 18601bb7af0Sdjm { 18701bb7af0Sdjm const fido_dev_info_t *di; 18801bb7af0Sdjm struct sk_usbhid **skv; 18901bb7af0Sdjm size_t i; 19001bb7af0Sdjm 19101bb7af0Sdjm *nopen = 0; 19201bb7af0Sdjm if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) { 19301bb7af0Sdjm skdebug(__func__, "calloc skv failed"); 19401bb7af0Sdjm return NULL; 19501bb7af0Sdjm } 19601bb7af0Sdjm for (i = 0; i < ndevs; i++) { 19701bb7af0Sdjm if ((di = fido_dev_info_ptr(devlist, i)) == NULL) 19801bb7af0Sdjm skdebug(__func__, "fido_dev_info_ptr failed"); 19901bb7af0Sdjm else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL) 20001bb7af0Sdjm skdebug(__func__, "sk_open failed"); 20101bb7af0Sdjm else 20201bb7af0Sdjm (*nopen)++; 20301bb7af0Sdjm } 20401bb7af0Sdjm if (*nopen == 0) { 20501bb7af0Sdjm for (i = 0; i < ndevs; i++) 20601bb7af0Sdjm sk_close(skv[i]); 20701bb7af0Sdjm free(skv); 20801bb7af0Sdjm skv = NULL; 20901bb7af0Sdjm } 21001bb7af0Sdjm 21101bb7af0Sdjm return skv; 21201bb7af0Sdjm } 21301bb7af0Sdjm 21401bb7af0Sdjm static void 21501bb7af0Sdjm sk_closev(struct sk_usbhid **skv, size_t nsk) 21601bb7af0Sdjm { 21701bb7af0Sdjm size_t i; 21801bb7af0Sdjm 21901bb7af0Sdjm for (i = 0; i < nsk; i++) 22001bb7af0Sdjm sk_close(skv[i]); 22101bb7af0Sdjm free(skv); 22201bb7af0Sdjm } 22301bb7af0Sdjm 224094c80e0Sdjm static int 22501bb7af0Sdjm sk_touch_begin(struct sk_usbhid **skv, size_t nsk) 22601bb7af0Sdjm { 22701bb7af0Sdjm size_t i, ok = 0; 22801bb7af0Sdjm int r; 22901bb7af0Sdjm 23001bb7af0Sdjm for (i = 0; i < nsk; i++) 23101bb7af0Sdjm if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK) 23201bb7af0Sdjm skdebug(__func__, "fido_dev_get_touch_begin %s failed:" 23301bb7af0Sdjm " %s", skv[i]->path, fido_strerr(r)); 23401bb7af0Sdjm else 23501bb7af0Sdjm ok++; 23601bb7af0Sdjm 23701bb7af0Sdjm return ok ? 0 : -1; 23801bb7af0Sdjm } 23901bb7af0Sdjm 24001bb7af0Sdjm static int 24101bb7af0Sdjm sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx) 24201bb7af0Sdjm { 24301bb7af0Sdjm struct timespec ts_pause; 24401bb7af0Sdjm size_t npoll, i; 24501bb7af0Sdjm int r; 24601bb7af0Sdjm 24701bb7af0Sdjm ts_pause.tv_sec = 0; 24801bb7af0Sdjm ts_pause.tv_nsec = POLL_SLEEP_NS; 24901bb7af0Sdjm nanosleep(&ts_pause, NULL); 25001bb7af0Sdjm npoll = nsk; 25101bb7af0Sdjm for (i = 0; i < nsk; i++) { 25201bb7af0Sdjm if (skv[i] == NULL) 25301bb7af0Sdjm continue; /* device discarded */ 25401bb7af0Sdjm skdebug(__func__, "polling %s", skv[i]->path); 25501bb7af0Sdjm if ((r = fido_dev_get_touch_status(skv[i]->dev, touch, 25601bb7af0Sdjm FIDO_POLL_MS)) != FIDO_OK) { 25701bb7af0Sdjm skdebug(__func__, "fido_dev_get_touch_status %s: %s", 25801bb7af0Sdjm skv[i]->path, fido_strerr(r)); 25901bb7af0Sdjm sk_close(skv[i]); /* discard device */ 26001bb7af0Sdjm skv[i] = NULL; 26101bb7af0Sdjm if (--npoll == 0) { 26201bb7af0Sdjm skdebug(__func__, "no device left to poll"); 26301bb7af0Sdjm return -1; 26401bb7af0Sdjm } 26501bb7af0Sdjm } else if (*touch) { 26601bb7af0Sdjm *idx = i; 26701bb7af0Sdjm return 0; 26801bb7af0Sdjm } 26901bb7af0Sdjm } 27001bb7af0Sdjm *touch = 0; 27101bb7af0Sdjm return 0; 27201bb7af0Sdjm } 27301bb7af0Sdjm 27401bb7af0Sdjm /* Calculate SHA256(m) */ 27501bb7af0Sdjm static int 27601bb7af0Sdjm sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen) 27701bb7af0Sdjm { 27801bb7af0Sdjm #ifdef WITH_OPENSSL 27901bb7af0Sdjm u_int mdlen; 2809424a71bSdjm #else 2819424a71bSdjm SHA2_CTX ctx; 28201bb7af0Sdjm #endif 28301bb7af0Sdjm 28401bb7af0Sdjm if (dlen != 32) 28501bb7af0Sdjm return -1; 28601bb7af0Sdjm #ifdef WITH_OPENSSL 28701bb7af0Sdjm mdlen = dlen; 28801bb7af0Sdjm if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL)) 28901bb7af0Sdjm return -1; 29001bb7af0Sdjm #else 2919424a71bSdjm SHA256Init(&ctx); 2929424a71bSdjm SHA256Update(&ctx, (const uint8_t *)m, mlen); 2939424a71bSdjm SHA256Final(d, &ctx); 29401bb7af0Sdjm #endif 29501bb7af0Sdjm return 0; 29601bb7af0Sdjm } 29701bb7af0Sdjm 29801bb7af0Sdjm /* Check if the specified key handle exists on a given sk. */ 29901bb7af0Sdjm static int 30001bb7af0Sdjm sk_try(const struct sk_usbhid *sk, const char *application, 30101bb7af0Sdjm const uint8_t *key_handle, size_t key_handle_len) 302094c80e0Sdjm { 303094c80e0Sdjm fido_assert_t *assert = NULL; 30401bb7af0Sdjm /* generate an invalid signature on FIDO2 tokens */ 30501bb7af0Sdjm const char *data = ""; 30601bb7af0Sdjm uint8_t message[32]; 307094c80e0Sdjm int r = FIDO_ERR_INTERNAL; 308094c80e0Sdjm 30901bb7af0Sdjm if (sha256_mem(data, strlen(data), message, sizeof(message)) != 0) { 31001bb7af0Sdjm skdebug(__func__, "hash message failed"); 31101bb7af0Sdjm goto out; 31201bb7af0Sdjm } 313094c80e0Sdjm if ((assert = fido_assert_new()) == NULL) { 314094c80e0Sdjm skdebug(__func__, "fido_assert_new failed"); 315094c80e0Sdjm goto out; 316094c80e0Sdjm } 317094c80e0Sdjm if ((r = fido_assert_set_clientdata_hash(assert, message, 31801bb7af0Sdjm sizeof(message))) != FIDO_OK) { 319094c80e0Sdjm skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 320094c80e0Sdjm fido_strerr(r)); 321094c80e0Sdjm goto out; 322094c80e0Sdjm } 323094c80e0Sdjm if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 324094c80e0Sdjm skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 325094c80e0Sdjm goto out; 326094c80e0Sdjm } 327094c80e0Sdjm if ((r = fido_assert_allow_cred(assert, key_handle, 328094c80e0Sdjm key_handle_len)) != FIDO_OK) { 329094c80e0Sdjm skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 330094c80e0Sdjm goto out; 331094c80e0Sdjm } 332094c80e0Sdjm if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { 333094c80e0Sdjm skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); 334094c80e0Sdjm goto out; 335094c80e0Sdjm } 33601bb7af0Sdjm r = fido_dev_get_assert(sk->dev, assert, NULL); 337094c80e0Sdjm skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 3387cf8e58dSdjm if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { 3397cf8e58dSdjm /* U2F tokens may return this */ 3407cf8e58dSdjm r = FIDO_OK; 3417cf8e58dSdjm } 342094c80e0Sdjm out: 343094c80e0Sdjm fido_assert_free(&assert); 344094c80e0Sdjm 345094c80e0Sdjm return r != FIDO_OK ? -1 : 0; 346094c80e0Sdjm } 347094c80e0Sdjm 34801bb7af0Sdjm static struct sk_usbhid * 34901bb7af0Sdjm sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs, 35001bb7af0Sdjm const char *application, const uint8_t *key_handle, size_t key_handle_len) 351094c80e0Sdjm { 35201bb7af0Sdjm struct sk_usbhid **skv, *sk; 35301bb7af0Sdjm size_t skvcnt, i; 354094c80e0Sdjm 35501bb7af0Sdjm if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) { 35601bb7af0Sdjm skdebug(__func__, "sk_openv failed"); 357a0caf565Sdjm return NULL; 358a0caf565Sdjm } 3598c966c57Sdjm if (skvcnt == 1) { 3608c966c57Sdjm sk = skv[0]; 3618c966c57Sdjm skv[0] = NULL; 3628c966c57Sdjm goto out; 3638c966c57Sdjm } 36401bb7af0Sdjm sk = NULL; 3658c966c57Sdjm for (i = 0; i < skvcnt; i++) { 36601bb7af0Sdjm if (sk_try(skv[i], application, key_handle, 36701bb7af0Sdjm key_handle_len) == 0) { 36801bb7af0Sdjm sk = skv[i]; 36901bb7af0Sdjm skv[i] = NULL; 37001bb7af0Sdjm skdebug(__func__, "found key in %s", sk->path); 37101bb7af0Sdjm break; 37201bb7af0Sdjm } 3738c966c57Sdjm } 3748c966c57Sdjm out: 37501bb7af0Sdjm sk_closev(skv, skvcnt); 37601bb7af0Sdjm return sk; 37701bb7af0Sdjm } 37801bb7af0Sdjm 37901bb7af0Sdjm static struct sk_usbhid * 38001bb7af0Sdjm sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs) 38101bb7af0Sdjm { 38201bb7af0Sdjm struct sk_usbhid **skv, *sk; 38301bb7af0Sdjm struct timeval tv_start, tv_now, tv_delta; 38401bb7af0Sdjm size_t skvcnt, idx; 38501bb7af0Sdjm int touch, ms_remain; 38601bb7af0Sdjm 38701bb7af0Sdjm if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) { 38801bb7af0Sdjm skdebug(__func__, "sk_openv failed"); 389a0caf565Sdjm return NULL; 390a0caf565Sdjm } 39101bb7af0Sdjm sk = NULL; 39201bb7af0Sdjm if (skvcnt < 2) { 39301bb7af0Sdjm if (skvcnt == 1) { 39401bb7af0Sdjm /* single candidate */ 39501bb7af0Sdjm sk = skv[0]; 39601bb7af0Sdjm skv[0] = NULL; 397a0caf565Sdjm } 39801bb7af0Sdjm goto out; 39901bb7af0Sdjm } 40001bb7af0Sdjm if (sk_touch_begin(skv, skvcnt) == -1) { 40101bb7af0Sdjm skdebug(__func__, "sk_touch_begin failed"); 40201bb7af0Sdjm goto out; 40301bb7af0Sdjm } 40401bb7af0Sdjm monotime_tv(&tv_start); 40501bb7af0Sdjm do { 40601bb7af0Sdjm if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) { 40701bb7af0Sdjm skdebug(__func__, "sk_touch_poll failed"); 40801bb7af0Sdjm goto out; 40901bb7af0Sdjm } 41001bb7af0Sdjm if (touch) { 41101bb7af0Sdjm sk = skv[idx]; 41201bb7af0Sdjm skv[idx] = NULL; 41301bb7af0Sdjm goto out; 41401bb7af0Sdjm } 41501bb7af0Sdjm monotime_tv(&tv_now); 41601bb7af0Sdjm timersub(&tv_now, &tv_start, &tv_delta); 41701bb7af0Sdjm ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 - 41801bb7af0Sdjm tv_delta.tv_usec / 1000; 41901bb7af0Sdjm } while (ms_remain >= FIDO_POLL_MS); 42001bb7af0Sdjm skdebug(__func__, "timeout"); 42101bb7af0Sdjm out: 42201bb7af0Sdjm sk_closev(skv, skvcnt); 42301bb7af0Sdjm return sk; 42401bb7af0Sdjm } 42501bb7af0Sdjm 42601bb7af0Sdjm static struct sk_usbhid * 42701bb7af0Sdjm sk_probe(const char *application, const uint8_t *key_handle, 42801bb7af0Sdjm size_t key_handle_len) 42901bb7af0Sdjm { 43001bb7af0Sdjm struct sk_usbhid *sk; 43101bb7af0Sdjm fido_dev_info_t *devlist; 43201bb7af0Sdjm size_t ndevs; 43301bb7af0Sdjm int r; 434a0caf565Sdjm 435094c80e0Sdjm if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { 436094c80e0Sdjm skdebug(__func__, "fido_dev_info_new failed"); 43701bb7af0Sdjm return NULL; 438094c80e0Sdjm } 439094c80e0Sdjm if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, 44001bb7af0Sdjm &ndevs)) != FIDO_OK) { 44101bb7af0Sdjm skdebug(__func__, "fido_dev_info_manifest failed: %s", 44201bb7af0Sdjm fido_strerr(r)); 443094c80e0Sdjm fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 44401bb7af0Sdjm return NULL; 44501bb7af0Sdjm } 44601bb7af0Sdjm skdebug(__func__, "%zu device(s) detected", ndevs); 44701bb7af0Sdjm if (ndevs == 0) { 44801bb7af0Sdjm sk = NULL; 44901bb7af0Sdjm } else if (application != NULL && key_handle != NULL) { 45001bb7af0Sdjm skdebug(__func__, "selecting sk by cred"); 45101bb7af0Sdjm sk = sk_select_by_cred(devlist, ndevs, application, key_handle, 45201bb7af0Sdjm key_handle_len); 45301bb7af0Sdjm } else { 45401bb7af0Sdjm skdebug(__func__, "selecting sk by touch"); 45501bb7af0Sdjm sk = sk_select_by_touch(devlist, ndevs); 45601bb7af0Sdjm } 45701bb7af0Sdjm fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 45801bb7af0Sdjm return sk; 459094c80e0Sdjm } 460094c80e0Sdjm 461f8cd6cb1Snaddy #ifdef WITH_OPENSSL 462094c80e0Sdjm /* 463094c80e0Sdjm * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, 464094c80e0Sdjm * but the API expects a SEC1 octet string. 465094c80e0Sdjm */ 466094c80e0Sdjm static int 4671ac4a90aSdjm pack_public_key_ecdsa(const fido_cred_t *cred, 4681ac4a90aSdjm struct sk_enroll_response *response) 469094c80e0Sdjm { 470094c80e0Sdjm const uint8_t *ptr; 471094c80e0Sdjm BIGNUM *x = NULL, *y = NULL; 472094c80e0Sdjm EC_POINT *q = NULL; 473094c80e0Sdjm EC_GROUP *g = NULL; 474094c80e0Sdjm int ret = -1; 475094c80e0Sdjm 476094c80e0Sdjm response->public_key = NULL; 477094c80e0Sdjm response->public_key_len = 0; 478094c80e0Sdjm 47907b718edSdjm if ((x = BN_new()) == NULL || 48007b718edSdjm (y = BN_new()) == NULL || 481094c80e0Sdjm (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || 482094c80e0Sdjm (q = EC_POINT_new(g)) == NULL) { 483094c80e0Sdjm skdebug(__func__, "libcrypto setup failed"); 484094c80e0Sdjm goto out; 485094c80e0Sdjm } 486094c80e0Sdjm if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 487094c80e0Sdjm skdebug(__func__, "fido_cred_pubkey_ptr failed"); 488094c80e0Sdjm goto out; 489094c80e0Sdjm } 490094c80e0Sdjm if (fido_cred_pubkey_len(cred) != 64) { 491094c80e0Sdjm skdebug(__func__, "bad fido_cred_pubkey_len %zu", 492094c80e0Sdjm fido_cred_pubkey_len(cred)); 493094c80e0Sdjm goto out; 494094c80e0Sdjm } 495094c80e0Sdjm 496094c80e0Sdjm if (BN_bin2bn(ptr, 32, x) == NULL || 497094c80e0Sdjm BN_bin2bn(ptr + 32, 32, y) == NULL) { 498094c80e0Sdjm skdebug(__func__, "BN_bin2bn failed"); 499094c80e0Sdjm goto out; 500094c80e0Sdjm } 50107b718edSdjm if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { 502094c80e0Sdjm skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); 503094c80e0Sdjm goto out; 504094c80e0Sdjm } 505094c80e0Sdjm response->public_key_len = EC_POINT_point2oct(g, q, 50607b718edSdjm POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); 507094c80e0Sdjm if (response->public_key_len == 0 || response->public_key_len > 2048) { 508094c80e0Sdjm skdebug(__func__, "bad pubkey length %zu", 509094c80e0Sdjm response->public_key_len); 510094c80e0Sdjm goto out; 511094c80e0Sdjm } 512094c80e0Sdjm if ((response->public_key = malloc(response->public_key_len)) == NULL) { 513094c80e0Sdjm skdebug(__func__, "malloc pubkey failed"); 514094c80e0Sdjm goto out; 515094c80e0Sdjm } 516094c80e0Sdjm if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, 51707b718edSdjm response->public_key, response->public_key_len, NULL) == 0) { 518094c80e0Sdjm skdebug(__func__, "EC_POINT_point2oct failed"); 519094c80e0Sdjm goto out; 520094c80e0Sdjm } 521094c80e0Sdjm /* success */ 522094c80e0Sdjm ret = 0; 523094c80e0Sdjm out: 524094c80e0Sdjm if (ret != 0 && response->public_key != NULL) { 525094c80e0Sdjm memset(response->public_key, 0, response->public_key_len); 526094c80e0Sdjm free(response->public_key); 527094c80e0Sdjm response->public_key = NULL; 528094c80e0Sdjm } 529094c80e0Sdjm EC_POINT_free(q); 530094c80e0Sdjm EC_GROUP_free(g); 53107b718edSdjm BN_clear_free(x); 53207b718edSdjm BN_clear_free(y); 533094c80e0Sdjm return ret; 534094c80e0Sdjm } 535f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 536094c80e0Sdjm 537094c80e0Sdjm static int 5381ac4a90aSdjm pack_public_key_ed25519(const fido_cred_t *cred, 5391ac4a90aSdjm struct sk_enroll_response *response) 540094c80e0Sdjm { 541094c80e0Sdjm const uint8_t *ptr; 542094c80e0Sdjm size_t len; 543094c80e0Sdjm int ret = -1; 544094c80e0Sdjm 545094c80e0Sdjm response->public_key = NULL; 546094c80e0Sdjm response->public_key_len = 0; 547094c80e0Sdjm 548094c80e0Sdjm if ((len = fido_cred_pubkey_len(cred)) != 32) { 549094c80e0Sdjm skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); 550094c80e0Sdjm goto out; 551094c80e0Sdjm } 552094c80e0Sdjm if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 553094c80e0Sdjm skdebug(__func__, "fido_cred_pubkey_ptr failed"); 554094c80e0Sdjm goto out; 555094c80e0Sdjm } 556094c80e0Sdjm response->public_key_len = len; 557094c80e0Sdjm if ((response->public_key = malloc(response->public_key_len)) == NULL) { 558094c80e0Sdjm skdebug(__func__, "malloc pubkey failed"); 559094c80e0Sdjm goto out; 560094c80e0Sdjm } 561094c80e0Sdjm memcpy(response->public_key, ptr, len); 562094c80e0Sdjm ret = 0; 563094c80e0Sdjm out: 564094c80e0Sdjm if (ret != 0) 565094c80e0Sdjm free(response->public_key); 566094c80e0Sdjm return ret; 567094c80e0Sdjm } 568094c80e0Sdjm 569094c80e0Sdjm static int 570a0caf565Sdjm pack_public_key(uint32_t alg, const fido_cred_t *cred, 5711ac4a90aSdjm struct sk_enroll_response *response) 572094c80e0Sdjm { 573094c80e0Sdjm switch(alg) { 574f8cd6cb1Snaddy #ifdef WITH_OPENSSL 575b0297854Sdjm case SSH_SK_ECDSA: 576094c80e0Sdjm return pack_public_key_ecdsa(cred, response); 577f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 578b0297854Sdjm case SSH_SK_ED25519: 579094c80e0Sdjm return pack_public_key_ed25519(cred, response); 580094c80e0Sdjm default: 581094c80e0Sdjm return -1; 582094c80e0Sdjm } 583094c80e0Sdjm } 584094c80e0Sdjm 585480af03fSdjm static int 586480af03fSdjm fidoerr_to_skerr(int fidoerr) 587480af03fSdjm { 588480af03fSdjm switch (fidoerr) { 589480af03fSdjm case FIDO_ERR_UNSUPPORTED_OPTION: 590b0297854Sdjm case FIDO_ERR_UNSUPPORTED_ALGORITHM: 591480af03fSdjm return SSH_SK_ERR_UNSUPPORTED; 592480af03fSdjm case FIDO_ERR_PIN_REQUIRED: 593480af03fSdjm case FIDO_ERR_PIN_INVALID: 594480af03fSdjm return SSH_SK_ERR_PIN_REQUIRED; 595480af03fSdjm default: 596480af03fSdjm return -1; 597480af03fSdjm } 598480af03fSdjm } 599480af03fSdjm 600a0caf565Sdjm static int 601a0caf565Sdjm check_enroll_options(struct sk_option **options, char **devicep, 602a0caf565Sdjm uint8_t *user_id, size_t user_id_len) 603a0caf565Sdjm { 604a0caf565Sdjm size_t i; 605a0caf565Sdjm 606a0caf565Sdjm if (options == NULL) 607a0caf565Sdjm return 0; 608a0caf565Sdjm for (i = 0; options[i] != NULL; i++) { 609a0caf565Sdjm if (strcmp(options[i]->name, "device") == 0) { 610a0caf565Sdjm if ((*devicep = strdup(options[i]->value)) == NULL) { 611a0caf565Sdjm skdebug(__func__, "strdup device failed"); 612a0caf565Sdjm return -1; 613a0caf565Sdjm } 614a0caf565Sdjm skdebug(__func__, "requested device %s", *devicep); 6151a9bfc1cSdjm } else if (strcmp(options[i]->name, "user") == 0) { 616a0caf565Sdjm if (strlcpy(user_id, options[i]->value, user_id_len) >= 617a0caf565Sdjm user_id_len) { 618a0caf565Sdjm skdebug(__func__, "user too long"); 619a0caf565Sdjm return -1; 620a0caf565Sdjm } 621a0caf565Sdjm skdebug(__func__, "requested user %s", 622a0caf565Sdjm (char *)user_id); 623a0caf565Sdjm } else { 624a0caf565Sdjm skdebug(__func__, "requested unsupported option %s", 625a0caf565Sdjm options[i]->name); 626a0caf565Sdjm if (options[i]->required) { 627a0caf565Sdjm skdebug(__func__, "unknown required option"); 628a0caf565Sdjm return -1; 629a0caf565Sdjm } 630a0caf565Sdjm } 631a0caf565Sdjm } 632a0caf565Sdjm return 0; 633a0caf565Sdjm } 634a0caf565Sdjm 635094c80e0Sdjm int 636a0caf565Sdjm sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, 6372db06755Sdjm const char *application, uint8_t flags, const char *pin, 638a0caf565Sdjm struct sk_option **options, struct sk_enroll_response **enroll_response) 639094c80e0Sdjm { 640094c80e0Sdjm fido_cred_t *cred = NULL; 641094c80e0Sdjm const uint8_t *ptr; 6425e70647bSdjm uint8_t user_id[32], chall_hash[32]; 64301bb7af0Sdjm struct sk_usbhid *sk = NULL; 644094c80e0Sdjm struct sk_enroll_response *response = NULL; 645094c80e0Sdjm size_t len; 6464a0fa473Sdjm int credprot; 647094c80e0Sdjm int cose_alg; 648480af03fSdjm int ret = SSH_SK_ERR_GENERAL; 649094c80e0Sdjm int r; 650094c80e0Sdjm char *device = NULL; 651094c80e0Sdjm 652fb0a6bcaSdjm fido_init(SSH_FIDO_INIT_ARG); 653fb0a6bcaSdjm 65435f0234cSmarkus if (enroll_response == NULL) { 65535f0234cSmarkus skdebug(__func__, "enroll_response == NULL"); 656094c80e0Sdjm goto out; 657094c80e0Sdjm } 65801bb7af0Sdjm *enroll_response = NULL; 659a0caf565Sdjm memset(user_id, 0, sizeof(user_id)); 66001bb7af0Sdjm if (check_enroll_options(options, &device, user_id, 66101bb7af0Sdjm sizeof(user_id)) != 0) 662a0caf565Sdjm goto out; /* error already logged */ 663a0caf565Sdjm 664094c80e0Sdjm switch(alg) { 665f8cd6cb1Snaddy #ifdef WITH_OPENSSL 666b0297854Sdjm case SSH_SK_ECDSA: 667094c80e0Sdjm cose_alg = COSE_ES256; 668094c80e0Sdjm break; 669f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 670b0297854Sdjm case SSH_SK_ED25519: 671094c80e0Sdjm cose_alg = COSE_EDDSA; 672094c80e0Sdjm break; 673094c80e0Sdjm default: 674094c80e0Sdjm skdebug(__func__, "unsupported key type %d", alg); 675094c80e0Sdjm goto out; 676094c80e0Sdjm } 67701bb7af0Sdjm if (device != NULL) 67801bb7af0Sdjm sk = sk_open(device); 67901bb7af0Sdjm else 68001bb7af0Sdjm sk = sk_probe(NULL, NULL, 0); 68101bb7af0Sdjm if (sk == NULL) { 68201bb7af0Sdjm skdebug(__func__, "failed to find sk"); 683094c80e0Sdjm goto out; 684094c80e0Sdjm } 68501bb7af0Sdjm skdebug(__func__, "using device %s", sk->path); 686094c80e0Sdjm if ((cred = fido_cred_new()) == NULL) { 687094c80e0Sdjm skdebug(__func__, "fido_cred_new failed"); 688094c80e0Sdjm goto out; 689094c80e0Sdjm } 690094c80e0Sdjm if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { 691094c80e0Sdjm skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); 692094c80e0Sdjm goto out; 693094c80e0Sdjm } 6945e70647bSdjm if (sha256_mem(challenge, challenge_len, 6955e70647bSdjm chall_hash, sizeof(chall_hash)) != 0) { 6965e70647bSdjm skdebug(__func__, "hash challenge failed"); 6975e70647bSdjm goto out; 6985e70647bSdjm } 6995e70647bSdjm if ((r = fido_cred_set_clientdata_hash(cred, chall_hash, 7005e70647bSdjm sizeof(chall_hash))) != FIDO_OK) { 701094c80e0Sdjm skdebug(__func__, "fido_cred_set_clientdata_hash: %s", 702094c80e0Sdjm fido_strerr(r)); 703094c80e0Sdjm goto out; 704094c80e0Sdjm } 705b0297854Sdjm if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ? 7068908bc36Sdjm FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) { 7078908bc36Sdjm skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r)); 7088908bc36Sdjm goto out; 7098908bc36Sdjm } 710094c80e0Sdjm if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), 711094c80e0Sdjm "openssh", "openssh", NULL)) != FIDO_OK) { 712094c80e0Sdjm skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); 713094c80e0Sdjm goto out; 714094c80e0Sdjm } 715094c80e0Sdjm if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { 716094c80e0Sdjm skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); 717094c80e0Sdjm goto out; 718094c80e0Sdjm } 7191f63d3c4Sdjm if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) { 72001bb7af0Sdjm if (!fido_dev_supports_cred_prot(sk->dev)) { 72101bb7af0Sdjm skdebug(__func__, "%s does not support credprot, " 72201bb7af0Sdjm "refusing to create unprotected " 72301bb7af0Sdjm "resident/verify-required key", sk->path); 7244a0fa473Sdjm ret = SSH_SK_ERR_UNSUPPORTED; 7254a0fa473Sdjm goto out; 7264a0fa473Sdjm } 7271f63d3c4Sdjm if ((flags & SSH_SK_USER_VERIFICATION_REQD)) 7281f63d3c4Sdjm credprot = FIDO_CRED_PROT_UV_REQUIRED; 7291f63d3c4Sdjm else 7301f63d3c4Sdjm credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID; 7311f63d3c4Sdjm 7321f63d3c4Sdjm if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) { 7334a0fa473Sdjm skdebug(__func__, "fido_cred_set_prot: %s", 7344a0fa473Sdjm fido_strerr(r)); 7354a0fa473Sdjm ret = fidoerr_to_skerr(r); 7364a0fa473Sdjm goto out; 7374a0fa473Sdjm } 7384a0fa473Sdjm } 73901bb7af0Sdjm if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) { 740094c80e0Sdjm skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); 741480af03fSdjm ret = fidoerr_to_skerr(r); 742094c80e0Sdjm goto out; 743094c80e0Sdjm } 744094c80e0Sdjm if (fido_cred_x5c_ptr(cred) != NULL) { 745094c80e0Sdjm if ((r = fido_cred_verify(cred)) != FIDO_OK) { 746094c80e0Sdjm skdebug(__func__, "fido_cred_verify: %s", 747094c80e0Sdjm fido_strerr(r)); 748094c80e0Sdjm goto out; 749094c80e0Sdjm } 750094c80e0Sdjm } else { 751094c80e0Sdjm skdebug(__func__, "self-attested credential"); 752094c80e0Sdjm if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { 753094c80e0Sdjm skdebug(__func__, "fido_cred_verify_self: %s", 754094c80e0Sdjm fido_strerr(r)); 755094c80e0Sdjm goto out; 756094c80e0Sdjm } 757094c80e0Sdjm } 758094c80e0Sdjm if ((response = calloc(1, sizeof(*response))) == NULL) { 759094c80e0Sdjm skdebug(__func__, "calloc response failed"); 760094c80e0Sdjm goto out; 761094c80e0Sdjm } 762094c80e0Sdjm if (pack_public_key(alg, cred, response) != 0) { 763094c80e0Sdjm skdebug(__func__, "pack_public_key failed"); 764094c80e0Sdjm goto out; 765094c80e0Sdjm } 766094c80e0Sdjm if ((ptr = fido_cred_id_ptr(cred)) != NULL) { 767094c80e0Sdjm len = fido_cred_id_len(cred); 768094c80e0Sdjm if ((response->key_handle = calloc(1, len)) == NULL) { 769094c80e0Sdjm skdebug(__func__, "calloc key handle failed"); 770094c80e0Sdjm goto out; 771094c80e0Sdjm } 772094c80e0Sdjm memcpy(response->key_handle, ptr, len); 773094c80e0Sdjm response->key_handle_len = len; 774094c80e0Sdjm } 775094c80e0Sdjm if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { 776094c80e0Sdjm len = fido_cred_sig_len(cred); 777094c80e0Sdjm if ((response->signature = calloc(1, len)) == NULL) { 778094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 779094c80e0Sdjm goto out; 780094c80e0Sdjm } 781094c80e0Sdjm memcpy(response->signature, ptr, len); 782094c80e0Sdjm response->signature_len = len; 783094c80e0Sdjm } 784094c80e0Sdjm if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { 785094c80e0Sdjm len = fido_cred_x5c_len(cred); 78648e6b99dSdjm skdebug(__func__, "attestation cert len=%zu", len); 787094c80e0Sdjm if ((response->attestation_cert = calloc(1, len)) == NULL) { 788094c80e0Sdjm skdebug(__func__, "calloc attestation cert failed"); 789094c80e0Sdjm goto out; 790094c80e0Sdjm } 791094c80e0Sdjm memcpy(response->attestation_cert, ptr, len); 792094c80e0Sdjm response->attestation_cert_len = len; 793094c80e0Sdjm } 794ee0a8761Sdjm if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) { 795ee0a8761Sdjm len = fido_cred_authdata_len(cred); 79648e6b99dSdjm skdebug(__func__, "authdata len=%zu", len); 797ee0a8761Sdjm if ((response->authdata = calloc(1, len)) == NULL) { 798ee0a8761Sdjm skdebug(__func__, "calloc authdata failed"); 799ee0a8761Sdjm goto out; 800ee0a8761Sdjm } 801ee0a8761Sdjm memcpy(response->authdata, ptr, len); 802ee0a8761Sdjm response->authdata_len = len; 803ee0a8761Sdjm } 80435f0234cSmarkus *enroll_response = response; 805094c80e0Sdjm response = NULL; 806094c80e0Sdjm ret = 0; 807094c80e0Sdjm out: 808094c80e0Sdjm free(device); 809094c80e0Sdjm if (response != NULL) { 810094c80e0Sdjm free(response->public_key); 811094c80e0Sdjm free(response->key_handle); 812094c80e0Sdjm free(response->signature); 813094c80e0Sdjm free(response->attestation_cert); 814ee0a8761Sdjm free(response->authdata); 815094c80e0Sdjm free(response); 816094c80e0Sdjm } 81701bb7af0Sdjm sk_close(sk); 818094c80e0Sdjm fido_cred_free(&cred); 819094c80e0Sdjm return ret; 820094c80e0Sdjm } 821094c80e0Sdjm 822f8cd6cb1Snaddy #ifdef WITH_OPENSSL 823094c80e0Sdjm static int 824094c80e0Sdjm pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) 825094c80e0Sdjm { 826094c80e0Sdjm ECDSA_SIG *sig = NULL; 827094c80e0Sdjm const BIGNUM *sig_r, *sig_s; 828094c80e0Sdjm const unsigned char *cp; 829094c80e0Sdjm size_t sig_len; 830094c80e0Sdjm int ret = -1; 831094c80e0Sdjm 832094c80e0Sdjm cp = fido_assert_sig_ptr(assert, 0); 833094c80e0Sdjm sig_len = fido_assert_sig_len(assert, 0); 834094c80e0Sdjm if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { 835094c80e0Sdjm skdebug(__func__, "d2i_ECDSA_SIG failed"); 836094c80e0Sdjm goto out; 837094c80e0Sdjm } 838094c80e0Sdjm ECDSA_SIG_get0(sig, &sig_r, &sig_s); 839094c80e0Sdjm response->sig_r_len = BN_num_bytes(sig_r); 840094c80e0Sdjm response->sig_s_len = BN_num_bytes(sig_s); 841094c80e0Sdjm if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || 842094c80e0Sdjm (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { 843094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 844094c80e0Sdjm goto out; 845094c80e0Sdjm } 846094c80e0Sdjm BN_bn2bin(sig_r, response->sig_r); 847094c80e0Sdjm BN_bn2bin(sig_s, response->sig_s); 848094c80e0Sdjm ret = 0; 849094c80e0Sdjm out: 850094c80e0Sdjm ECDSA_SIG_free(sig); 851094c80e0Sdjm if (ret != 0) { 852094c80e0Sdjm free(response->sig_r); 853094c80e0Sdjm free(response->sig_s); 854094c80e0Sdjm response->sig_r = NULL; 855094c80e0Sdjm response->sig_s = NULL; 856094c80e0Sdjm } 857094c80e0Sdjm return ret; 858094c80e0Sdjm } 859f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 860094c80e0Sdjm 861094c80e0Sdjm static int 862094c80e0Sdjm pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) 863094c80e0Sdjm { 864094c80e0Sdjm const unsigned char *ptr; 865094c80e0Sdjm size_t len; 866094c80e0Sdjm int ret = -1; 867094c80e0Sdjm 868094c80e0Sdjm ptr = fido_assert_sig_ptr(assert, 0); 869094c80e0Sdjm len = fido_assert_sig_len(assert, 0); 870094c80e0Sdjm if (len != 64) { 871094c80e0Sdjm skdebug(__func__, "bad length %zu", len); 872094c80e0Sdjm goto out; 873094c80e0Sdjm } 874094c80e0Sdjm response->sig_r_len = len; 875094c80e0Sdjm if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { 876094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 877094c80e0Sdjm goto out; 878094c80e0Sdjm } 879094c80e0Sdjm memcpy(response->sig_r, ptr, len); 880094c80e0Sdjm ret = 0; 881094c80e0Sdjm out: 882094c80e0Sdjm if (ret != 0) { 883094c80e0Sdjm free(response->sig_r); 884094c80e0Sdjm response->sig_r = NULL; 885094c80e0Sdjm } 886094c80e0Sdjm return ret; 887094c80e0Sdjm } 888094c80e0Sdjm 889094c80e0Sdjm static int 890a0caf565Sdjm pack_sig(uint32_t alg, fido_assert_t *assert, 891a0caf565Sdjm struct sk_sign_response *response) 892094c80e0Sdjm { 893094c80e0Sdjm switch(alg) { 894f8cd6cb1Snaddy #ifdef WITH_OPENSSL 895b0297854Sdjm case SSH_SK_ECDSA: 896094c80e0Sdjm return pack_sig_ecdsa(assert, response); 897f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 898b0297854Sdjm case SSH_SK_ED25519: 899094c80e0Sdjm return pack_sig_ed25519(assert, response); 900094c80e0Sdjm default: 901094c80e0Sdjm return -1; 902094c80e0Sdjm } 903094c80e0Sdjm } 904094c80e0Sdjm 905a0caf565Sdjm /* Checks sk_options for sk_sign() and sk_load_resident_keys() */ 906a0caf565Sdjm static int 907a0caf565Sdjm check_sign_load_resident_options(struct sk_option **options, char **devicep) 908a0caf565Sdjm { 909a0caf565Sdjm size_t i; 910a0caf565Sdjm 911a0caf565Sdjm if (options == NULL) 912a0caf565Sdjm return 0; 913a0caf565Sdjm for (i = 0; options[i] != NULL; i++) { 914a0caf565Sdjm if (strcmp(options[i]->name, "device") == 0) { 915a0caf565Sdjm if ((*devicep = strdup(options[i]->value)) == NULL) { 916a0caf565Sdjm skdebug(__func__, "strdup device failed"); 917a0caf565Sdjm return -1; 918a0caf565Sdjm } 919a0caf565Sdjm skdebug(__func__, "requested device %s", *devicep); 920a0caf565Sdjm } else { 921a0caf565Sdjm skdebug(__func__, "requested unsupported option %s", 922a0caf565Sdjm options[i]->name); 923a0caf565Sdjm if (options[i]->required) { 924a0caf565Sdjm skdebug(__func__, "unknown required option"); 925a0caf565Sdjm return -1; 926a0caf565Sdjm } 927a0caf565Sdjm } 928a0caf565Sdjm } 929a0caf565Sdjm return 0; 930a0caf565Sdjm } 931a0caf565Sdjm 932094c80e0Sdjm int 9333ce2af41Sdjm sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, 934094c80e0Sdjm const char *application, 935094c80e0Sdjm const uint8_t *key_handle, size_t key_handle_len, 936a0caf565Sdjm uint8_t flags, const char *pin, struct sk_option **options, 937a0caf565Sdjm struct sk_sign_response **sign_response) 938094c80e0Sdjm { 939094c80e0Sdjm fido_assert_t *assert = NULL; 940a0caf565Sdjm char *device = NULL; 94101bb7af0Sdjm struct sk_usbhid *sk = NULL; 942094c80e0Sdjm struct sk_sign_response *response = NULL; 9433ce2af41Sdjm uint8_t message[32]; 944480af03fSdjm int ret = SSH_SK_ERR_GENERAL; 945094c80e0Sdjm int r; 946094c80e0Sdjm 947fb0a6bcaSdjm fido_init(SSH_FIDO_INIT_ARG); 948094c80e0Sdjm 949094c80e0Sdjm if (sign_response == NULL) { 950094c80e0Sdjm skdebug(__func__, "sign_response == NULL"); 951094c80e0Sdjm goto out; 952094c80e0Sdjm } 953094c80e0Sdjm *sign_response = NULL; 954a0caf565Sdjm if (check_sign_load_resident_options(options, &device) != 0) 955a0caf565Sdjm goto out; /* error already logged */ 9563ce2af41Sdjm /* hash data to be signed before it goes to the security key */ 9573ce2af41Sdjm if ((r = sha256_mem(data, datalen, message, sizeof(message))) != 0) { 9583ce2af41Sdjm skdebug(__func__, "hash message failed"); 9593ce2af41Sdjm goto out; 9603ce2af41Sdjm } 96101bb7af0Sdjm if (device != NULL) 96201bb7af0Sdjm sk = sk_open(device); 96301bb7af0Sdjm else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD)) 96401bb7af0Sdjm sk = sk_probe(NULL, NULL, 0); 96501bb7af0Sdjm else 96601bb7af0Sdjm sk = sk_probe(application, key_handle, key_handle_len); 96701bb7af0Sdjm if (sk == NULL) { 96801bb7af0Sdjm skdebug(__func__, "failed to find sk"); 969094c80e0Sdjm goto out; 970094c80e0Sdjm } 971094c80e0Sdjm if ((assert = fido_assert_new()) == NULL) { 972094c80e0Sdjm skdebug(__func__, "fido_assert_new failed"); 973094c80e0Sdjm goto out; 974094c80e0Sdjm } 975094c80e0Sdjm if ((r = fido_assert_set_clientdata_hash(assert, message, 9763ce2af41Sdjm sizeof(message))) != FIDO_OK) { 977094c80e0Sdjm skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 978094c80e0Sdjm fido_strerr(r)); 979094c80e0Sdjm goto out; 980094c80e0Sdjm } 981094c80e0Sdjm if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 982094c80e0Sdjm skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 983094c80e0Sdjm goto out; 984094c80e0Sdjm } 985094c80e0Sdjm if ((r = fido_assert_allow_cred(assert, key_handle, 986094c80e0Sdjm key_handle_len)) != FIDO_OK) { 987094c80e0Sdjm skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 988094c80e0Sdjm goto out; 989094c80e0Sdjm } 990094c80e0Sdjm if ((r = fido_assert_set_up(assert, 991b0297854Sdjm (flags & SSH_SK_USER_PRESENCE_REQD) ? 992094c80e0Sdjm FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { 993094c80e0Sdjm skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); 994094c80e0Sdjm goto out; 995094c80e0Sdjm } 9961f63d3c4Sdjm if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD) && 9971f63d3c4Sdjm (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) { 9981f63d3c4Sdjm skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r)); 9991f63d3c4Sdjm ret = FIDO_ERR_PIN_REQUIRED; 10001f63d3c4Sdjm goto out; 10011f63d3c4Sdjm } 100201bb7af0Sdjm if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) { 1003094c80e0Sdjm skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 10041f63d3c4Sdjm ret = fidoerr_to_skerr(r); 1005094c80e0Sdjm goto out; 1006094c80e0Sdjm } 1007094c80e0Sdjm if ((response = calloc(1, sizeof(*response))) == NULL) { 1008094c80e0Sdjm skdebug(__func__, "calloc response failed"); 1009094c80e0Sdjm goto out; 1010094c80e0Sdjm } 1011094c80e0Sdjm response->flags = fido_assert_flags(assert, 0); 1012094c80e0Sdjm response->counter = fido_assert_sigcount(assert, 0); 1013094c80e0Sdjm if (pack_sig(alg, assert, response) != 0) { 1014094c80e0Sdjm skdebug(__func__, "pack_sig failed"); 1015094c80e0Sdjm goto out; 1016094c80e0Sdjm } 1017094c80e0Sdjm *sign_response = response; 1018094c80e0Sdjm response = NULL; 1019094c80e0Sdjm ret = 0; 1020094c80e0Sdjm out: 10213ce2af41Sdjm explicit_bzero(message, sizeof(message)); 1022a0caf565Sdjm free(device); 1023094c80e0Sdjm if (response != NULL) { 1024094c80e0Sdjm free(response->sig_r); 1025094c80e0Sdjm free(response->sig_s); 1026094c80e0Sdjm free(response); 1027094c80e0Sdjm } 102801bb7af0Sdjm sk_close(sk); 1029094c80e0Sdjm fido_assert_free(&assert); 1030094c80e0Sdjm return ret; 1031094c80e0Sdjm } 10321ac4a90aSdjm 10331ac4a90aSdjm static int 103401bb7af0Sdjm read_rks(struct sk_usbhid *sk, const char *pin, 10351ac4a90aSdjm struct sk_resident_key ***rksp, size_t *nrksp) 10361ac4a90aSdjm { 1037480af03fSdjm int ret = SSH_SK_ERR_GENERAL, r = -1; 10381ac4a90aSdjm fido_credman_metadata_t *metadata = NULL; 10391ac4a90aSdjm fido_credman_rp_t *rp = NULL; 10401ac4a90aSdjm fido_credman_rk_t *rk = NULL; 1041*991d5a20Sdjm size_t i, j, nrp, nrk, user_id_len; 10421ac4a90aSdjm const fido_cred_t *cred; 1043*991d5a20Sdjm const char *rp_id, *rp_name, *user_name; 10441ac4a90aSdjm struct sk_resident_key *srk = NULL, **tmp; 1045*991d5a20Sdjm const u_char *user_id; 10461ac4a90aSdjm 104701bb7af0Sdjm if (pin == NULL) { 104801bb7af0Sdjm skdebug(__func__, "no PIN specified"); 104901bb7af0Sdjm ret = SSH_SK_ERR_PIN_REQUIRED; 105001bb7af0Sdjm goto out; 10511ac4a90aSdjm } 10521ac4a90aSdjm if ((metadata = fido_credman_metadata_new()) == NULL) { 10531ac4a90aSdjm skdebug(__func__, "alloc failed"); 10541ac4a90aSdjm goto out; 10551ac4a90aSdjm } 10561ac4a90aSdjm 105701bb7af0Sdjm if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) { 10581ac4a90aSdjm if (r == FIDO_ERR_INVALID_COMMAND) { 10591ac4a90aSdjm skdebug(__func__, "device %s does not support " 106001bb7af0Sdjm "resident keys", sk->path); 1061480af03fSdjm ret = 0; 10621ac4a90aSdjm goto out; 10631ac4a90aSdjm } 10641ac4a90aSdjm skdebug(__func__, "get metadata for %s failed: %s", 106501bb7af0Sdjm sk->path, fido_strerr(r)); 1066a0caf565Sdjm ret = fidoerr_to_skerr(r); 10671ac4a90aSdjm goto out; 10681ac4a90aSdjm } 10691ac4a90aSdjm skdebug(__func__, "existing %llu, remaining %llu", 10701ac4a90aSdjm (unsigned long long)fido_credman_rk_existing(metadata), 10711ac4a90aSdjm (unsigned long long)fido_credman_rk_remaining(metadata)); 10721ac4a90aSdjm if ((rp = fido_credman_rp_new()) == NULL) { 10731ac4a90aSdjm skdebug(__func__, "alloc rp failed"); 10741ac4a90aSdjm goto out; 10751ac4a90aSdjm } 107601bb7af0Sdjm if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) { 10771ac4a90aSdjm skdebug(__func__, "get RPs for %s failed: %s", 107801bb7af0Sdjm sk->path, fido_strerr(r)); 10791ac4a90aSdjm goto out; 10801ac4a90aSdjm } 10811ac4a90aSdjm nrp = fido_credman_rp_count(rp); 10821ac4a90aSdjm skdebug(__func__, "Device %s has resident keys for %zu RPs", 108301bb7af0Sdjm sk->path, nrp); 10841ac4a90aSdjm 10851ac4a90aSdjm /* Iterate over RP IDs that have resident keys */ 10861ac4a90aSdjm for (i = 0; i < nrp; i++) { 1087*991d5a20Sdjm rp_id = fido_credman_rp_id(rp, i); 1088*991d5a20Sdjm rp_name = fido_credman_rp_name(rp, i); 10891ac4a90aSdjm skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu", 1090*991d5a20Sdjm i, rp_name == NULL ? "(none)" : rp_name, 1091*991d5a20Sdjm rp_id == NULL ? "(none)" : rp_id, 10921ac4a90aSdjm fido_credman_rp_id_hash_len(rp, i)); 10931ac4a90aSdjm 10941ac4a90aSdjm /* Skip non-SSH RP IDs */ 1095*991d5a20Sdjm if (rp_id == NULL || 1096*991d5a20Sdjm strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0) 10971ac4a90aSdjm continue; 10981ac4a90aSdjm 10991ac4a90aSdjm fido_credman_rk_free(&rk); 11001ac4a90aSdjm if ((rk = fido_credman_rk_new()) == NULL) { 11011ac4a90aSdjm skdebug(__func__, "alloc rk failed"); 11021ac4a90aSdjm goto out; 11031ac4a90aSdjm } 110401bb7af0Sdjm if ((r = fido_credman_get_dev_rk(sk->dev, 110501bb7af0Sdjm fido_credman_rp_id(rp, i), rk, pin)) != 0) { 11061ac4a90aSdjm skdebug(__func__, "get RKs for %s slot %zu failed: %s", 110701bb7af0Sdjm sk->path, i, fido_strerr(r)); 11081ac4a90aSdjm goto out; 11091ac4a90aSdjm } 11101ac4a90aSdjm nrk = fido_credman_rk_count(rk); 11111ac4a90aSdjm skdebug(__func__, "RP \"%s\" has %zu resident keys", 11121ac4a90aSdjm fido_credman_rp_id(rp, i), nrk); 11131ac4a90aSdjm 11141ac4a90aSdjm /* Iterate over resident keys for this RP ID */ 11151ac4a90aSdjm for (j = 0; j < nrk; j++) { 11161ac4a90aSdjm if ((cred = fido_credman_rk(rk, j)) == NULL) { 11171ac4a90aSdjm skdebug(__func__, "no RK in slot %zu", j); 11181ac4a90aSdjm continue; 11191ac4a90aSdjm } 1120*991d5a20Sdjm if ((user_name = fido_cred_user_name(cred)) == NULL) 1121*991d5a20Sdjm user_name = ""; 1122*991d5a20Sdjm user_id = fido_cred_user_id_ptr(cred); 1123*991d5a20Sdjm user_id_len = fido_cred_user_id_len(cred); 1124*991d5a20Sdjm skdebug(__func__, "Device %s RP \"%s\" user \"%s\" " 1125*991d5a20Sdjm "uidlen %zu slot %zu: type %d flags 0x%02x " 1126*991d5a20Sdjm "prot 0x%02x", sk->path, rp_id, user_name, 1127*991d5a20Sdjm user_id_len, j, fido_cred_type(cred), 11281f63d3c4Sdjm fido_cred_flags(cred), fido_cred_prot(cred)); 11291ac4a90aSdjm 11301ac4a90aSdjm /* build response entry */ 11311ac4a90aSdjm if ((srk = calloc(1, sizeof(*srk))) == NULL || 11321ac4a90aSdjm (srk->key.key_handle = calloc(1, 11331ac4a90aSdjm fido_cred_id_len(cred))) == NULL || 1134*991d5a20Sdjm (srk->application = strdup(rp_id)) == NULL || 1135*991d5a20Sdjm (user_id_len > 0 && 1136*991d5a20Sdjm (srk->user_id = calloc(1, user_id_len)) == NULL)) { 11371ac4a90aSdjm skdebug(__func__, "alloc sk_resident_key"); 11381ac4a90aSdjm goto out; 11391ac4a90aSdjm } 11401ac4a90aSdjm 11411ac4a90aSdjm srk->key.key_handle_len = fido_cred_id_len(cred); 114215a2cdb6Sdjm memcpy(srk->key.key_handle, fido_cred_id_ptr(cred), 11431ac4a90aSdjm srk->key.key_handle_len); 1144*991d5a20Sdjm srk->user_id_len = user_id_len; 1145*991d5a20Sdjm if (srk->user_id_len != 0) 1146*991d5a20Sdjm memcpy(srk->user_id, user_id, srk->user_id_len); 11471ac4a90aSdjm 11481ac4a90aSdjm switch (fido_cred_type(cred)) { 11491ac4a90aSdjm case COSE_ES256: 1150b0297854Sdjm srk->alg = SSH_SK_ECDSA; 11511ac4a90aSdjm break; 11521ac4a90aSdjm case COSE_EDDSA: 1153b0297854Sdjm srk->alg = SSH_SK_ED25519; 11541ac4a90aSdjm break; 11551ac4a90aSdjm default: 11561ac4a90aSdjm skdebug(__func__, "unsupported key type %d", 11571ac4a90aSdjm fido_cred_type(cred)); 1158b0297854Sdjm goto out; /* XXX free rk and continue */ 11591ac4a90aSdjm } 11601ac4a90aSdjm 116115a2cdb6Sdjm if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED) 116215a2cdb6Sdjm srk->flags |= SSH_SK_USER_VERIFICATION_REQD; 116315a2cdb6Sdjm 11641ac4a90aSdjm if ((r = pack_public_key(srk->alg, cred, 11651ac4a90aSdjm &srk->key)) != 0) { 11661ac4a90aSdjm skdebug(__func__, "pack public key failed"); 11671ac4a90aSdjm goto out; 11681ac4a90aSdjm } 11691ac4a90aSdjm /* append */ 11701ac4a90aSdjm if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1, 11711ac4a90aSdjm sizeof(**rksp))) == NULL) { 11721ac4a90aSdjm skdebug(__func__, "alloc rksp"); 11731ac4a90aSdjm goto out; 11741ac4a90aSdjm } 11751ac4a90aSdjm *rksp = tmp; 11761ac4a90aSdjm (*rksp)[(*nrksp)++] = srk; 11771ac4a90aSdjm srk = NULL; 11781ac4a90aSdjm } 11791ac4a90aSdjm } 11801ac4a90aSdjm /* Success */ 1181480af03fSdjm ret = 0; 11821ac4a90aSdjm out: 11831ac4a90aSdjm if (srk != NULL) { 11841ac4a90aSdjm free(srk->application); 11851ac4a90aSdjm freezero(srk->key.public_key, srk->key.public_key_len); 11861ac4a90aSdjm freezero(srk->key.key_handle, srk->key.key_handle_len); 1187*991d5a20Sdjm freezero(srk->user_id, srk->user_id_len); 11881ac4a90aSdjm freezero(srk, sizeof(*srk)); 11891ac4a90aSdjm } 11901ac4a90aSdjm fido_credman_rp_free(&rp); 11911ac4a90aSdjm fido_credman_rk_free(&rk); 11921ac4a90aSdjm fido_credman_metadata_free(&metadata); 1193480af03fSdjm return ret; 11941ac4a90aSdjm } 11951ac4a90aSdjm 11961ac4a90aSdjm int 1197a0caf565Sdjm sk_load_resident_keys(const char *pin, struct sk_option **options, 11981ac4a90aSdjm struct sk_resident_key ***rksp, size_t *nrksp) 11991ac4a90aSdjm { 1200480af03fSdjm int ret = SSH_SK_ERR_GENERAL, r = -1; 120101bb7af0Sdjm size_t i, nrks = 0; 12021ac4a90aSdjm struct sk_resident_key **rks = NULL; 120301bb7af0Sdjm struct sk_usbhid *sk = NULL; 1204a0caf565Sdjm char *device = NULL; 120501bb7af0Sdjm 12061ac4a90aSdjm *rksp = NULL; 12071ac4a90aSdjm *nrksp = 0; 12081ac4a90aSdjm 1209fb0a6bcaSdjm fido_init(SSH_FIDO_INIT_ARG); 1210fb0a6bcaSdjm 1211a0caf565Sdjm if (check_sign_load_resident_options(options, &device) != 0) 1212a0caf565Sdjm goto out; /* error already logged */ 121301bb7af0Sdjm if (device != NULL) 121401bb7af0Sdjm sk = sk_open(device); 121501bb7af0Sdjm else 121601bb7af0Sdjm sk = sk_probe(NULL, NULL, 0); 121701bb7af0Sdjm if (sk == NULL) { 121801bb7af0Sdjm skdebug(__func__, "failed to find sk"); 121901bb7af0Sdjm goto out; 122001bb7af0Sdjm } 122101bb7af0Sdjm skdebug(__func__, "trying %s", sk->path); 122201bb7af0Sdjm if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) { 122301bb7af0Sdjm skdebug(__func__, "read_rks failed for %s", sk->path); 1224a0caf565Sdjm ret = r; 1225a0caf565Sdjm goto out; 1226a0caf565Sdjm } 1227a0caf565Sdjm /* success, unless we have no keys but a specific error */ 1228a0caf565Sdjm if (nrks > 0 || ret == SSH_SK_ERR_GENERAL) 1229480af03fSdjm ret = 0; 12301ac4a90aSdjm *rksp = rks; 12311ac4a90aSdjm *nrksp = nrks; 12321ac4a90aSdjm rks = NULL; 12331ac4a90aSdjm nrks = 0; 12341ac4a90aSdjm out: 123501bb7af0Sdjm sk_close(sk); 12361ac4a90aSdjm for (i = 0; i < nrks; i++) { 12371ac4a90aSdjm free(rks[i]->application); 12381ac4a90aSdjm freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); 12391ac4a90aSdjm freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); 1240*991d5a20Sdjm freezero(rks[i]->user_id, rks[i]->user_id_len); 12411ac4a90aSdjm freezero(rks[i], sizeof(*rks[i])); 12421ac4a90aSdjm } 12431ac4a90aSdjm free(rks); 1244480af03fSdjm return ret; 12451ac4a90aSdjm } 12461ac4a90aSdjm 1247