1*5411e769Sdjm /* $OpenBSD: ssh-sk.c,v 1.41 2024/08/15 00:51:51 djm Exp $ */ 277045ccdSdjm /* 377045ccdSdjm * Copyright (c) 2019 Google LLC 477045ccdSdjm * 577045ccdSdjm * Permission to use, copy, modify, and distribute this software for any 677045ccdSdjm * purpose with or without fee is hereby granted, provided that the above 777045ccdSdjm * copyright notice and this permission notice appear in all copies. 877045ccdSdjm * 977045ccdSdjm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1077045ccdSdjm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1177045ccdSdjm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1277045ccdSdjm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1377045ccdSdjm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1477045ccdSdjm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1577045ccdSdjm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1677045ccdSdjm */ 1777045ccdSdjm 1877045ccdSdjm /* #define DEBUG_SK 1 */ 1977045ccdSdjm 2077045ccdSdjm #include <dlfcn.h> 2177045ccdSdjm #include <stddef.h> 2277045ccdSdjm #include <stdint.h> 2377045ccdSdjm #include <string.h> 2477045ccdSdjm #include <stdio.h> 2577045ccdSdjm 26f8cd6cb1Snaddy #ifdef WITH_OPENSSL 2777045ccdSdjm #include <openssl/objects.h> 2877045ccdSdjm #include <openssl/ec.h> 29*5411e769Sdjm #include <openssl/evp.h> 30f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 3177045ccdSdjm 3277045ccdSdjm #include "log.h" 3377045ccdSdjm #include "misc.h" 3477045ccdSdjm #include "sshbuf.h" 3577045ccdSdjm #include "sshkey.h" 3677045ccdSdjm #include "ssherr.h" 3777045ccdSdjm #include "digest.h" 3877045ccdSdjm 3977045ccdSdjm #include "ssh-sk.h" 4077045ccdSdjm #include "sk-api.h" 41af4c80eeSmarkus #include "crypto_api.h" 4277045ccdSdjm 4377045ccdSdjm struct sshsk_provider { 4477045ccdSdjm char *path; 4577045ccdSdjm void *dlhandle; 4677045ccdSdjm 4777045ccdSdjm /* Return the version of the middleware API */ 4877045ccdSdjm uint32_t (*sk_api_version)(void); 4977045ccdSdjm 5077045ccdSdjm /* Enroll a U2F key (private key generation) */ 51a3dd6837Smarkus int (*sk_enroll)(int alg, const uint8_t *challenge, 52a3dd6837Smarkus size_t challenge_len, const char *application, uint8_t flags, 53a0caf565Sdjm const char *pin, struct sk_option **opts, 54a0caf565Sdjm struct sk_enroll_response **enroll_response); 5577045ccdSdjm 5677045ccdSdjm /* Sign a challenge */ 57a3dd6837Smarkus int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, 5877045ccdSdjm const char *application, 5977045ccdSdjm const uint8_t *key_handle, size_t key_handle_len, 60a0caf565Sdjm uint8_t flags, const char *pin, struct sk_option **opts, 612db06755Sdjm struct sk_sign_response **sign_response); 621ac4a90aSdjm 631ac4a90aSdjm /* Enumerate resident keys */ 64a0caf565Sdjm int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts, 651ac4a90aSdjm struct sk_resident_key ***rks, size_t *nrks); 6677045ccdSdjm }; 6777045ccdSdjm 68094c80e0Sdjm /* Built-in version */ 69094c80e0Sdjm int ssh_sk_enroll(int alg, const uint8_t *challenge, 70094c80e0Sdjm size_t challenge_len, const char *application, uint8_t flags, 71a0caf565Sdjm const char *pin, struct sk_option **opts, 72a0caf565Sdjm struct sk_enroll_response **enroll_response); 73094c80e0Sdjm int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, 74094c80e0Sdjm const char *application, 75094c80e0Sdjm const uint8_t *key_handle, size_t key_handle_len, 76a0caf565Sdjm uint8_t flags, const char *pin, struct sk_option **opts, 77a0caf565Sdjm struct sk_sign_response **sign_response); 78a0caf565Sdjm int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts, 791ac4a90aSdjm struct sk_resident_key ***rks, size_t *nrks); 80094c80e0Sdjm 8177045ccdSdjm static void 8277045ccdSdjm sshsk_free(struct sshsk_provider *p) 8377045ccdSdjm { 8477045ccdSdjm if (p == NULL) 8577045ccdSdjm return; 8677045ccdSdjm free(p->path); 8777045ccdSdjm if (p->dlhandle != NULL) 8877045ccdSdjm dlclose(p->dlhandle); 8977045ccdSdjm free(p); 9077045ccdSdjm } 9177045ccdSdjm 9277045ccdSdjm static struct sshsk_provider * 9377045ccdSdjm sshsk_open(const char *path) 9477045ccdSdjm { 9577045ccdSdjm struct sshsk_provider *ret = NULL; 9677045ccdSdjm uint32_t version; 9777045ccdSdjm 9850c28975Sdjm if (path == NULL || *path == '\0') { 9950c28975Sdjm error("No FIDO SecurityKeyProvider specified"); 10050c28975Sdjm return NULL; 10150c28975Sdjm } 10277045ccdSdjm if ((ret = calloc(1, sizeof(*ret))) == NULL) { 10348e6b99dSdjm error_f("calloc failed"); 10477045ccdSdjm return NULL; 10577045ccdSdjm } 10677045ccdSdjm if ((ret->path = strdup(path)) == NULL) { 10748e6b99dSdjm error_f("strdup failed"); 10877045ccdSdjm goto fail; 10977045ccdSdjm } 110094c80e0Sdjm /* Skip the rest if we're using the linked in middleware */ 111094c80e0Sdjm if (strcasecmp(ret->path, "internal") == 0) { 112094c80e0Sdjm ret->sk_enroll = ssh_sk_enroll; 113094c80e0Sdjm ret->sk_sign = ssh_sk_sign; 1141ac4a90aSdjm ret->sk_load_resident_keys = ssh_sk_load_resident_keys; 115094c80e0Sdjm return ret; 116094c80e0Sdjm } 117f8f5a6b0Sdjm if (lib_contains_symbol(path, "sk_api_version") != 0) { 118f8f5a6b0Sdjm error("provider %s is not an OpenSSH FIDO library", path); 119f8f5a6b0Sdjm goto fail; 120f8f5a6b0Sdjm } 12177045ccdSdjm if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) { 1225f47a660Snaddy error("Provider \"%s\" dlopen failed: %s", path, dlerror()); 12377045ccdSdjm goto fail; 12477045ccdSdjm } 12577045ccdSdjm if ((ret->sk_api_version = dlsym(ret->dlhandle, 12677045ccdSdjm "sk_api_version")) == NULL) { 127f8f5a6b0Sdjm fatal("Provider \"%s\" dlsym(sk_api_version) failed: %s", 1285f47a660Snaddy path, dlerror()); 12977045ccdSdjm } 13077045ccdSdjm version = ret->sk_api_version(); 13148e6b99dSdjm debug_f("provider %s implements version 0x%08lx", ret->path, 13248e6b99dSdjm (u_long)version); 13377045ccdSdjm if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) { 1345f47a660Snaddy error("Provider \"%s\" implements unsupported " 13529322520Sdjm "version 0x%08lx (supported: 0x%08lx)", 13629322520Sdjm path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR); 13777045ccdSdjm goto fail; 13877045ccdSdjm } 13977045ccdSdjm if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) { 1405f47a660Snaddy error("Provider %s dlsym(sk_enroll) failed: %s", 1415f47a660Snaddy path, dlerror()); 14277045ccdSdjm goto fail; 14377045ccdSdjm } 14477045ccdSdjm if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) { 1455f47a660Snaddy error("Provider \"%s\" dlsym(sk_sign) failed: %s", 14677045ccdSdjm path, dlerror()); 14777045ccdSdjm goto fail; 14877045ccdSdjm } 1491ac4a90aSdjm if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle, 1501ac4a90aSdjm "sk_load_resident_keys")) == NULL) { 1515f47a660Snaddy error("Provider \"%s\" dlsym(sk_load_resident_keys) " 1525f47a660Snaddy "failed: %s", path, dlerror()); 1531ac4a90aSdjm goto fail; 1541ac4a90aSdjm } 15577045ccdSdjm /* success */ 15677045ccdSdjm return ret; 15777045ccdSdjm fail: 15877045ccdSdjm sshsk_free(ret); 15977045ccdSdjm return NULL; 16077045ccdSdjm } 16177045ccdSdjm 16277045ccdSdjm static void 16377045ccdSdjm sshsk_free_enroll_response(struct sk_enroll_response *r) 16477045ccdSdjm { 16577045ccdSdjm if (r == NULL) 16677045ccdSdjm return; 16777045ccdSdjm freezero(r->key_handle, r->key_handle_len); 16877045ccdSdjm freezero(r->public_key, r->public_key_len); 16977045ccdSdjm freezero(r->signature, r->signature_len); 17077045ccdSdjm freezero(r->attestation_cert, r->attestation_cert_len); 171ee0a8761Sdjm freezero(r->authdata, r->authdata_len); 17277045ccdSdjm freezero(r, sizeof(*r)); 17333e48bcdSdjm } 17477045ccdSdjm 17577045ccdSdjm static void 17677045ccdSdjm sshsk_free_sign_response(struct sk_sign_response *r) 17777045ccdSdjm { 17877045ccdSdjm if (r == NULL) 17977045ccdSdjm return; 18077045ccdSdjm freezero(r->sig_r, r->sig_r_len); 18177045ccdSdjm freezero(r->sig_s, r->sig_s_len); 18277045ccdSdjm freezero(r, sizeof(*r)); 18333e48bcdSdjm } 18477045ccdSdjm 185f8cd6cb1Snaddy #ifdef WITH_OPENSSL 186e1f7e2e4Smarkus /* Assemble key from response */ 187e1f7e2e4Smarkus static int 188e1f7e2e4Smarkus sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 189e1f7e2e4Smarkus { 190e1f7e2e4Smarkus struct sshkey *key = NULL; 191e1f7e2e4Smarkus struct sshbuf *b = NULL; 192*5411e769Sdjm EC_KEY *ecdsa = NULL; 193e1f7e2e4Smarkus EC_POINT *q = NULL; 194*5411e769Sdjm const EC_GROUP *g = NULL; 195e1f7e2e4Smarkus int r; 196e1f7e2e4Smarkus 197e1f7e2e4Smarkus *keyp = NULL; 198e1f7e2e4Smarkus if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) { 19948e6b99dSdjm error_f("sshkey_new failed"); 200e1f7e2e4Smarkus r = SSH_ERR_ALLOC_FAIL; 201e1f7e2e4Smarkus goto out; 202e1f7e2e4Smarkus } 203e1f7e2e4Smarkus key->ecdsa_nid = NID_X9_62_prime256v1; 204*5411e769Sdjm if ((ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL || 205*5411e769Sdjm (g = EC_KEY_get0_group(ecdsa)) == NULL || 206*5411e769Sdjm (q = EC_POINT_new(g)) == NULL || 207e1f7e2e4Smarkus (b = sshbuf_new()) == NULL) { 20848e6b99dSdjm error_f("allocation failed"); 209e1f7e2e4Smarkus r = SSH_ERR_ALLOC_FAIL; 210e1f7e2e4Smarkus goto out; 211e1f7e2e4Smarkus } 212e1f7e2e4Smarkus if ((r = sshbuf_put_string(b, 213e1f7e2e4Smarkus resp->public_key, resp->public_key_len)) != 0) { 21448e6b99dSdjm error_fr(r, "sshbuf_put_string"); 215e1f7e2e4Smarkus goto out; 216e1f7e2e4Smarkus } 217*5411e769Sdjm if ((r = sshbuf_get_ec(b, q, g)) != 0) { 21848e6b99dSdjm error_fr(r, "parse"); 219e1f7e2e4Smarkus r = SSH_ERR_INVALID_FORMAT; 220e1f7e2e4Smarkus goto out; 221e1f7e2e4Smarkus } 222*5411e769Sdjm if (sshkey_ec_validate_public(g, q) != 0) { 2235f47a660Snaddy error("Authenticator returned invalid ECDSA key"); 224e1f7e2e4Smarkus r = SSH_ERR_KEY_INVALID_EC_VALUE; 225e1f7e2e4Smarkus goto out; 226e1f7e2e4Smarkus } 227*5411e769Sdjm if (EC_KEY_set_public_key(ecdsa, q) != 1) { 228e1f7e2e4Smarkus /* XXX assume it is a allocation error */ 22948e6b99dSdjm error_f("allocation failed"); 230e1f7e2e4Smarkus r = SSH_ERR_ALLOC_FAIL; 231e1f7e2e4Smarkus goto out; 232e1f7e2e4Smarkus } 233*5411e769Sdjm if ((key->pkey = EVP_PKEY_new()) == NULL) { 234*5411e769Sdjm error_f("allocation failed"); 235*5411e769Sdjm r = SSH_ERR_ALLOC_FAIL; 236*5411e769Sdjm goto out; 237*5411e769Sdjm } 238*5411e769Sdjm if (EVP_PKEY_set1_EC_KEY(key->pkey, ecdsa) != 1) { 239*5411e769Sdjm error_f("Assigning EC_KEY failed"); 240*5411e769Sdjm r = SSH_ERR_LIBCRYPTO_ERROR; 241*5411e769Sdjm goto out; 242*5411e769Sdjm } 243e1f7e2e4Smarkus /* success */ 244e1f7e2e4Smarkus *keyp = key; 245e1f7e2e4Smarkus key = NULL; /* transferred */ 246e1f7e2e4Smarkus r = 0; 247e1f7e2e4Smarkus out: 248e1f7e2e4Smarkus sshkey_free(key); 249e1f7e2e4Smarkus sshbuf_free(b); 250*5411e769Sdjm EC_KEY_free(ecdsa); 251*5411e769Sdjm EC_POINT_free(q); 252e1f7e2e4Smarkus return r; 253e1f7e2e4Smarkus } 254f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 255e1f7e2e4Smarkus 256af4c80eeSmarkus static int 257af4c80eeSmarkus sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 258af4c80eeSmarkus { 259af4c80eeSmarkus struct sshkey *key = NULL; 260af4c80eeSmarkus int r; 261af4c80eeSmarkus 262af4c80eeSmarkus *keyp = NULL; 263af4c80eeSmarkus if (resp->public_key_len != ED25519_PK_SZ) { 26448e6b99dSdjm error_f("invalid size: %zu", resp->public_key_len); 265af4c80eeSmarkus r = SSH_ERR_INVALID_FORMAT; 266af4c80eeSmarkus goto out; 267af4c80eeSmarkus } 268af4c80eeSmarkus if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) { 26948e6b99dSdjm error_f("sshkey_new failed"); 270af4c80eeSmarkus r = SSH_ERR_ALLOC_FAIL; 271af4c80eeSmarkus goto out; 272af4c80eeSmarkus } 273af4c80eeSmarkus if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { 27448e6b99dSdjm error_f("malloc failed"); 275af4c80eeSmarkus r = SSH_ERR_ALLOC_FAIL; 276af4c80eeSmarkus goto out; 277af4c80eeSmarkus } 278af4c80eeSmarkus memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ); 279af4c80eeSmarkus /* success */ 280af4c80eeSmarkus *keyp = key; 281af4c80eeSmarkus key = NULL; /* transferred */ 282af4c80eeSmarkus r = 0; 283af4c80eeSmarkus out: 284af4c80eeSmarkus sshkey_free(key); 285af4c80eeSmarkus return r; 286af4c80eeSmarkus } 287af4c80eeSmarkus 288cb85020cSdjm static int 289cb85020cSdjm sshsk_key_from_response(int alg, const char *application, uint8_t flags, 290cb85020cSdjm struct sk_enroll_response *resp, struct sshkey **keyp) 291cb85020cSdjm { 292cb85020cSdjm struct sshkey *key = NULL; 293cb85020cSdjm int r = SSH_ERR_INTERNAL_ERROR; 294cb85020cSdjm 295cb85020cSdjm *keyp = NULL; 296cb85020cSdjm 297cb85020cSdjm /* Check response validity */ 2981ac4a90aSdjm if (resp->public_key == NULL || resp->key_handle == NULL) { 29948e6b99dSdjm error_f("sk_enroll response invalid"); 300cb85020cSdjm r = SSH_ERR_INVALID_FORMAT; 301cb85020cSdjm goto out; 302cb85020cSdjm } 303cb85020cSdjm switch (alg) { 304cb85020cSdjm #ifdef WITH_OPENSSL 305cb85020cSdjm case SSH_SK_ECDSA: 306cb85020cSdjm if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0) 307cb85020cSdjm goto out; 308cb85020cSdjm break; 309cb85020cSdjm #endif /* WITH_OPENSSL */ 310cb85020cSdjm case SSH_SK_ED25519: 311cb85020cSdjm if ((r = sshsk_ed25519_assemble(resp, &key)) != 0) 312cb85020cSdjm goto out; 313cb85020cSdjm break; 314cb85020cSdjm default: 31548e6b99dSdjm error_f("unsupported algorithm %d", alg); 316cb85020cSdjm r = SSH_ERR_INVALID_ARGUMENT; 317cb85020cSdjm goto out; 318cb85020cSdjm } 319cb85020cSdjm key->sk_flags = flags; 320cb85020cSdjm if ((key->sk_key_handle = sshbuf_new()) == NULL || 321cb85020cSdjm (key->sk_reserved = sshbuf_new()) == NULL) { 32248e6b99dSdjm error_f("allocation failed"); 323cb85020cSdjm r = SSH_ERR_ALLOC_FAIL; 324cb85020cSdjm goto out; 325cb85020cSdjm } 326cb85020cSdjm if ((key->sk_application = strdup(application)) == NULL) { 32748e6b99dSdjm error_f("strdup application failed"); 328cb85020cSdjm r = SSH_ERR_ALLOC_FAIL; 329cb85020cSdjm goto out; 330cb85020cSdjm } 331cb85020cSdjm if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle, 332cb85020cSdjm resp->key_handle_len)) != 0) { 33348e6b99dSdjm error_fr(r, "put key handle"); 334cb85020cSdjm goto out; 335cb85020cSdjm } 336cb85020cSdjm /* success */ 337cb85020cSdjm r = 0; 338cb85020cSdjm *keyp = key; 339cb85020cSdjm key = NULL; 340cb85020cSdjm out: 341cb85020cSdjm sshkey_free(key); 342cb85020cSdjm return r; 343cb85020cSdjm } 344cb85020cSdjm 345480af03fSdjm static int 346480af03fSdjm skerr_to_ssherr(int skerr) 347480af03fSdjm { 348480af03fSdjm switch (skerr) { 349480af03fSdjm case SSH_SK_ERR_UNSUPPORTED: 350480af03fSdjm return SSH_ERR_FEATURE_UNSUPPORTED; 351480af03fSdjm case SSH_SK_ERR_PIN_REQUIRED: 352480af03fSdjm return SSH_ERR_KEY_WRONG_PASSPHRASE; 353b0297854Sdjm case SSH_SK_ERR_DEVICE_NOT_FOUND: 354b0297854Sdjm return SSH_ERR_DEVICE_NOT_FOUND; 3554fe47877Sdjm case SSH_SK_ERR_CREDENTIAL_EXISTS: 3564fe47877Sdjm return SSH_ERR_KEY_BAD_PERMISSIONS; 357480af03fSdjm case SSH_SK_ERR_GENERAL: 358480af03fSdjm default: 359480af03fSdjm return SSH_ERR_INVALID_FORMAT; 360480af03fSdjm } 361480af03fSdjm } 362480af03fSdjm 363a0caf565Sdjm static void 364a0caf565Sdjm sshsk_free_options(struct sk_option **opts) 365a0caf565Sdjm { 366a0caf565Sdjm size_t i; 367a0caf565Sdjm 368a0caf565Sdjm if (opts == NULL) 369a0caf565Sdjm return; 370a0caf565Sdjm for (i = 0; opts[i] != NULL; i++) { 371a0caf565Sdjm free(opts[i]->name); 372a0caf565Sdjm free(opts[i]->value); 373a0caf565Sdjm free(opts[i]); 374a0caf565Sdjm } 375a0caf565Sdjm free(opts); 376a0caf565Sdjm } 377a0caf565Sdjm 378a0caf565Sdjm static int 379a0caf565Sdjm sshsk_add_option(struct sk_option ***optsp, size_t *noptsp, 380a0caf565Sdjm const char *name, const char *value, uint8_t required) 381a0caf565Sdjm { 382a0caf565Sdjm struct sk_option **opts = *optsp; 383a0caf565Sdjm size_t nopts = *noptsp; 384a0caf565Sdjm 385a0caf565Sdjm if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ 386a0caf565Sdjm sizeof(*opts))) == NULL) { 38748e6b99dSdjm error_f("array alloc failed"); 388a0caf565Sdjm return SSH_ERR_ALLOC_FAIL; 389a0caf565Sdjm } 390a0caf565Sdjm *optsp = opts; 391a0caf565Sdjm *noptsp = nopts + 1; 392a0caf565Sdjm if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { 39348e6b99dSdjm error_f("alloc failed"); 394a0caf565Sdjm return SSH_ERR_ALLOC_FAIL; 395a0caf565Sdjm } 396a0caf565Sdjm if ((opts[nopts]->name = strdup(name)) == NULL || 397a0caf565Sdjm (opts[nopts]->value = strdup(value)) == NULL) { 39848e6b99dSdjm error_f("alloc failed"); 399a0caf565Sdjm return SSH_ERR_ALLOC_FAIL; 400a0caf565Sdjm } 401a0caf565Sdjm opts[nopts]->required = required; 402a0caf565Sdjm return 0; 403a0caf565Sdjm } 404a0caf565Sdjm 405a0caf565Sdjm static int 406a0caf565Sdjm make_options(const char *device, const char *user_id, 407a0caf565Sdjm struct sk_option ***optsp) 408a0caf565Sdjm { 409a0caf565Sdjm struct sk_option **opts = NULL; 410a0caf565Sdjm size_t nopts = 0; 411a0caf565Sdjm int r, ret = SSH_ERR_INTERNAL_ERROR; 412a0caf565Sdjm 413a0caf565Sdjm if (device != NULL && 414a0caf565Sdjm (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) { 415a0caf565Sdjm ret = r; 416a0caf565Sdjm goto out; 417a0caf565Sdjm } 418a0caf565Sdjm if (user_id != NULL && 419a0caf565Sdjm (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) { 420a0caf565Sdjm ret = r; 421a0caf565Sdjm goto out; 422a0caf565Sdjm } 423a0caf565Sdjm /* success */ 424a0caf565Sdjm *optsp = opts; 425a0caf565Sdjm opts = NULL; 426a0caf565Sdjm nopts = 0; 427a0caf565Sdjm ret = 0; 428a0caf565Sdjm out: 429a0caf565Sdjm sshsk_free_options(opts); 430a0caf565Sdjm return ret; 431a0caf565Sdjm } 432a0caf565Sdjm 433ee0a8761Sdjm 434ee0a8761Sdjm static int 435ee0a8761Sdjm fill_attestation_blob(const struct sk_enroll_response *resp, 436ee0a8761Sdjm struct sshbuf *attest) 437ee0a8761Sdjm { 438ee0a8761Sdjm int r; 439ee0a8761Sdjm 440ee0a8761Sdjm if (attest == NULL) 441ee0a8761Sdjm return 0; /* nothing to do */ 442ee0a8761Sdjm if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 || 443ee0a8761Sdjm (r = sshbuf_put_string(attest, 444ee0a8761Sdjm resp->attestation_cert, resp->attestation_cert_len)) != 0 || 445ee0a8761Sdjm (r = sshbuf_put_string(attest, 446ee0a8761Sdjm resp->signature, resp->signature_len)) != 0 || 447ee0a8761Sdjm (r = sshbuf_put_string(attest, 448ee0a8761Sdjm resp->authdata, resp->authdata_len)) != 0 || 449ee0a8761Sdjm (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ 450ee0a8761Sdjm (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { 45148e6b99dSdjm error_fr(r, "compose"); 452ee0a8761Sdjm return r; 453ee0a8761Sdjm } 454ee0a8761Sdjm /* success */ 455ee0a8761Sdjm return 0; 456ee0a8761Sdjm } 457ee0a8761Sdjm 45877045ccdSdjm int 459a0caf565Sdjm sshsk_enroll(int type, const char *provider_path, const char *device, 460a0caf565Sdjm const char *application, const char *userid, uint8_t flags, 461a0caf565Sdjm const char *pin, struct sshbuf *challenge_buf, 4622db06755Sdjm struct sshkey **keyp, struct sshbuf *attest) 46377045ccdSdjm { 46477045ccdSdjm struct sshsk_provider *skp = NULL; 46577045ccdSdjm struct sshkey *key = NULL; 46677045ccdSdjm u_char randchall[32]; 46777045ccdSdjm const u_char *challenge; 46877045ccdSdjm size_t challenge_len; 46977045ccdSdjm struct sk_enroll_response *resp = NULL; 470a0caf565Sdjm struct sk_option **opts = NULL; 47177045ccdSdjm int r = SSH_ERR_INTERNAL_ERROR; 472a3dd6837Smarkus int alg; 47377045ccdSdjm 47448e6b99dSdjm debug_f("provider \"%s\", device \"%s\", application \"%s\", " 47548e6b99dSdjm "userid \"%s\", flags 0x%02x, challenge len %zu%s", 476a0caf565Sdjm provider_path, device, application, userid, flags, 477a0caf565Sdjm challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), 4782db06755Sdjm (pin != NULL && *pin != '\0') ? " with-pin" : ""); 479c6495727Sdjm 48077045ccdSdjm *keyp = NULL; 48177045ccdSdjm if (attest) 48277045ccdSdjm sshbuf_reset(attest); 483a0caf565Sdjm 484a0caf565Sdjm if ((r = make_options(device, userid, &opts)) != 0) 485a0caf565Sdjm goto out; 486a0caf565Sdjm 487af4c80eeSmarkus switch (type) { 488f8cd6cb1Snaddy #ifdef WITH_OPENSSL 489af4c80eeSmarkus case KEY_ECDSA_SK: 490a3dd6837Smarkus alg = SSH_SK_ECDSA; 491a3dd6837Smarkus break; 492f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 493af4c80eeSmarkus case KEY_ED25519_SK: 494a3dd6837Smarkus alg = SSH_SK_ED25519; 495af4c80eeSmarkus break; 496af4c80eeSmarkus default: 49748e6b99dSdjm error_f("unsupported key type"); 498af4c80eeSmarkus r = SSH_ERR_INVALID_ARGUMENT; 499af4c80eeSmarkus goto out; 500af4c80eeSmarkus } 50177045ccdSdjm if (provider_path == NULL) { 50248e6b99dSdjm error_f("missing provider"); 50377045ccdSdjm r = SSH_ERR_INVALID_ARGUMENT; 50477045ccdSdjm goto out; 50577045ccdSdjm } 50677045ccdSdjm if (application == NULL || *application == '\0') { 50748e6b99dSdjm error_f("missing application"); 50877045ccdSdjm r = SSH_ERR_INVALID_ARGUMENT; 50977045ccdSdjm goto out; 51077045ccdSdjm } 51177045ccdSdjm if (challenge_buf == NULL) { 51248e6b99dSdjm debug_f("using random challenge"); 51377045ccdSdjm arc4random_buf(randchall, sizeof(randchall)); 51477045ccdSdjm challenge = randchall; 51577045ccdSdjm challenge_len = sizeof(randchall); 51677045ccdSdjm } else if (sshbuf_len(challenge_buf) == 0) { 51777045ccdSdjm error("Missing enrollment challenge"); 51877045ccdSdjm r = SSH_ERR_INVALID_ARGUMENT; 51977045ccdSdjm goto out; 52077045ccdSdjm } else { 52177045ccdSdjm challenge = sshbuf_ptr(challenge_buf); 52277045ccdSdjm challenge_len = sshbuf_len(challenge_buf); 52348e6b99dSdjm debug3_f("using explicit challenge len=%zd", challenge_len); 52477045ccdSdjm } 52577045ccdSdjm if ((skp = sshsk_open(provider_path)) == NULL) { 52677045ccdSdjm r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 52777045ccdSdjm goto out; 52877045ccdSdjm } 52977045ccdSdjm /* XXX validate flags? */ 53077045ccdSdjm /* enroll key */ 531a3dd6837Smarkus if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, 532a0caf565Sdjm flags, pin, opts, &resp)) != 0) { 53348e6b99dSdjm debug_f("provider \"%s\" failure %d", provider_path, r); 534480af03fSdjm r = skerr_to_ssherr(r); 53577045ccdSdjm goto out; 53677045ccdSdjm } 537cb85020cSdjm 53818b3d906Sdjm if ((r = sshsk_key_from_response(alg, application, resp->flags, 539cb85020cSdjm resp, &key)) != 0) 54077045ccdSdjm goto out; 541cb85020cSdjm 54277045ccdSdjm /* Optionally fill in the attestation information */ 543ee0a8761Sdjm if ((r = fill_attestation_blob(resp, attest)) != 0) 54477045ccdSdjm goto out; 545ee0a8761Sdjm 54677045ccdSdjm /* success */ 54777045ccdSdjm *keyp = key; 54877045ccdSdjm key = NULL; /* transferred */ 54977045ccdSdjm r = 0; 55077045ccdSdjm out: 551a0caf565Sdjm sshsk_free_options(opts); 55277045ccdSdjm sshsk_free(skp); 55377045ccdSdjm sshkey_free(key); 55477045ccdSdjm sshsk_free_enroll_response(resp); 55577045ccdSdjm explicit_bzero(randchall, sizeof(randchall)); 55677045ccdSdjm return r; 55777045ccdSdjm } 55877045ccdSdjm 559f8cd6cb1Snaddy #ifdef WITH_OPENSSL 560f4a621d5Smarkus static int 56137ada6ffSmarkus sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig) 562f4a621d5Smarkus { 563f4a621d5Smarkus struct sshbuf *inner_sig = NULL; 564f4a621d5Smarkus int r = SSH_ERR_INTERNAL_ERROR; 565f4a621d5Smarkus 56688d9b866Smarkus /* Check response validity */ 567a777987dSmarkus if (resp->sig_r == NULL || resp->sig_s == NULL) { 56848e6b99dSdjm error_f("sk_sign response invalid"); 56988d9b866Smarkus r = SSH_ERR_INVALID_FORMAT; 57088d9b866Smarkus goto out; 57188d9b866Smarkus } 572f4a621d5Smarkus if ((inner_sig = sshbuf_new()) == NULL) { 573f4a621d5Smarkus r = SSH_ERR_ALLOC_FAIL; 574f4a621d5Smarkus goto out; 575f4a621d5Smarkus } 57637ada6ffSmarkus /* Prepare and append inner signature object */ 577f4a621d5Smarkus if ((r = sshbuf_put_bignum2_bytes(inner_sig, 578f4a621d5Smarkus resp->sig_r, resp->sig_r_len)) != 0 || 579f4a621d5Smarkus (r = sshbuf_put_bignum2_bytes(inner_sig, 580039d6aaeSdjm resp->sig_s, resp->sig_s_len)) != 0) { 58148e6b99dSdjm error_fr(r, "compose inner"); 582f4a621d5Smarkus goto out; 583f4a621d5Smarkus } 584039d6aaeSdjm if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 || 585039d6aaeSdjm (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 586039d6aaeSdjm (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 58748e6b99dSdjm error_fr(r, "compose"); 58837ada6ffSmarkus goto out; 58937ada6ffSmarkus } 590f4a621d5Smarkus #ifdef DEBUG_SK 591f4a621d5Smarkus fprintf(stderr, "%s: sig_r:\n", __func__); 592f4a621d5Smarkus sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 593f4a621d5Smarkus fprintf(stderr, "%s: sig_s:\n", __func__); 594f4a621d5Smarkus sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr); 59537ada6ffSmarkus fprintf(stderr, "%s: inner:\n", __func__); 59637ada6ffSmarkus sshbuf_dump(inner_sig, stderr); 597dfd98029Smarkus #endif 598dfd98029Smarkus r = 0; 599dfd98029Smarkus out: 600dfd98029Smarkus sshbuf_free(inner_sig); 601dfd98029Smarkus return r; 602dfd98029Smarkus } 603f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 604dfd98029Smarkus 605dfd98029Smarkus static int 60637ada6ffSmarkus sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig) 607dfd98029Smarkus { 608dfd98029Smarkus int r = SSH_ERR_INTERNAL_ERROR; 609dfd98029Smarkus 61088d9b866Smarkus /* Check response validity */ 61188d9b866Smarkus if (resp->sig_r == NULL) { 61248e6b99dSdjm error_f("sk_sign response invalid"); 61388d9b866Smarkus r = SSH_ERR_INVALID_FORMAT; 61488d9b866Smarkus goto out; 61588d9b866Smarkus } 61637ada6ffSmarkus if ((r = sshbuf_put_string(sig, 617dfd98029Smarkus resp->sig_r, resp->sig_r_len)) != 0 || 61837ada6ffSmarkus (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 61937ada6ffSmarkus (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 62048e6b99dSdjm error_fr(r, "compose"); 621dfd98029Smarkus goto out; 622dfd98029Smarkus } 623dfd98029Smarkus #ifdef DEBUG_SK 624dfd98029Smarkus fprintf(stderr, "%s: sig_r:\n", __func__); 625dfd98029Smarkus sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 626f4a621d5Smarkus #endif 627f4a621d5Smarkus r = 0; 628f4a621d5Smarkus out: 62923d69673Smarkus return r; 630f4a621d5Smarkus } 631f4a621d5Smarkus 63277045ccdSdjm int 6334852100aSdjm sshsk_sign(const char *provider_path, struct sshkey *key, 63477045ccdSdjm u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, 6352db06755Sdjm u_int compat, const char *pin) 63677045ccdSdjm { 63777045ccdSdjm struct sshsk_provider *skp = NULL; 63877045ccdSdjm int r = SSH_ERR_INTERNAL_ERROR; 639a3dd6837Smarkus int type, alg; 64077045ccdSdjm struct sk_sign_response *resp = NULL; 64177045ccdSdjm struct sshbuf *inner_sig = NULL, *sig = NULL; 642a0caf565Sdjm struct sk_option **opts = NULL; 64377045ccdSdjm 64448e6b99dSdjm debug_f("provider \"%s\", key %s, flags 0x%02x%s", 6452db06755Sdjm provider_path, sshkey_type(key), key->sk_flags, 6462db06755Sdjm (pin != NULL && *pin != '\0') ? " with-pin" : ""); 647c6495727Sdjm 64877045ccdSdjm if (sigp != NULL) 64977045ccdSdjm *sigp = NULL; 65077045ccdSdjm if (lenp != NULL) 65177045ccdSdjm *lenp = 0; 652dfd98029Smarkus type = sshkey_type_plain(key->type); 653dfd98029Smarkus switch (type) { 654f8cd6cb1Snaddy #ifdef WITH_OPENSSL 655dfd98029Smarkus case KEY_ECDSA_SK: 656a3dd6837Smarkus alg = SSH_SK_ECDSA; 657a3dd6837Smarkus break; 658f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 659dfd98029Smarkus case KEY_ED25519_SK: 660a3dd6837Smarkus alg = SSH_SK_ED25519; 661dfd98029Smarkus break; 662dfd98029Smarkus default: 663dfd98029Smarkus return SSH_ERR_INVALID_ARGUMENT; 664dfd98029Smarkus } 66577045ccdSdjm if (provider_path == NULL || 66677045ccdSdjm key->sk_key_handle == NULL || 66777045ccdSdjm key->sk_application == NULL || *key->sk_application == '\0') { 66877045ccdSdjm r = SSH_ERR_INVALID_ARGUMENT; 66977045ccdSdjm goto out; 67077045ccdSdjm } 67177045ccdSdjm if ((skp = sshsk_open(provider_path)) == NULL) { 67277045ccdSdjm r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 67377045ccdSdjm goto out; 67477045ccdSdjm } 6750b5f6ed5Sdjm #ifdef DEBUG_SK 6760b5f6ed5Sdjm fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n", 6770b5f6ed5Sdjm __func__, key->sk_flags, key->sk_application); 6780b5f6ed5Sdjm fprintf(stderr, "%s: sk_key_handle:\n", __func__); 6790b5f6ed5Sdjm sshbuf_dump(key->sk_key_handle, stderr); 6800b5f6ed5Sdjm #endif 6813ce2af41Sdjm if ((r = skp->sk_sign(alg, data, datalen, key->sk_application, 68277045ccdSdjm sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), 683a0caf565Sdjm key->sk_flags, pin, opts, &resp)) != 0) { 68448e6b99dSdjm debug_f("sk_sign failed with code %d", r); 685480af03fSdjm r = skerr_to_ssherr(r); 68677045ccdSdjm goto out; 68777045ccdSdjm } 68837ada6ffSmarkus /* Assemble signature */ 689f4a621d5Smarkus if ((sig = sshbuf_new()) == NULL) { 69077045ccdSdjm r = SSH_ERR_ALLOC_FAIL; 69177045ccdSdjm goto out; 69277045ccdSdjm } 69337ada6ffSmarkus if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) { 69448e6b99dSdjm error_fr(r, "compose outer"); 69577045ccdSdjm goto out; 69677045ccdSdjm } 69737ada6ffSmarkus switch (type) { 698f8cd6cb1Snaddy #ifdef WITH_OPENSSL 69937ada6ffSmarkus case KEY_ECDSA_SK: 70037ada6ffSmarkus if ((r = sshsk_ecdsa_sig(resp, sig)) != 0) 70137ada6ffSmarkus goto out; 70237ada6ffSmarkus break; 703f8cd6cb1Snaddy #endif /* WITH_OPENSSL */ 70437ada6ffSmarkus case KEY_ED25519_SK: 70537ada6ffSmarkus if ((r = sshsk_ed25519_sig(resp, sig)) != 0) 70637ada6ffSmarkus goto out; 70737ada6ffSmarkus break; 70837ada6ffSmarkus } 70977045ccdSdjm #ifdef DEBUG_SK 710dfd98029Smarkus fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 711dfd98029Smarkus __func__, resp->flags, resp->counter); 7126239c001Sdjm fprintf(stderr, "%s: data to sign:\n", __func__); 7136239c001Sdjm sshbuf_dump_data(data, datalen, stderr); 71477045ccdSdjm fprintf(stderr, "%s: sigbuf:\n", __func__); 71577045ccdSdjm sshbuf_dump(sig, stderr); 71677045ccdSdjm #endif 71777045ccdSdjm if (sigp != NULL) { 71877045ccdSdjm if ((*sigp = malloc(sshbuf_len(sig))) == NULL) { 71977045ccdSdjm r = SSH_ERR_ALLOC_FAIL; 72077045ccdSdjm goto out; 72177045ccdSdjm } 72277045ccdSdjm memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig)); 72377045ccdSdjm } 72477045ccdSdjm if (lenp != NULL) 72577045ccdSdjm *lenp = sshbuf_len(sig); 72677045ccdSdjm /* success */ 72777045ccdSdjm r = 0; 72877045ccdSdjm out: 729a0caf565Sdjm sshsk_free_options(opts); 73077045ccdSdjm sshsk_free(skp); 73177045ccdSdjm sshsk_free_sign_response(resp); 73277045ccdSdjm sshbuf_free(sig); 73377045ccdSdjm sshbuf_free(inner_sig); 73477045ccdSdjm return r; 73577045ccdSdjm } 7361ac4a90aSdjm 7371ac4a90aSdjm static void 7381ac4a90aSdjm sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) 7391ac4a90aSdjm { 7401ac4a90aSdjm size_t i; 7411ac4a90aSdjm 7421ac4a90aSdjm if (nrks == 0 || rks == NULL) 7431ac4a90aSdjm return; 7441ac4a90aSdjm for (i = 0; i < nrks; i++) { 7451ac4a90aSdjm free(rks[i]->application); 7460081f855Sdjm freezero(rks[i]->user_id, rks[i]->user_id_len); 7471ac4a90aSdjm freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); 7481ac4a90aSdjm freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); 7491ac4a90aSdjm freezero(rks[i]->key.signature, rks[i]->key.signature_len); 7501ac4a90aSdjm freezero(rks[i]->key.attestation_cert, 7511ac4a90aSdjm rks[i]->key.attestation_cert_len); 7521ac4a90aSdjm freezero(rks[i], sizeof(**rks)); 7531ac4a90aSdjm } 7541ac4a90aSdjm free(rks); 7551ac4a90aSdjm } 7561ac4a90aSdjm 757991d5a20Sdjm static void 758991d5a20Sdjm sshsk_free_resident_key(struct sshsk_resident_key *srk) 759991d5a20Sdjm { 760991d5a20Sdjm if (srk == NULL) 761991d5a20Sdjm return; 762991d5a20Sdjm sshkey_free(srk->key); 763991d5a20Sdjm freezero(srk->user_id, srk->user_id_len); 764991d5a20Sdjm free(srk); 765991d5a20Sdjm } 766991d5a20Sdjm 767991d5a20Sdjm 768991d5a20Sdjm void 769991d5a20Sdjm sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks) 770991d5a20Sdjm { 771991d5a20Sdjm size_t i; 772991d5a20Sdjm 773991d5a20Sdjm if (srks == NULL || nsrks == 0) 774991d5a20Sdjm return; 775991d5a20Sdjm 776991d5a20Sdjm for (i = 0; i < nsrks; i++) 777991d5a20Sdjm sshsk_free_resident_key(srks[i]); 778991d5a20Sdjm free(srks); 779991d5a20Sdjm } 780991d5a20Sdjm 7811ac4a90aSdjm int 782a0caf565Sdjm sshsk_load_resident(const char *provider_path, const char *device, 783991d5a20Sdjm const char *pin, u_int flags, struct sshsk_resident_key ***srksp, 784991d5a20Sdjm size_t *nsrksp) 7851ac4a90aSdjm { 7861ac4a90aSdjm struct sshsk_provider *skp = NULL; 7871ac4a90aSdjm int r = SSH_ERR_INTERNAL_ERROR; 7881ac4a90aSdjm struct sk_resident_key **rks = NULL; 789991d5a20Sdjm size_t i, nrks = 0, nsrks = 0; 790991d5a20Sdjm struct sshkey *key = NULL; 791991d5a20Sdjm struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp; 792991d5a20Sdjm uint8_t sk_flags; 793a0caf565Sdjm struct sk_option **opts = NULL; 7941ac4a90aSdjm 79548e6b99dSdjm debug_f("provider \"%s\"%s", provider_path, 7961ac4a90aSdjm (pin != NULL && *pin != '\0') ? ", have-pin": ""); 7971ac4a90aSdjm 798991d5a20Sdjm if (srksp == NULL || nsrksp == NULL) 7991ac4a90aSdjm return SSH_ERR_INVALID_ARGUMENT; 800991d5a20Sdjm *srksp = NULL; 801991d5a20Sdjm *nsrksp = 0; 8021ac4a90aSdjm 803a0caf565Sdjm if ((r = make_options(device, NULL, &opts)) != 0) 804a0caf565Sdjm goto out; 8051ac4a90aSdjm if ((skp = sshsk_open(provider_path)) == NULL) { 8061ac4a90aSdjm r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 8071ac4a90aSdjm goto out; 8081ac4a90aSdjm } 809a0caf565Sdjm if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) { 8105f47a660Snaddy error("Provider \"%s\" returned failure %d", provider_path, r); 811480af03fSdjm r = skerr_to_ssherr(r); 8121ac4a90aSdjm goto out; 8131ac4a90aSdjm } 8141ac4a90aSdjm for (i = 0; i < nrks; i++) { 815991d5a20Sdjm debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu", 816991d5a20Sdjm i, rks[i]->slot, rks[i]->alg, rks[i]->application, 817991d5a20Sdjm rks[i]->user_id_len); 8181ac4a90aSdjm /* XXX need better filter here */ 8191ac4a90aSdjm if (strncmp(rks[i]->application, "ssh:", 4) != 0) 8201ac4a90aSdjm continue; 8211ac4a90aSdjm switch (rks[i]->alg) { 8221ac4a90aSdjm case SSH_SK_ECDSA: 8231ac4a90aSdjm case SSH_SK_ED25519: 8241ac4a90aSdjm break; 8251ac4a90aSdjm default: 8261ac4a90aSdjm continue; 8271ac4a90aSdjm } 828991d5a20Sdjm sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY; 82915a2cdb6Sdjm if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD)) 830991d5a20Sdjm sk_flags |= SSH_SK_USER_VERIFICATION_REQD; 8311ac4a90aSdjm if ((r = sshsk_key_from_response(rks[i]->alg, 832991d5a20Sdjm rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0) 8331ac4a90aSdjm goto out; 834991d5a20Sdjm if ((srk = calloc(1, sizeof(*srk))) == NULL) { 835991d5a20Sdjm error_f("calloc failed"); 836991d5a20Sdjm r = SSH_ERR_ALLOC_FAIL; 837991d5a20Sdjm goto out; 838991d5a20Sdjm } 839991d5a20Sdjm srk->key = key; 840991d5a20Sdjm key = NULL; /* transferred */ 841991d5a20Sdjm if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) { 842991d5a20Sdjm error_f("calloc failed"); 843991d5a20Sdjm r = SSH_ERR_ALLOC_FAIL; 844991d5a20Sdjm goto out; 845991d5a20Sdjm } 846991d5a20Sdjm memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len); 847991d5a20Sdjm srk->user_id_len = rks[i]->user_id_len; 848991d5a20Sdjm if ((tmp = recallocarray(srks, nsrks, nsrks + 1, 8491ac4a90aSdjm sizeof(*tmp))) == NULL) { 85048e6b99dSdjm error_f("recallocarray failed"); 8511ac4a90aSdjm r = SSH_ERR_ALLOC_FAIL; 8521ac4a90aSdjm goto out; 8531ac4a90aSdjm } 854991d5a20Sdjm srks = tmp; 855991d5a20Sdjm srks[nsrks++] = srk; 856991d5a20Sdjm srk = NULL; 8571ac4a90aSdjm /* XXX synthesise comment */ 8581ac4a90aSdjm } 8591ac4a90aSdjm /* success */ 860991d5a20Sdjm *srksp = srks; 861991d5a20Sdjm *nsrksp = nsrks; 862991d5a20Sdjm srks = NULL; 863991d5a20Sdjm nsrks = 0; 8641ac4a90aSdjm r = 0; 8651ac4a90aSdjm out: 866a0caf565Sdjm sshsk_free_options(opts); 8671ac4a90aSdjm sshsk_free(skp); 8681ac4a90aSdjm sshsk_free_sk_resident_keys(rks, nrks); 8691ac4a90aSdjm sshkey_free(key); 870991d5a20Sdjm sshsk_free_resident_key(srk); 871991d5a20Sdjm sshsk_free_resident_keys(srks, nsrks); 8721ac4a90aSdjm return r; 8731ac4a90aSdjm } 8741ac4a90aSdjm 875