1094c80e0Sdjm /* 2094c80e0Sdjm * Copyright (c) 2019 Markus Friedl 3094c80e0Sdjm * 4094c80e0Sdjm * Permission to use, copy, modify, and distribute this software for any 5094c80e0Sdjm * purpose with or without fee is hereby granted, provided that the above 6094c80e0Sdjm * copyright notice and this permission notice appear in all copies. 7094c80e0Sdjm * 8094c80e0Sdjm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9094c80e0Sdjm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10094c80e0Sdjm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11094c80e0Sdjm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12094c80e0Sdjm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13094c80e0Sdjm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14094c80e0Sdjm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15094c80e0Sdjm */ 16094c80e0Sdjm 17094c80e0Sdjm #include <stdint.h> 18094c80e0Sdjm #include <stdlib.h> 19094c80e0Sdjm #include <string.h> 20094c80e0Sdjm #include <stdio.h> 21094c80e0Sdjm #include <stddef.h> 22094c80e0Sdjm #include <stdarg.h> 23094c80e0Sdjm 24f8cd6cb1Snaddy #ifdef WITH_OPENSSL 25094c80e0Sdjm #include <openssl/opensslv.h> 26094c80e0Sdjm #include <openssl/crypto.h> 27094c80e0Sdjm #include <openssl/bn.h> 28094c80e0Sdjm #include <openssl/ec.h> 29094c80e0Sdjm #include <openssl/ecdsa.h> 30f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 31094c80e0Sdjm 32094c80e0Sdjm #include <fido.h> 331ac4a90aSdjm #include <fido/credman.h> 34094c80e0Sdjm 35094c80e0Sdjm #ifndef SK_STANDALONE 36094c80e0Sdjm #include "log.h" 37094c80e0Sdjm #include "xmalloc.h" 38094c80e0Sdjm #endif 39094c80e0Sdjm 40094c80e0Sdjm /* #define SK_DEBUG 1 */ 41094c80e0Sdjm 42094c80e0Sdjm #define MAX_FIDO_DEVICES 256 43094c80e0Sdjm 44094c80e0Sdjm /* Compatibility with OpenSSH 1.0.x */ 45094c80e0Sdjm #if (OPENSSL_VERSION_NUMBER < 0x10100000L) 46094c80e0Sdjm #define ECDSA_SIG_get0(sig, pr, ps) \ 47094c80e0Sdjm do { \ 48094c80e0Sdjm (*pr) = sig->r; \ 49094c80e0Sdjm (*ps) = sig->s; \ 50094c80e0Sdjm } while (0) 51094c80e0Sdjm #endif 52094c80e0Sdjm 532db06755Sdjm #define SK_VERSION_MAJOR 0x00030000 /* current API version */ 54094c80e0Sdjm 55094c80e0Sdjm /* Flags */ 56094c80e0Sdjm #define SK_USER_PRESENCE_REQD 0x01 578908bc36Sdjm #define SK_USER_VERIFICATION_REQD 0x04 588908bc36Sdjm #define SK_RESIDENT_KEY 0x20 59094c80e0Sdjm 60094c80e0Sdjm /* Algs */ 61094c80e0Sdjm #define SK_ECDSA 0x00 62094c80e0Sdjm #define SK_ED25519 0x01 63094c80e0Sdjm 64*480af03fSdjm /* Error codes */ 65*480af03fSdjm #define SSH_SK_ERR_GENERAL -1 66*480af03fSdjm #define SSH_SK_ERR_UNSUPPORTED -2 67*480af03fSdjm #define SSH_SK_ERR_PIN_REQUIRED -3 68*480af03fSdjm 69094c80e0Sdjm struct sk_enroll_response { 70094c80e0Sdjm uint8_t *public_key; 71094c80e0Sdjm size_t public_key_len; 72094c80e0Sdjm uint8_t *key_handle; 73094c80e0Sdjm size_t key_handle_len; 74094c80e0Sdjm uint8_t *signature; 75094c80e0Sdjm size_t signature_len; 76094c80e0Sdjm uint8_t *attestation_cert; 77094c80e0Sdjm size_t attestation_cert_len; 78094c80e0Sdjm }; 79094c80e0Sdjm 80094c80e0Sdjm struct sk_sign_response { 81094c80e0Sdjm uint8_t flags; 82094c80e0Sdjm uint32_t counter; 83094c80e0Sdjm uint8_t *sig_r; 84094c80e0Sdjm size_t sig_r_len; 85094c80e0Sdjm uint8_t *sig_s; 86094c80e0Sdjm size_t sig_s_len; 87094c80e0Sdjm }; 88094c80e0Sdjm 891ac4a90aSdjm struct sk_resident_key { 901ac4a90aSdjm uint8_t alg; 911ac4a90aSdjm size_t slot; 921ac4a90aSdjm char *application; 931ac4a90aSdjm struct sk_enroll_response key; 941ac4a90aSdjm }; 951ac4a90aSdjm 96094c80e0Sdjm /* If building as part of OpenSSH, then rename exported functions */ 97094c80e0Sdjm #if !defined(SK_STANDALONE) 98094c80e0Sdjm #define sk_api_version ssh_sk_api_version 99094c80e0Sdjm #define sk_enroll ssh_sk_enroll 100094c80e0Sdjm #define sk_sign ssh_sk_sign 1011ac4a90aSdjm #define sk_load_resident_keys ssh_sk_load_resident_keys 102094c80e0Sdjm #endif 103094c80e0Sdjm 104094c80e0Sdjm /* Return the version of the middleware API */ 105094c80e0Sdjm uint32_t sk_api_version(void); 106094c80e0Sdjm 107094c80e0Sdjm /* Enroll a U2F key (private key generation) */ 108094c80e0Sdjm int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, 1092db06755Sdjm const char *application, uint8_t flags, const char *pin, 110094c80e0Sdjm struct sk_enroll_response **enroll_response); 111094c80e0Sdjm 112094c80e0Sdjm /* Sign a challenge */ 113094c80e0Sdjm int sk_sign(int alg, const uint8_t *message, size_t message_len, 114094c80e0Sdjm const char *application, const uint8_t *key_handle, size_t key_handle_len, 1152db06755Sdjm uint8_t flags, const char *pin, struct sk_sign_response **sign_response); 116094c80e0Sdjm 1171ac4a90aSdjm /* Load resident keys */ 1181ac4a90aSdjm int sk_load_resident_keys(const char *pin, 1191ac4a90aSdjm struct sk_resident_key ***rks, size_t *nrks); 1201ac4a90aSdjm 121094c80e0Sdjm static void skdebug(const char *func, const char *fmt, ...) 122094c80e0Sdjm __attribute__((__format__ (printf, 2, 3))); 123094c80e0Sdjm 124094c80e0Sdjm static void 125094c80e0Sdjm skdebug(const char *func, const char *fmt, ...) 126094c80e0Sdjm { 127094c80e0Sdjm #if !defined(SK_STANDALONE) 128094c80e0Sdjm char *msg; 129094c80e0Sdjm va_list ap; 130094c80e0Sdjm 131094c80e0Sdjm va_start(ap, fmt); 132094c80e0Sdjm xvasprintf(&msg, fmt, ap); 133094c80e0Sdjm va_end(ap); 13459959935Sdjm debug("%s: %s", func, msg); 135094c80e0Sdjm free(msg); 136094c80e0Sdjm #elif defined(SK_DEBUG) 137094c80e0Sdjm va_list ap; 138094c80e0Sdjm 139094c80e0Sdjm va_start(ap, fmt); 140094c80e0Sdjm fprintf(stderr, "%s: ", func); 141094c80e0Sdjm vfprintf(stderr, fmt, ap); 142094c80e0Sdjm fputc('\n', stderr); 143094c80e0Sdjm va_end(ap); 144094c80e0Sdjm #else 145094c80e0Sdjm (void)func; /* XXX */ 146094c80e0Sdjm (void)fmt; /* XXX */ 147094c80e0Sdjm #endif 148094c80e0Sdjm } 149094c80e0Sdjm 150094c80e0Sdjm uint32_t 151094c80e0Sdjm sk_api_version(void) 152094c80e0Sdjm { 153094c80e0Sdjm return SK_VERSION_MAJOR; 154094c80e0Sdjm } 155094c80e0Sdjm 156094c80e0Sdjm /* Select the first identified FIDO device attached to the system */ 157094c80e0Sdjm static char * 158094c80e0Sdjm pick_first_device(void) 159094c80e0Sdjm { 160094c80e0Sdjm char *ret = NULL; 161094c80e0Sdjm fido_dev_info_t *devlist = NULL; 162094c80e0Sdjm size_t olen = 0; 163094c80e0Sdjm int r; 164094c80e0Sdjm const fido_dev_info_t *di; 165094c80e0Sdjm 166094c80e0Sdjm if ((devlist = fido_dev_info_new(1)) == NULL) { 167094c80e0Sdjm skdebug(__func__, "fido_dev_info_new failed"); 168094c80e0Sdjm goto out; 169094c80e0Sdjm } 170094c80e0Sdjm if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) { 171094c80e0Sdjm skdebug(__func__, "fido_dev_info_manifest failed: %s", 172094c80e0Sdjm fido_strerr(r)); 173094c80e0Sdjm goto out; 174094c80e0Sdjm } 175094c80e0Sdjm if (olen != 1) { 176094c80e0Sdjm skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen); 177094c80e0Sdjm goto out; 178094c80e0Sdjm } 179094c80e0Sdjm di = fido_dev_info_ptr(devlist, 0); 180094c80e0Sdjm if ((ret = strdup(fido_dev_info_path(di))) == NULL) { 181094c80e0Sdjm skdebug(__func__, "fido_dev_info_path failed"); 182094c80e0Sdjm goto out; 183094c80e0Sdjm } 184094c80e0Sdjm out: 185094c80e0Sdjm fido_dev_info_free(&devlist, 1); 186094c80e0Sdjm return ret; 187094c80e0Sdjm } 188094c80e0Sdjm 189094c80e0Sdjm /* Check if the specified key handle exists on a given device. */ 190094c80e0Sdjm static int 191094c80e0Sdjm try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, 192094c80e0Sdjm const char *application, const uint8_t *key_handle, size_t key_handle_len) 193094c80e0Sdjm { 194094c80e0Sdjm fido_assert_t *assert = NULL; 195094c80e0Sdjm int r = FIDO_ERR_INTERNAL; 196094c80e0Sdjm 197094c80e0Sdjm if ((assert = fido_assert_new()) == NULL) { 198094c80e0Sdjm skdebug(__func__, "fido_assert_new failed"); 199094c80e0Sdjm goto out; 200094c80e0Sdjm } 201094c80e0Sdjm if ((r = fido_assert_set_clientdata_hash(assert, message, 202094c80e0Sdjm message_len)) != FIDO_OK) { 203094c80e0Sdjm skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 204094c80e0Sdjm fido_strerr(r)); 205094c80e0Sdjm goto out; 206094c80e0Sdjm } 207094c80e0Sdjm if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 208094c80e0Sdjm skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 209094c80e0Sdjm goto out; 210094c80e0Sdjm } 211094c80e0Sdjm if ((r = fido_assert_allow_cred(assert, key_handle, 212094c80e0Sdjm key_handle_len)) != FIDO_OK) { 213094c80e0Sdjm skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 214094c80e0Sdjm goto out; 215094c80e0Sdjm } 216094c80e0Sdjm if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { 217094c80e0Sdjm skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); 218094c80e0Sdjm goto out; 219094c80e0Sdjm } 220094c80e0Sdjm r = fido_dev_get_assert(dev, assert, NULL); 221094c80e0Sdjm skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 2227cf8e58dSdjm if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { 2237cf8e58dSdjm /* U2F tokens may return this */ 2247cf8e58dSdjm r = FIDO_OK; 2257cf8e58dSdjm } 226094c80e0Sdjm out: 227094c80e0Sdjm fido_assert_free(&assert); 228094c80e0Sdjm 229094c80e0Sdjm return r != FIDO_OK ? -1 : 0; 230094c80e0Sdjm } 231094c80e0Sdjm 232094c80e0Sdjm /* Iterate over configured devices looking for a specific key handle */ 233094c80e0Sdjm static fido_dev_t * 234094c80e0Sdjm find_device(const uint8_t *message, size_t message_len, const char *application, 235094c80e0Sdjm const uint8_t *key_handle, size_t key_handle_len) 236094c80e0Sdjm { 237094c80e0Sdjm fido_dev_info_t *devlist = NULL; 238094c80e0Sdjm fido_dev_t *dev = NULL; 2392b479b17Sderaadt size_t devlist_len = 0, i; 240094c80e0Sdjm const char *path; 241094c80e0Sdjm int r; 242094c80e0Sdjm 243094c80e0Sdjm if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { 244094c80e0Sdjm skdebug(__func__, "fido_dev_info_new failed"); 245094c80e0Sdjm goto out; 246094c80e0Sdjm } 247094c80e0Sdjm if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, 248094c80e0Sdjm &devlist_len)) != FIDO_OK) { 249094c80e0Sdjm skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r)); 250094c80e0Sdjm goto out; 251094c80e0Sdjm } 252094c80e0Sdjm 253094c80e0Sdjm skdebug(__func__, "found %zu device(s)", devlist_len); 254094c80e0Sdjm 2552b479b17Sderaadt for (i = 0; i < devlist_len; i++) { 256094c80e0Sdjm const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); 257094c80e0Sdjm 258094c80e0Sdjm if (di == NULL) { 259094c80e0Sdjm skdebug(__func__, "fido_dev_info_ptr %zu failed", i); 260094c80e0Sdjm continue; 261094c80e0Sdjm } 262094c80e0Sdjm if ((path = fido_dev_info_path(di)) == NULL) { 263094c80e0Sdjm skdebug(__func__, "fido_dev_info_path %zu failed", i); 264094c80e0Sdjm continue; 265094c80e0Sdjm } 266094c80e0Sdjm skdebug(__func__, "trying device %zu: %s", i, path); 267094c80e0Sdjm if ((dev = fido_dev_new()) == NULL) { 268094c80e0Sdjm skdebug(__func__, "fido_dev_new failed"); 269094c80e0Sdjm continue; 270094c80e0Sdjm } 271094c80e0Sdjm if ((r = fido_dev_open(dev, path)) != FIDO_OK) { 272094c80e0Sdjm skdebug(__func__, "fido_dev_open failed"); 273094c80e0Sdjm fido_dev_free(&dev); 274094c80e0Sdjm continue; 275094c80e0Sdjm } 276094c80e0Sdjm if (try_device(dev, message, message_len, application, 277094c80e0Sdjm key_handle, key_handle_len) == 0) { 278094c80e0Sdjm skdebug(__func__, "found key"); 279094c80e0Sdjm break; 280094c80e0Sdjm } 281094c80e0Sdjm fido_dev_close(dev); 282094c80e0Sdjm fido_dev_free(&dev); 283094c80e0Sdjm } 284094c80e0Sdjm 285094c80e0Sdjm out: 286094c80e0Sdjm if (devlist != NULL) 287094c80e0Sdjm fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 288094c80e0Sdjm 289094c80e0Sdjm return dev; 290094c80e0Sdjm } 291094c80e0Sdjm 292f8cd6cb1Snaddy #ifdef WITH_OPENSSL 293094c80e0Sdjm /* 294094c80e0Sdjm * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, 295094c80e0Sdjm * but the API expects a SEC1 octet string. 296094c80e0Sdjm */ 297094c80e0Sdjm static int 2981ac4a90aSdjm pack_public_key_ecdsa(const fido_cred_t *cred, 2991ac4a90aSdjm struct sk_enroll_response *response) 300094c80e0Sdjm { 301094c80e0Sdjm const uint8_t *ptr; 302094c80e0Sdjm BIGNUM *x = NULL, *y = NULL; 303094c80e0Sdjm EC_POINT *q = NULL; 304094c80e0Sdjm EC_GROUP *g = NULL; 305094c80e0Sdjm int ret = -1; 306094c80e0Sdjm 307094c80e0Sdjm response->public_key = NULL; 308094c80e0Sdjm response->public_key_len = 0; 309094c80e0Sdjm 31007b718edSdjm if ((x = BN_new()) == NULL || 31107b718edSdjm (y = BN_new()) == NULL || 312094c80e0Sdjm (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || 313094c80e0Sdjm (q = EC_POINT_new(g)) == NULL) { 314094c80e0Sdjm skdebug(__func__, "libcrypto setup failed"); 315094c80e0Sdjm goto out; 316094c80e0Sdjm } 317094c80e0Sdjm if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 318094c80e0Sdjm skdebug(__func__, "fido_cred_pubkey_ptr failed"); 319094c80e0Sdjm goto out; 320094c80e0Sdjm } 321094c80e0Sdjm if (fido_cred_pubkey_len(cred) != 64) { 322094c80e0Sdjm skdebug(__func__, "bad fido_cred_pubkey_len %zu", 323094c80e0Sdjm fido_cred_pubkey_len(cred)); 324094c80e0Sdjm goto out; 325094c80e0Sdjm } 326094c80e0Sdjm 327094c80e0Sdjm if (BN_bin2bn(ptr, 32, x) == NULL || 328094c80e0Sdjm BN_bin2bn(ptr + 32, 32, y) == NULL) { 329094c80e0Sdjm skdebug(__func__, "BN_bin2bn failed"); 330094c80e0Sdjm goto out; 331094c80e0Sdjm } 33207b718edSdjm if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { 333094c80e0Sdjm skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); 334094c80e0Sdjm goto out; 335094c80e0Sdjm } 336094c80e0Sdjm response->public_key_len = EC_POINT_point2oct(g, q, 33707b718edSdjm POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); 338094c80e0Sdjm if (response->public_key_len == 0 || response->public_key_len > 2048) { 339094c80e0Sdjm skdebug(__func__, "bad pubkey length %zu", 340094c80e0Sdjm response->public_key_len); 341094c80e0Sdjm goto out; 342094c80e0Sdjm } 343094c80e0Sdjm if ((response->public_key = malloc(response->public_key_len)) == NULL) { 344094c80e0Sdjm skdebug(__func__, "malloc pubkey failed"); 345094c80e0Sdjm goto out; 346094c80e0Sdjm } 347094c80e0Sdjm if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, 34807b718edSdjm response->public_key, response->public_key_len, NULL) == 0) { 349094c80e0Sdjm skdebug(__func__, "EC_POINT_point2oct failed"); 350094c80e0Sdjm goto out; 351094c80e0Sdjm } 352094c80e0Sdjm /* success */ 353094c80e0Sdjm ret = 0; 354094c80e0Sdjm out: 355094c80e0Sdjm if (ret != 0 && response->public_key != NULL) { 356094c80e0Sdjm memset(response->public_key, 0, response->public_key_len); 357094c80e0Sdjm free(response->public_key); 358094c80e0Sdjm response->public_key = NULL; 359094c80e0Sdjm } 360094c80e0Sdjm EC_POINT_free(q); 361094c80e0Sdjm EC_GROUP_free(g); 36207b718edSdjm BN_clear_free(x); 36307b718edSdjm BN_clear_free(y); 364094c80e0Sdjm return ret; 365094c80e0Sdjm } 366f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 367094c80e0Sdjm 368094c80e0Sdjm static int 3691ac4a90aSdjm pack_public_key_ed25519(const fido_cred_t *cred, 3701ac4a90aSdjm struct sk_enroll_response *response) 371094c80e0Sdjm { 372094c80e0Sdjm const uint8_t *ptr; 373094c80e0Sdjm size_t len; 374094c80e0Sdjm int ret = -1; 375094c80e0Sdjm 376094c80e0Sdjm response->public_key = NULL; 377094c80e0Sdjm response->public_key_len = 0; 378094c80e0Sdjm 379094c80e0Sdjm if ((len = fido_cred_pubkey_len(cred)) != 32) { 380094c80e0Sdjm skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); 381094c80e0Sdjm goto out; 382094c80e0Sdjm } 383094c80e0Sdjm if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 384094c80e0Sdjm skdebug(__func__, "fido_cred_pubkey_ptr failed"); 385094c80e0Sdjm goto out; 386094c80e0Sdjm } 387094c80e0Sdjm response->public_key_len = len; 388094c80e0Sdjm if ((response->public_key = malloc(response->public_key_len)) == NULL) { 389094c80e0Sdjm skdebug(__func__, "malloc pubkey failed"); 390094c80e0Sdjm goto out; 391094c80e0Sdjm } 392094c80e0Sdjm memcpy(response->public_key, ptr, len); 393094c80e0Sdjm ret = 0; 394094c80e0Sdjm out: 395094c80e0Sdjm if (ret != 0) 396094c80e0Sdjm free(response->public_key); 397094c80e0Sdjm return ret; 398094c80e0Sdjm } 399094c80e0Sdjm 400094c80e0Sdjm static int 4011ac4a90aSdjm pack_public_key(int alg, const fido_cred_t *cred, 4021ac4a90aSdjm struct sk_enroll_response *response) 403094c80e0Sdjm { 404094c80e0Sdjm switch(alg) { 405f8cd6cb1Snaddy #ifdef WITH_OPENSSL 406094c80e0Sdjm case SK_ECDSA: 407094c80e0Sdjm return pack_public_key_ecdsa(cred, response); 408f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 409094c80e0Sdjm case SK_ED25519: 410094c80e0Sdjm return pack_public_key_ed25519(cred, response); 411094c80e0Sdjm default: 412094c80e0Sdjm return -1; 413094c80e0Sdjm } 414094c80e0Sdjm } 415094c80e0Sdjm 416*480af03fSdjm static int 417*480af03fSdjm fidoerr_to_skerr(int fidoerr) 418*480af03fSdjm { 419*480af03fSdjm switch (fidoerr) { 420*480af03fSdjm case FIDO_ERR_UNSUPPORTED_OPTION: 421*480af03fSdjm return SSH_SK_ERR_UNSUPPORTED; 422*480af03fSdjm case FIDO_ERR_PIN_REQUIRED: 423*480af03fSdjm case FIDO_ERR_PIN_INVALID: 424*480af03fSdjm return SSH_SK_ERR_PIN_REQUIRED; 425*480af03fSdjm default: 426*480af03fSdjm return -1; 427*480af03fSdjm } 428*480af03fSdjm } 429*480af03fSdjm 430094c80e0Sdjm int 431094c80e0Sdjm sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, 4322db06755Sdjm const char *application, uint8_t flags, const char *pin, 43335f0234cSmarkus struct sk_enroll_response **enroll_response) 434094c80e0Sdjm { 435094c80e0Sdjm fido_cred_t *cred = NULL; 436094c80e0Sdjm fido_dev_t *dev = NULL; 437094c80e0Sdjm const uint8_t *ptr; 438094c80e0Sdjm uint8_t user_id[32]; 439094c80e0Sdjm struct sk_enroll_response *response = NULL; 440094c80e0Sdjm size_t len; 441094c80e0Sdjm int cose_alg; 442*480af03fSdjm int ret = SSH_SK_ERR_GENERAL; 443094c80e0Sdjm int r; 444094c80e0Sdjm char *device = NULL; 445094c80e0Sdjm 446094c80e0Sdjm #ifdef SK_DEBUG 447094c80e0Sdjm fido_init(FIDO_DEBUG); 448094c80e0Sdjm #endif 44935f0234cSmarkus if (enroll_response == NULL) { 45035f0234cSmarkus skdebug(__func__, "enroll_response == NULL"); 451094c80e0Sdjm goto out; 452094c80e0Sdjm } 45335f0234cSmarkus *enroll_response = NULL; 454094c80e0Sdjm switch(alg) { 455f8cd6cb1Snaddy #ifdef WITH_OPENSSL 456094c80e0Sdjm case SK_ECDSA: 457094c80e0Sdjm cose_alg = COSE_ES256; 458094c80e0Sdjm break; 459f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 460094c80e0Sdjm case SK_ED25519: 461094c80e0Sdjm cose_alg = COSE_EDDSA; 462094c80e0Sdjm break; 463094c80e0Sdjm default: 464094c80e0Sdjm skdebug(__func__, "unsupported key type %d", alg); 465094c80e0Sdjm goto out; 466094c80e0Sdjm } 467094c80e0Sdjm if ((device = pick_first_device()) == NULL) { 468094c80e0Sdjm skdebug(__func__, "pick_first_device failed"); 469094c80e0Sdjm goto out; 470094c80e0Sdjm } 471094c80e0Sdjm skdebug(__func__, "using device %s", device); 472094c80e0Sdjm if ((cred = fido_cred_new()) == NULL) { 473094c80e0Sdjm skdebug(__func__, "fido_cred_new failed"); 474094c80e0Sdjm goto out; 475094c80e0Sdjm } 476094c80e0Sdjm memset(user_id, 0, sizeof(user_id)); 477094c80e0Sdjm if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { 478094c80e0Sdjm skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); 479094c80e0Sdjm goto out; 480094c80e0Sdjm } 481094c80e0Sdjm if ((r = fido_cred_set_clientdata_hash(cred, challenge, 482094c80e0Sdjm challenge_len)) != FIDO_OK) { 483094c80e0Sdjm skdebug(__func__, "fido_cred_set_clientdata_hash: %s", 484094c80e0Sdjm fido_strerr(r)); 485094c80e0Sdjm goto out; 486094c80e0Sdjm } 4878908bc36Sdjm if ((r = fido_cred_set_rk(cred, (flags & SK_RESIDENT_KEY) != 0 ? 4888908bc36Sdjm FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) { 4898908bc36Sdjm skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r)); 4908908bc36Sdjm goto out; 4918908bc36Sdjm } 492094c80e0Sdjm if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), 493094c80e0Sdjm "openssh", "openssh", NULL)) != FIDO_OK) { 494094c80e0Sdjm skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); 495094c80e0Sdjm goto out; 496094c80e0Sdjm } 497094c80e0Sdjm if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { 498094c80e0Sdjm skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); 499094c80e0Sdjm goto out; 500094c80e0Sdjm } 501094c80e0Sdjm if ((dev = fido_dev_new()) == NULL) { 502094c80e0Sdjm skdebug(__func__, "fido_dev_new failed"); 503094c80e0Sdjm goto out; 504094c80e0Sdjm } 505094c80e0Sdjm if ((r = fido_dev_open(dev, device)) != FIDO_OK) { 506094c80e0Sdjm skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); 507094c80e0Sdjm goto out; 508094c80e0Sdjm } 509*480af03fSdjm if ((r = fido_dev_make_cred(dev, cred, pin)) != FIDO_OK) { 510094c80e0Sdjm skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); 511*480af03fSdjm ret = fidoerr_to_skerr(r); 512094c80e0Sdjm goto out; 513094c80e0Sdjm } 514094c80e0Sdjm if (fido_cred_x5c_ptr(cred) != NULL) { 515094c80e0Sdjm if ((r = fido_cred_verify(cred)) != FIDO_OK) { 516094c80e0Sdjm skdebug(__func__, "fido_cred_verify: %s", 517094c80e0Sdjm fido_strerr(r)); 518094c80e0Sdjm goto out; 519094c80e0Sdjm } 520094c80e0Sdjm } else { 521094c80e0Sdjm skdebug(__func__, "self-attested credential"); 522094c80e0Sdjm if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { 523094c80e0Sdjm skdebug(__func__, "fido_cred_verify_self: %s", 524094c80e0Sdjm fido_strerr(r)); 525094c80e0Sdjm goto out; 526094c80e0Sdjm } 527094c80e0Sdjm } 528094c80e0Sdjm if ((response = calloc(1, sizeof(*response))) == NULL) { 529094c80e0Sdjm skdebug(__func__, "calloc response failed"); 530094c80e0Sdjm goto out; 531094c80e0Sdjm } 532094c80e0Sdjm if (pack_public_key(alg, cred, response) != 0) { 533094c80e0Sdjm skdebug(__func__, "pack_public_key failed"); 534094c80e0Sdjm goto out; 535094c80e0Sdjm } 536094c80e0Sdjm if ((ptr = fido_cred_id_ptr(cred)) != NULL) { 537094c80e0Sdjm len = fido_cred_id_len(cred); 538094c80e0Sdjm if ((response->key_handle = calloc(1, len)) == NULL) { 539094c80e0Sdjm skdebug(__func__, "calloc key handle failed"); 540094c80e0Sdjm goto out; 541094c80e0Sdjm } 542094c80e0Sdjm memcpy(response->key_handle, ptr, len); 543094c80e0Sdjm response->key_handle_len = len; 544094c80e0Sdjm } 545094c80e0Sdjm if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { 546094c80e0Sdjm len = fido_cred_sig_len(cred); 547094c80e0Sdjm if ((response->signature = calloc(1, len)) == NULL) { 548094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 549094c80e0Sdjm goto out; 550094c80e0Sdjm } 551094c80e0Sdjm memcpy(response->signature, ptr, len); 552094c80e0Sdjm response->signature_len = len; 553094c80e0Sdjm } 554094c80e0Sdjm if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { 555094c80e0Sdjm len = fido_cred_x5c_len(cred); 556094c80e0Sdjm if ((response->attestation_cert = calloc(1, len)) == NULL) { 557094c80e0Sdjm skdebug(__func__, "calloc attestation cert failed"); 558094c80e0Sdjm goto out; 559094c80e0Sdjm } 560094c80e0Sdjm memcpy(response->attestation_cert, ptr, len); 561094c80e0Sdjm response->attestation_cert_len = len; 562094c80e0Sdjm } 56335f0234cSmarkus *enroll_response = response; 564094c80e0Sdjm response = NULL; 565094c80e0Sdjm ret = 0; 566094c80e0Sdjm out: 567094c80e0Sdjm free(device); 568094c80e0Sdjm if (response != NULL) { 569094c80e0Sdjm free(response->public_key); 570094c80e0Sdjm free(response->key_handle); 571094c80e0Sdjm free(response->signature); 572094c80e0Sdjm free(response->attestation_cert); 573094c80e0Sdjm free(response); 574094c80e0Sdjm } 575094c80e0Sdjm if (dev != NULL) { 576094c80e0Sdjm fido_dev_close(dev); 577094c80e0Sdjm fido_dev_free(&dev); 578094c80e0Sdjm } 579094c80e0Sdjm if (cred != NULL) { 580094c80e0Sdjm fido_cred_free(&cred); 581094c80e0Sdjm } 582094c80e0Sdjm return ret; 583094c80e0Sdjm } 584094c80e0Sdjm 585f8cd6cb1Snaddy #ifdef WITH_OPENSSL 586094c80e0Sdjm static int 587094c80e0Sdjm pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) 588094c80e0Sdjm { 589094c80e0Sdjm ECDSA_SIG *sig = NULL; 590094c80e0Sdjm const BIGNUM *sig_r, *sig_s; 591094c80e0Sdjm const unsigned char *cp; 592094c80e0Sdjm size_t sig_len; 593094c80e0Sdjm int ret = -1; 594094c80e0Sdjm 595094c80e0Sdjm cp = fido_assert_sig_ptr(assert, 0); 596094c80e0Sdjm sig_len = fido_assert_sig_len(assert, 0); 597094c80e0Sdjm if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { 598094c80e0Sdjm skdebug(__func__, "d2i_ECDSA_SIG failed"); 599094c80e0Sdjm goto out; 600094c80e0Sdjm } 601094c80e0Sdjm ECDSA_SIG_get0(sig, &sig_r, &sig_s); 602094c80e0Sdjm response->sig_r_len = BN_num_bytes(sig_r); 603094c80e0Sdjm response->sig_s_len = BN_num_bytes(sig_s); 604094c80e0Sdjm if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || 605094c80e0Sdjm (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { 606094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 607094c80e0Sdjm goto out; 608094c80e0Sdjm } 609094c80e0Sdjm BN_bn2bin(sig_r, response->sig_r); 610094c80e0Sdjm BN_bn2bin(sig_s, response->sig_s); 611094c80e0Sdjm ret = 0; 612094c80e0Sdjm out: 613094c80e0Sdjm ECDSA_SIG_free(sig); 614094c80e0Sdjm if (ret != 0) { 615094c80e0Sdjm free(response->sig_r); 616094c80e0Sdjm free(response->sig_s); 617094c80e0Sdjm response->sig_r = NULL; 618094c80e0Sdjm response->sig_s = NULL; 619094c80e0Sdjm } 620094c80e0Sdjm return ret; 621094c80e0Sdjm } 622f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 623094c80e0Sdjm 624094c80e0Sdjm static int 625094c80e0Sdjm pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) 626094c80e0Sdjm { 627094c80e0Sdjm const unsigned char *ptr; 628094c80e0Sdjm size_t len; 629094c80e0Sdjm int ret = -1; 630094c80e0Sdjm 631094c80e0Sdjm ptr = fido_assert_sig_ptr(assert, 0); 632094c80e0Sdjm len = fido_assert_sig_len(assert, 0); 633094c80e0Sdjm if (len != 64) { 634094c80e0Sdjm skdebug(__func__, "bad length %zu", len); 635094c80e0Sdjm goto out; 636094c80e0Sdjm } 637094c80e0Sdjm response->sig_r_len = len; 638094c80e0Sdjm if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { 639094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 640094c80e0Sdjm goto out; 641094c80e0Sdjm } 642094c80e0Sdjm memcpy(response->sig_r, ptr, len); 643094c80e0Sdjm ret = 0; 644094c80e0Sdjm out: 645094c80e0Sdjm if (ret != 0) { 646094c80e0Sdjm free(response->sig_r); 647094c80e0Sdjm response->sig_r = NULL; 648094c80e0Sdjm } 649094c80e0Sdjm return ret; 650094c80e0Sdjm } 651094c80e0Sdjm 652094c80e0Sdjm static int 653094c80e0Sdjm pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) 654094c80e0Sdjm { 655094c80e0Sdjm switch(alg) { 656f8cd6cb1Snaddy #ifdef WITH_OPENSSL 657094c80e0Sdjm case SK_ECDSA: 658094c80e0Sdjm return pack_sig_ecdsa(assert, response); 659f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 660094c80e0Sdjm case SK_ED25519: 661094c80e0Sdjm return pack_sig_ed25519(assert, response); 662094c80e0Sdjm default: 663094c80e0Sdjm return -1; 664094c80e0Sdjm } 665094c80e0Sdjm } 666094c80e0Sdjm 667094c80e0Sdjm int 668094c80e0Sdjm sk_sign(int alg, const uint8_t *message, size_t message_len, 669094c80e0Sdjm const char *application, 670094c80e0Sdjm const uint8_t *key_handle, size_t key_handle_len, 6712db06755Sdjm uint8_t flags, const char *pin, struct sk_sign_response **sign_response) 672094c80e0Sdjm { 673094c80e0Sdjm fido_assert_t *assert = NULL; 674094c80e0Sdjm fido_dev_t *dev = NULL; 675094c80e0Sdjm struct sk_sign_response *response = NULL; 676*480af03fSdjm int ret = SSH_SK_ERR_GENERAL; 677094c80e0Sdjm int r; 678094c80e0Sdjm 679094c80e0Sdjm #ifdef SK_DEBUG 680094c80e0Sdjm fido_init(FIDO_DEBUG); 681094c80e0Sdjm #endif 682094c80e0Sdjm 683094c80e0Sdjm if (sign_response == NULL) { 684094c80e0Sdjm skdebug(__func__, "sign_response == NULL"); 685094c80e0Sdjm goto out; 686094c80e0Sdjm } 687094c80e0Sdjm *sign_response = NULL; 688094c80e0Sdjm if ((dev = find_device(message, message_len, application, key_handle, 689094c80e0Sdjm key_handle_len)) == NULL) { 690094c80e0Sdjm skdebug(__func__, "couldn't find device for key handle"); 691094c80e0Sdjm goto out; 692094c80e0Sdjm } 693094c80e0Sdjm if ((assert = fido_assert_new()) == NULL) { 694094c80e0Sdjm skdebug(__func__, "fido_assert_new failed"); 695094c80e0Sdjm goto out; 696094c80e0Sdjm } 697094c80e0Sdjm if ((r = fido_assert_set_clientdata_hash(assert, message, 698094c80e0Sdjm message_len)) != FIDO_OK) { 699094c80e0Sdjm skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 700094c80e0Sdjm fido_strerr(r)); 701094c80e0Sdjm goto out; 702094c80e0Sdjm } 703094c80e0Sdjm if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 704094c80e0Sdjm skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 705094c80e0Sdjm goto out; 706094c80e0Sdjm } 707094c80e0Sdjm if ((r = fido_assert_allow_cred(assert, key_handle, 708094c80e0Sdjm key_handle_len)) != FIDO_OK) { 709094c80e0Sdjm skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 710094c80e0Sdjm goto out; 711094c80e0Sdjm } 712094c80e0Sdjm if ((r = fido_assert_set_up(assert, 713094c80e0Sdjm (flags & SK_USER_PRESENCE_REQD) ? 714094c80e0Sdjm FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { 715094c80e0Sdjm skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); 716094c80e0Sdjm goto out; 717094c80e0Sdjm } 718094c80e0Sdjm if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) { 719094c80e0Sdjm skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 720094c80e0Sdjm goto out; 721094c80e0Sdjm } 722094c80e0Sdjm if ((response = calloc(1, sizeof(*response))) == NULL) { 723094c80e0Sdjm skdebug(__func__, "calloc response failed"); 724094c80e0Sdjm goto out; 725094c80e0Sdjm } 726094c80e0Sdjm response->flags = fido_assert_flags(assert, 0); 727094c80e0Sdjm response->counter = fido_assert_sigcount(assert, 0); 728094c80e0Sdjm if (pack_sig(alg, assert, response) != 0) { 729094c80e0Sdjm skdebug(__func__, "pack_sig failed"); 730094c80e0Sdjm goto out; 731094c80e0Sdjm } 732094c80e0Sdjm *sign_response = response; 733094c80e0Sdjm response = NULL; 734094c80e0Sdjm ret = 0; 735094c80e0Sdjm out: 736094c80e0Sdjm if (response != NULL) { 737094c80e0Sdjm free(response->sig_r); 738094c80e0Sdjm free(response->sig_s); 739094c80e0Sdjm free(response); 740094c80e0Sdjm } 741094c80e0Sdjm if (dev != NULL) { 742094c80e0Sdjm fido_dev_close(dev); 743094c80e0Sdjm fido_dev_free(&dev); 744094c80e0Sdjm } 745094c80e0Sdjm if (assert != NULL) { 746094c80e0Sdjm fido_assert_free(&assert); 747094c80e0Sdjm } 748094c80e0Sdjm return ret; 749094c80e0Sdjm } 7501ac4a90aSdjm 7511ac4a90aSdjm static int 7521ac4a90aSdjm read_rks(const char *devpath, const char *pin, 7531ac4a90aSdjm struct sk_resident_key ***rksp, size_t *nrksp) 7541ac4a90aSdjm { 755*480af03fSdjm int ret = SSH_SK_ERR_GENERAL, r = -1; 7561ac4a90aSdjm fido_dev_t *dev = NULL; 7571ac4a90aSdjm fido_credman_metadata_t *metadata = NULL; 7581ac4a90aSdjm fido_credman_rp_t *rp = NULL; 7591ac4a90aSdjm fido_credman_rk_t *rk = NULL; 7601ac4a90aSdjm size_t i, j, nrp, nrk; 7611ac4a90aSdjm const fido_cred_t *cred; 7621ac4a90aSdjm struct sk_resident_key *srk = NULL, **tmp; 7631ac4a90aSdjm 7641ac4a90aSdjm if ((dev = fido_dev_new()) == NULL) { 7651ac4a90aSdjm skdebug(__func__, "fido_dev_new failed"); 766*480af03fSdjm return ret; 7671ac4a90aSdjm } 7681ac4a90aSdjm if ((r = fido_dev_open(dev, devpath)) != FIDO_OK) { 7691ac4a90aSdjm skdebug(__func__, "fido_dev_open %s failed: %s", 7701ac4a90aSdjm devpath, fido_strerr(r)); 7711ac4a90aSdjm fido_dev_free(&dev); 772*480af03fSdjm return ret; 7731ac4a90aSdjm } 7741ac4a90aSdjm if ((metadata = fido_credman_metadata_new()) == NULL) { 7751ac4a90aSdjm skdebug(__func__, "alloc failed"); 7761ac4a90aSdjm goto out; 7771ac4a90aSdjm } 7781ac4a90aSdjm 7791ac4a90aSdjm if ((r = fido_credman_get_dev_metadata(dev, metadata, pin)) != 0) { 7801ac4a90aSdjm if (r == FIDO_ERR_INVALID_COMMAND) { 7811ac4a90aSdjm skdebug(__func__, "device %s does not support " 7821ac4a90aSdjm "resident keys", devpath); 783*480af03fSdjm ret = 0; 7841ac4a90aSdjm goto out; 7851ac4a90aSdjm } 7861ac4a90aSdjm skdebug(__func__, "get metadata for %s failed: %s", 7871ac4a90aSdjm devpath, fido_strerr(r)); 7881ac4a90aSdjm goto out; 7891ac4a90aSdjm } 7901ac4a90aSdjm skdebug(__func__, "existing %llu, remaining %llu", 7911ac4a90aSdjm (unsigned long long)fido_credman_rk_existing(metadata), 7921ac4a90aSdjm (unsigned long long)fido_credman_rk_remaining(metadata)); 7931ac4a90aSdjm if ((rp = fido_credman_rp_new()) == NULL) { 7941ac4a90aSdjm skdebug(__func__, "alloc rp failed"); 7951ac4a90aSdjm goto out; 7961ac4a90aSdjm } 7971ac4a90aSdjm if ((r = fido_credman_get_dev_rp(dev, rp, pin)) != 0) { 7981ac4a90aSdjm skdebug(__func__, "get RPs for %s failed: %s", 7991ac4a90aSdjm devpath, fido_strerr(r)); 8001ac4a90aSdjm goto out; 8011ac4a90aSdjm } 8021ac4a90aSdjm nrp = fido_credman_rp_count(rp); 8031ac4a90aSdjm skdebug(__func__, "Device %s has resident keys for %zu RPs", 8041ac4a90aSdjm devpath, nrp); 8051ac4a90aSdjm 8061ac4a90aSdjm /* Iterate over RP IDs that have resident keys */ 8071ac4a90aSdjm for (i = 0; i < nrp; i++) { 8081ac4a90aSdjm skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu", 8091ac4a90aSdjm i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i), 8101ac4a90aSdjm fido_credman_rp_id_hash_len(rp, i)); 8111ac4a90aSdjm 8121ac4a90aSdjm /* Skip non-SSH RP IDs */ 8131ac4a90aSdjm if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0) 8141ac4a90aSdjm continue; 8151ac4a90aSdjm 8161ac4a90aSdjm fido_credman_rk_free(&rk); 8171ac4a90aSdjm if ((rk = fido_credman_rk_new()) == NULL) { 8181ac4a90aSdjm skdebug(__func__, "alloc rk failed"); 8191ac4a90aSdjm goto out; 8201ac4a90aSdjm } 8211ac4a90aSdjm if ((r = fido_credman_get_dev_rk(dev, fido_credman_rp_id(rp, i), 8221ac4a90aSdjm rk, pin)) != 0) { 8231ac4a90aSdjm skdebug(__func__, "get RKs for %s slot %zu failed: %s", 8241ac4a90aSdjm devpath, i, fido_strerr(r)); 8251ac4a90aSdjm goto out; 8261ac4a90aSdjm } 8271ac4a90aSdjm nrk = fido_credman_rk_count(rk); 8281ac4a90aSdjm skdebug(__func__, "RP \"%s\" has %zu resident keys", 8291ac4a90aSdjm fido_credman_rp_id(rp, i), nrk); 8301ac4a90aSdjm 8311ac4a90aSdjm /* Iterate over resident keys for this RP ID */ 8321ac4a90aSdjm for (j = 0; j < nrk; j++) { 8331ac4a90aSdjm if ((cred = fido_credman_rk(rk, j)) == NULL) { 8341ac4a90aSdjm skdebug(__func__, "no RK in slot %zu", j); 8351ac4a90aSdjm continue; 8361ac4a90aSdjm } 8371ac4a90aSdjm skdebug(__func__, "Device %s RP \"%s\" slot %zu: " 8381ac4a90aSdjm "type %d", devpath, fido_credman_rp_id(rp, i), j, 8391ac4a90aSdjm fido_cred_type(cred)); 8401ac4a90aSdjm 8411ac4a90aSdjm /* build response entry */ 8421ac4a90aSdjm if ((srk = calloc(1, sizeof(*srk))) == NULL || 8431ac4a90aSdjm (srk->key.key_handle = calloc(1, 8441ac4a90aSdjm fido_cred_id_len(cred))) == NULL || 8451ac4a90aSdjm (srk->application = strdup(fido_credman_rp_id(rp, 8461ac4a90aSdjm i))) == NULL) { 8471ac4a90aSdjm skdebug(__func__, "alloc sk_resident_key"); 8481ac4a90aSdjm goto out; 8491ac4a90aSdjm } 8501ac4a90aSdjm 8511ac4a90aSdjm srk->key.key_handle_len = fido_cred_id_len(cred); 8521ac4a90aSdjm memcpy(srk->key.key_handle, 8531ac4a90aSdjm fido_cred_id_ptr(cred), 8541ac4a90aSdjm srk->key.key_handle_len); 8551ac4a90aSdjm 8561ac4a90aSdjm switch (fido_cred_type(cred)) { 8571ac4a90aSdjm case COSE_ES256: 8581ac4a90aSdjm srk->alg = SK_ECDSA; 8591ac4a90aSdjm break; 8601ac4a90aSdjm case COSE_EDDSA: 8611ac4a90aSdjm srk->alg = SK_ED25519; 8621ac4a90aSdjm break; 8631ac4a90aSdjm default: 8641ac4a90aSdjm skdebug(__func__, "unsupported key type %d", 8651ac4a90aSdjm fido_cred_type(cred)); 8661ac4a90aSdjm goto out; 8671ac4a90aSdjm } 8681ac4a90aSdjm 8691ac4a90aSdjm if ((r = pack_public_key(srk->alg, cred, 8701ac4a90aSdjm &srk->key)) != 0) { 8711ac4a90aSdjm skdebug(__func__, "pack public key failed"); 8721ac4a90aSdjm goto out; 8731ac4a90aSdjm } 8741ac4a90aSdjm /* append */ 8751ac4a90aSdjm if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1, 8761ac4a90aSdjm sizeof(**rksp))) == NULL) { 8771ac4a90aSdjm skdebug(__func__, "alloc rksp"); 8781ac4a90aSdjm goto out; 8791ac4a90aSdjm } 8801ac4a90aSdjm *rksp = tmp; 8811ac4a90aSdjm (*rksp)[(*nrksp)++] = srk; 8821ac4a90aSdjm srk = NULL; 8831ac4a90aSdjm } 8841ac4a90aSdjm } 8851ac4a90aSdjm /* Success */ 886*480af03fSdjm ret = 0; 8871ac4a90aSdjm out: 8881ac4a90aSdjm if (srk != NULL) { 8891ac4a90aSdjm free(srk->application); 8901ac4a90aSdjm freezero(srk->key.public_key, srk->key.public_key_len); 8911ac4a90aSdjm freezero(srk->key.key_handle, srk->key.key_handle_len); 8921ac4a90aSdjm freezero(srk, sizeof(*srk)); 8931ac4a90aSdjm } 8941ac4a90aSdjm fido_credman_rp_free(&rp); 8951ac4a90aSdjm fido_credman_rk_free(&rk); 8961ac4a90aSdjm fido_dev_close(dev); 8971ac4a90aSdjm fido_dev_free(&dev); 8981ac4a90aSdjm fido_credman_metadata_free(&metadata); 899*480af03fSdjm return ret; 9001ac4a90aSdjm } 9011ac4a90aSdjm 9021ac4a90aSdjm int 9031ac4a90aSdjm sk_load_resident_keys(const char *pin, 9041ac4a90aSdjm struct sk_resident_key ***rksp, size_t *nrksp) 9051ac4a90aSdjm { 906*480af03fSdjm int ret = SSH_SK_ERR_GENERAL, r = -1; 9071ac4a90aSdjm fido_dev_info_t *devlist = NULL; 9081ac4a90aSdjm size_t i, ndev = 0, nrks = 0; 9091ac4a90aSdjm const fido_dev_info_t *di; 9101ac4a90aSdjm struct sk_resident_key **rks = NULL; 9111ac4a90aSdjm *rksp = NULL; 9121ac4a90aSdjm *nrksp = 0; 9131ac4a90aSdjm 9141ac4a90aSdjm if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { 9151ac4a90aSdjm skdebug(__func__, "fido_dev_info_new failed"); 9161ac4a90aSdjm goto out; 9171ac4a90aSdjm } 9181ac4a90aSdjm if ((r = fido_dev_info_manifest(devlist, 9191ac4a90aSdjm MAX_FIDO_DEVICES, &ndev)) != FIDO_OK) { 9201ac4a90aSdjm skdebug(__func__, "fido_dev_info_manifest failed: %s", 9211ac4a90aSdjm fido_strerr(r)); 9221ac4a90aSdjm goto out; 9231ac4a90aSdjm } 9241ac4a90aSdjm for (i = 0; i < ndev; i++) { 9251ac4a90aSdjm if ((di = fido_dev_info_ptr(devlist, i)) == NULL) { 9261ac4a90aSdjm skdebug(__func__, "no dev info at %zu", i); 9271ac4a90aSdjm continue; 9281ac4a90aSdjm } 9291ac4a90aSdjm skdebug(__func__, "trying %s", fido_dev_info_path(di)); 9301ac4a90aSdjm if ((r = read_rks(fido_dev_info_path(di), pin, 9311ac4a90aSdjm &rks, &nrks)) != 0) { 9321ac4a90aSdjm skdebug(__func__, "read_rks failed for %s", 9331ac4a90aSdjm fido_dev_info_path(di)); 9341ac4a90aSdjm continue; 9351ac4a90aSdjm } 9361ac4a90aSdjm } 9371ac4a90aSdjm /* success */ 938*480af03fSdjm ret = 0; 9391ac4a90aSdjm *rksp = rks; 9401ac4a90aSdjm *nrksp = nrks; 9411ac4a90aSdjm rks = NULL; 9421ac4a90aSdjm nrks = 0; 9431ac4a90aSdjm out: 9441ac4a90aSdjm for (i = 0; i < nrks; i++) { 9451ac4a90aSdjm free(rks[i]->application); 9461ac4a90aSdjm freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); 9471ac4a90aSdjm freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); 9481ac4a90aSdjm freezero(rks[i], sizeof(*rks[i])); 9491ac4a90aSdjm } 9501ac4a90aSdjm free(rks); 9511ac4a90aSdjm fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 952*480af03fSdjm return ret; 9531ac4a90aSdjm } 9541ac4a90aSdjm 955