1*38a52bd3SEd Maste /* $OpenBSD: ssh-sk.c,v 1.39 2022/07/20 03:29:14 djm Exp $ */ 219261079SEd Maste /* 319261079SEd Maste * Copyright (c) 2019 Google LLC 419261079SEd Maste * 519261079SEd Maste * Permission to use, copy, modify, and distribute this software for any 619261079SEd Maste * purpose with or without fee is hereby granted, provided that the above 719261079SEd Maste * copyright notice and this permission notice appear in all copies. 819261079SEd Maste * 919261079SEd Maste * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1019261079SEd Maste * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1119261079SEd Maste * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1219261079SEd Maste * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1319261079SEd Maste * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1419261079SEd Maste * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1519261079SEd Maste * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1619261079SEd Maste */ 1719261079SEd Maste 1819261079SEd Maste /* #define DEBUG_SK 1 */ 1919261079SEd Maste 2019261079SEd Maste #include "includes.h" 2119261079SEd Maste 2219261079SEd Maste #ifdef ENABLE_SK 2319261079SEd Maste 2419261079SEd Maste #include <dlfcn.h> 2519261079SEd Maste #include <stddef.h> 2619261079SEd Maste #ifdef HAVE_STDINT_H 2719261079SEd Maste # include <stdint.h> 2819261079SEd Maste #endif 2919261079SEd Maste #include <string.h> 3019261079SEd Maste #include <stdio.h> 3119261079SEd Maste 321323ec57SEd Maste #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) 3319261079SEd Maste #include <openssl/objects.h> 3419261079SEd Maste #include <openssl/ec.h> 351323ec57SEd Maste #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ 3619261079SEd Maste 3719261079SEd Maste #include "log.h" 3819261079SEd Maste #include "misc.h" 3919261079SEd Maste #include "sshbuf.h" 4019261079SEd Maste #include "sshkey.h" 4119261079SEd Maste #include "ssherr.h" 4219261079SEd Maste #include "digest.h" 4319261079SEd Maste 4419261079SEd Maste #include "ssh-sk.h" 4519261079SEd Maste #include "sk-api.h" 4619261079SEd Maste #include "crypto_api.h" 4719261079SEd Maste 481323ec57SEd Maste /* 491323ec57SEd Maste * Almost every use of OpenSSL in this file is for ECDSA-NISTP256. 501323ec57SEd Maste * This is strictly a larger hammer than necessary, but it reduces changes 511323ec57SEd Maste * with upstream. 521323ec57SEd Maste */ 531323ec57SEd Maste #ifndef OPENSSL_HAS_ECC 541323ec57SEd Maste # undef WITH_OPENSSL 551323ec57SEd Maste #endif 561323ec57SEd Maste 5719261079SEd Maste struct sshsk_provider { 5819261079SEd Maste char *path; 5919261079SEd Maste void *dlhandle; 6019261079SEd Maste 6119261079SEd Maste /* Return the version of the middleware API */ 6219261079SEd Maste uint32_t (*sk_api_version)(void); 6319261079SEd Maste 6419261079SEd Maste /* Enroll a U2F key (private key generation) */ 6519261079SEd Maste int (*sk_enroll)(int alg, const uint8_t *challenge, 6619261079SEd Maste size_t challenge_len, const char *application, uint8_t flags, 6719261079SEd Maste const char *pin, struct sk_option **opts, 6819261079SEd Maste struct sk_enroll_response **enroll_response); 6919261079SEd Maste 7019261079SEd Maste /* Sign a challenge */ 7119261079SEd Maste int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, 7219261079SEd Maste const char *application, 7319261079SEd Maste const uint8_t *key_handle, size_t key_handle_len, 7419261079SEd Maste uint8_t flags, const char *pin, struct sk_option **opts, 7519261079SEd Maste struct sk_sign_response **sign_response); 7619261079SEd Maste 7719261079SEd Maste /* Enumerate resident keys */ 7819261079SEd Maste int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts, 7919261079SEd Maste struct sk_resident_key ***rks, size_t *nrks); 8019261079SEd Maste }; 8119261079SEd Maste 8219261079SEd Maste /* Built-in version */ 8319261079SEd Maste int ssh_sk_enroll(int alg, const uint8_t *challenge, 8419261079SEd Maste size_t challenge_len, const char *application, uint8_t flags, 8519261079SEd Maste const char *pin, struct sk_option **opts, 8619261079SEd Maste struct sk_enroll_response **enroll_response); 8719261079SEd Maste int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, 8819261079SEd Maste const char *application, 8919261079SEd Maste const uint8_t *key_handle, size_t key_handle_len, 9019261079SEd Maste uint8_t flags, const char *pin, struct sk_option **opts, 9119261079SEd Maste struct sk_sign_response **sign_response); 9219261079SEd Maste int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts, 9319261079SEd Maste struct sk_resident_key ***rks, size_t *nrks); 9419261079SEd Maste 9519261079SEd Maste static void 9619261079SEd Maste sshsk_free(struct sshsk_provider *p) 9719261079SEd Maste { 9819261079SEd Maste if (p == NULL) 9919261079SEd Maste return; 10019261079SEd Maste free(p->path); 10119261079SEd Maste if (p->dlhandle != NULL) 10219261079SEd Maste dlclose(p->dlhandle); 10319261079SEd Maste free(p); 10419261079SEd Maste } 10519261079SEd Maste 10619261079SEd Maste static struct sshsk_provider * 10719261079SEd Maste sshsk_open(const char *path) 10819261079SEd Maste { 10919261079SEd Maste struct sshsk_provider *ret = NULL; 11019261079SEd Maste uint32_t version; 11119261079SEd Maste 11219261079SEd Maste if (path == NULL || *path == '\0') { 11319261079SEd Maste error("No FIDO SecurityKeyProvider specified"); 11419261079SEd Maste return NULL; 11519261079SEd Maste } 11619261079SEd Maste if ((ret = calloc(1, sizeof(*ret))) == NULL) { 11719261079SEd Maste error_f("calloc failed"); 11819261079SEd Maste return NULL; 11919261079SEd Maste } 12019261079SEd Maste if ((ret->path = strdup(path)) == NULL) { 12119261079SEd Maste error_f("strdup failed"); 12219261079SEd Maste goto fail; 12319261079SEd Maste } 12419261079SEd Maste /* Skip the rest if we're using the linked in middleware */ 12519261079SEd Maste if (strcasecmp(ret->path, "internal") == 0) { 12619261079SEd Maste #ifdef ENABLE_SK_INTERNAL 12719261079SEd Maste ret->sk_enroll = ssh_sk_enroll; 12819261079SEd Maste ret->sk_sign = ssh_sk_sign; 12919261079SEd Maste ret->sk_load_resident_keys = ssh_sk_load_resident_keys; 130*38a52bd3SEd Maste return ret; 13119261079SEd Maste #else 13219261079SEd Maste error("internal security key support not enabled"); 133*38a52bd3SEd Maste goto fail; 13419261079SEd Maste #endif 13519261079SEd Maste } 13619261079SEd Maste if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) { 13719261079SEd Maste error("Provider \"%s\" dlopen failed: %s", path, dlerror()); 13819261079SEd Maste goto fail; 13919261079SEd Maste } 14019261079SEd Maste if ((ret->sk_api_version = dlsym(ret->dlhandle, 14119261079SEd Maste "sk_api_version")) == NULL) { 14219261079SEd Maste error("Provider \"%s\" dlsym(sk_api_version) failed: %s", 14319261079SEd Maste path, dlerror()); 14419261079SEd Maste goto fail; 14519261079SEd Maste } 14619261079SEd Maste version = ret->sk_api_version(); 14719261079SEd Maste debug_f("provider %s implements version 0x%08lx", ret->path, 14819261079SEd Maste (u_long)version); 14919261079SEd Maste if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) { 15019261079SEd Maste error("Provider \"%s\" implements unsupported " 15119261079SEd Maste "version 0x%08lx (supported: 0x%08lx)", 15219261079SEd Maste path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR); 15319261079SEd Maste goto fail; 15419261079SEd Maste } 15519261079SEd Maste if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) { 15619261079SEd Maste error("Provider %s dlsym(sk_enroll) failed: %s", 15719261079SEd Maste path, dlerror()); 15819261079SEd Maste goto fail; 15919261079SEd Maste } 16019261079SEd Maste if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) { 16119261079SEd Maste error("Provider \"%s\" dlsym(sk_sign) failed: %s", 16219261079SEd Maste path, dlerror()); 16319261079SEd Maste goto fail; 16419261079SEd Maste } 16519261079SEd Maste if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle, 16619261079SEd Maste "sk_load_resident_keys")) == NULL) { 16719261079SEd Maste error("Provider \"%s\" dlsym(sk_load_resident_keys) " 16819261079SEd Maste "failed: %s", path, dlerror()); 16919261079SEd Maste goto fail; 17019261079SEd Maste } 17119261079SEd Maste /* success */ 17219261079SEd Maste return ret; 17319261079SEd Maste fail: 17419261079SEd Maste sshsk_free(ret); 17519261079SEd Maste return NULL; 17619261079SEd Maste } 17719261079SEd Maste 17819261079SEd Maste static void 17919261079SEd Maste sshsk_free_enroll_response(struct sk_enroll_response *r) 18019261079SEd Maste { 18119261079SEd Maste if (r == NULL) 18219261079SEd Maste return; 18319261079SEd Maste freezero(r->key_handle, r->key_handle_len); 18419261079SEd Maste freezero(r->public_key, r->public_key_len); 18519261079SEd Maste freezero(r->signature, r->signature_len); 18619261079SEd Maste freezero(r->attestation_cert, r->attestation_cert_len); 18719261079SEd Maste freezero(r->authdata, r->authdata_len); 18819261079SEd Maste freezero(r, sizeof(*r)); 18919261079SEd Maste } 19019261079SEd Maste 19119261079SEd Maste static void 19219261079SEd Maste sshsk_free_sign_response(struct sk_sign_response *r) 19319261079SEd Maste { 19419261079SEd Maste if (r == NULL) 19519261079SEd Maste return; 19619261079SEd Maste freezero(r->sig_r, r->sig_r_len); 19719261079SEd Maste freezero(r->sig_s, r->sig_s_len); 19819261079SEd Maste freezero(r, sizeof(*r)); 19919261079SEd Maste } 20019261079SEd Maste 20119261079SEd Maste #ifdef WITH_OPENSSL 20219261079SEd Maste /* Assemble key from response */ 20319261079SEd Maste static int 20419261079SEd Maste sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 20519261079SEd Maste { 20619261079SEd Maste struct sshkey *key = NULL; 20719261079SEd Maste struct sshbuf *b = NULL; 20819261079SEd Maste EC_POINT *q = NULL; 20919261079SEd Maste int r; 21019261079SEd Maste 21119261079SEd Maste *keyp = NULL; 21219261079SEd Maste if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) { 21319261079SEd Maste error_f("sshkey_new failed"); 21419261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 21519261079SEd Maste goto out; 21619261079SEd Maste } 21719261079SEd Maste key->ecdsa_nid = NID_X9_62_prime256v1; 21819261079SEd Maste if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL || 21919261079SEd Maste (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL || 22019261079SEd Maste (b = sshbuf_new()) == NULL) { 22119261079SEd Maste error_f("allocation failed"); 22219261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 22319261079SEd Maste goto out; 22419261079SEd Maste } 22519261079SEd Maste if ((r = sshbuf_put_string(b, 22619261079SEd Maste resp->public_key, resp->public_key_len)) != 0) { 22719261079SEd Maste error_fr(r, "sshbuf_put_string"); 22819261079SEd Maste goto out; 22919261079SEd Maste } 23019261079SEd Maste if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) { 23119261079SEd Maste error_fr(r, "parse"); 23219261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 23319261079SEd Maste goto out; 23419261079SEd Maste } 23519261079SEd Maste if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) { 23619261079SEd Maste error("Authenticator returned invalid ECDSA key"); 23719261079SEd Maste r = SSH_ERR_KEY_INVALID_EC_VALUE; 23819261079SEd Maste goto out; 23919261079SEd Maste } 24019261079SEd Maste if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { 24119261079SEd Maste /* XXX assume it is a allocation error */ 24219261079SEd Maste error_f("allocation failed"); 24319261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 24419261079SEd Maste goto out; 24519261079SEd Maste } 24619261079SEd Maste /* success */ 24719261079SEd Maste *keyp = key; 24819261079SEd Maste key = NULL; /* transferred */ 24919261079SEd Maste r = 0; 25019261079SEd Maste out: 25119261079SEd Maste EC_POINT_free(q); 25219261079SEd Maste sshkey_free(key); 25319261079SEd Maste sshbuf_free(b); 25419261079SEd Maste return r; 25519261079SEd Maste } 25619261079SEd Maste #endif /* WITH_OPENSSL */ 25719261079SEd Maste 25819261079SEd Maste static int 25919261079SEd Maste sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) 26019261079SEd Maste { 26119261079SEd Maste struct sshkey *key = NULL; 26219261079SEd Maste int r; 26319261079SEd Maste 26419261079SEd Maste *keyp = NULL; 26519261079SEd Maste if (resp->public_key_len != ED25519_PK_SZ) { 26619261079SEd Maste error_f("invalid size: %zu", resp->public_key_len); 26719261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 26819261079SEd Maste goto out; 26919261079SEd Maste } 27019261079SEd Maste if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) { 27119261079SEd Maste error_f("sshkey_new failed"); 27219261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 27319261079SEd Maste goto out; 27419261079SEd Maste } 27519261079SEd Maste if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { 27619261079SEd Maste error_f("malloc failed"); 27719261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 27819261079SEd Maste goto out; 27919261079SEd Maste } 28019261079SEd Maste memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ); 28119261079SEd Maste /* success */ 28219261079SEd Maste *keyp = key; 28319261079SEd Maste key = NULL; /* transferred */ 28419261079SEd Maste r = 0; 28519261079SEd Maste out: 28619261079SEd Maste sshkey_free(key); 28719261079SEd Maste return r; 28819261079SEd Maste } 28919261079SEd Maste 29019261079SEd Maste static int 29119261079SEd Maste sshsk_key_from_response(int alg, const char *application, uint8_t flags, 29219261079SEd Maste struct sk_enroll_response *resp, struct sshkey **keyp) 29319261079SEd Maste { 29419261079SEd Maste struct sshkey *key = NULL; 29519261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 29619261079SEd Maste 29719261079SEd Maste *keyp = NULL; 29819261079SEd Maste 29919261079SEd Maste /* Check response validity */ 30019261079SEd Maste if (resp->public_key == NULL || resp->key_handle == NULL) { 30119261079SEd Maste error_f("sk_enroll response invalid"); 30219261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 30319261079SEd Maste goto out; 30419261079SEd Maste } 30519261079SEd Maste switch (alg) { 30619261079SEd Maste #ifdef WITH_OPENSSL 30719261079SEd Maste case SSH_SK_ECDSA: 30819261079SEd Maste if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0) 30919261079SEd Maste goto out; 31019261079SEd Maste break; 31119261079SEd Maste #endif /* WITH_OPENSSL */ 31219261079SEd Maste case SSH_SK_ED25519: 31319261079SEd Maste if ((r = sshsk_ed25519_assemble(resp, &key)) != 0) 31419261079SEd Maste goto out; 31519261079SEd Maste break; 31619261079SEd Maste default: 31719261079SEd Maste error_f("unsupported algorithm %d", alg); 31819261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 31919261079SEd Maste goto out; 32019261079SEd Maste } 32119261079SEd Maste key->sk_flags = flags; 32219261079SEd Maste if ((key->sk_key_handle = sshbuf_new()) == NULL || 32319261079SEd Maste (key->sk_reserved = sshbuf_new()) == NULL) { 32419261079SEd Maste error_f("allocation failed"); 32519261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 32619261079SEd Maste goto out; 32719261079SEd Maste } 32819261079SEd Maste if ((key->sk_application = strdup(application)) == NULL) { 32919261079SEd Maste error_f("strdup application failed"); 33019261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 33119261079SEd Maste goto out; 33219261079SEd Maste } 33319261079SEd Maste if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle, 33419261079SEd Maste resp->key_handle_len)) != 0) { 33519261079SEd Maste error_fr(r, "put key handle"); 33619261079SEd Maste goto out; 33719261079SEd Maste } 33819261079SEd Maste /* success */ 33919261079SEd Maste r = 0; 34019261079SEd Maste *keyp = key; 34119261079SEd Maste key = NULL; 34219261079SEd Maste out: 34319261079SEd Maste sshkey_free(key); 34419261079SEd Maste return r; 34519261079SEd Maste } 34619261079SEd Maste 34719261079SEd Maste static int 34819261079SEd Maste skerr_to_ssherr(int skerr) 34919261079SEd Maste { 35019261079SEd Maste switch (skerr) { 35119261079SEd Maste case SSH_SK_ERR_UNSUPPORTED: 35219261079SEd Maste return SSH_ERR_FEATURE_UNSUPPORTED; 35319261079SEd Maste case SSH_SK_ERR_PIN_REQUIRED: 35419261079SEd Maste return SSH_ERR_KEY_WRONG_PASSPHRASE; 35519261079SEd Maste case SSH_SK_ERR_DEVICE_NOT_FOUND: 35619261079SEd Maste return SSH_ERR_DEVICE_NOT_FOUND; 357*38a52bd3SEd Maste case SSH_SK_ERR_CREDENTIAL_EXISTS: 358*38a52bd3SEd Maste return SSH_ERR_KEY_BAD_PERMISSIONS; 35919261079SEd Maste case SSH_SK_ERR_GENERAL: 36019261079SEd Maste default: 36119261079SEd Maste return SSH_ERR_INVALID_FORMAT; 36219261079SEd Maste } 36319261079SEd Maste } 36419261079SEd Maste 36519261079SEd Maste static void 36619261079SEd Maste sshsk_free_options(struct sk_option **opts) 36719261079SEd Maste { 36819261079SEd Maste size_t i; 36919261079SEd Maste 37019261079SEd Maste if (opts == NULL) 37119261079SEd Maste return; 37219261079SEd Maste for (i = 0; opts[i] != NULL; i++) { 37319261079SEd Maste free(opts[i]->name); 37419261079SEd Maste free(opts[i]->value); 37519261079SEd Maste free(opts[i]); 37619261079SEd Maste } 37719261079SEd Maste free(opts); 37819261079SEd Maste } 37919261079SEd Maste 38019261079SEd Maste static int 38119261079SEd Maste sshsk_add_option(struct sk_option ***optsp, size_t *noptsp, 38219261079SEd Maste const char *name, const char *value, uint8_t required) 38319261079SEd Maste { 38419261079SEd Maste struct sk_option **opts = *optsp; 38519261079SEd Maste size_t nopts = *noptsp; 38619261079SEd Maste 38719261079SEd Maste if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ 38819261079SEd Maste sizeof(*opts))) == NULL) { 38919261079SEd Maste error_f("array alloc failed"); 39019261079SEd Maste return SSH_ERR_ALLOC_FAIL; 39119261079SEd Maste } 39219261079SEd Maste *optsp = opts; 39319261079SEd Maste *noptsp = nopts + 1; 39419261079SEd Maste if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { 39519261079SEd Maste error_f("alloc failed"); 39619261079SEd Maste return SSH_ERR_ALLOC_FAIL; 39719261079SEd Maste } 39819261079SEd Maste if ((opts[nopts]->name = strdup(name)) == NULL || 39919261079SEd Maste (opts[nopts]->value = strdup(value)) == NULL) { 40019261079SEd Maste error_f("alloc failed"); 40119261079SEd Maste return SSH_ERR_ALLOC_FAIL; 40219261079SEd Maste } 40319261079SEd Maste opts[nopts]->required = required; 40419261079SEd Maste return 0; 40519261079SEd Maste } 40619261079SEd Maste 40719261079SEd Maste static int 40819261079SEd Maste make_options(const char *device, const char *user_id, 40919261079SEd Maste struct sk_option ***optsp) 41019261079SEd Maste { 41119261079SEd Maste struct sk_option **opts = NULL; 41219261079SEd Maste size_t nopts = 0; 41319261079SEd Maste int r, ret = SSH_ERR_INTERNAL_ERROR; 41419261079SEd Maste 41519261079SEd Maste if (device != NULL && 41619261079SEd Maste (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) { 41719261079SEd Maste ret = r; 41819261079SEd Maste goto out; 41919261079SEd Maste } 42019261079SEd Maste if (user_id != NULL && 42119261079SEd Maste (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) { 42219261079SEd Maste ret = r; 42319261079SEd Maste goto out; 42419261079SEd Maste } 42519261079SEd Maste /* success */ 42619261079SEd Maste *optsp = opts; 42719261079SEd Maste opts = NULL; 42819261079SEd Maste nopts = 0; 42919261079SEd Maste ret = 0; 43019261079SEd Maste out: 43119261079SEd Maste sshsk_free_options(opts); 43219261079SEd Maste return ret; 43319261079SEd Maste } 43419261079SEd Maste 43519261079SEd Maste 43619261079SEd Maste static int 43719261079SEd Maste fill_attestation_blob(const struct sk_enroll_response *resp, 43819261079SEd Maste struct sshbuf *attest) 43919261079SEd Maste { 44019261079SEd Maste int r; 44119261079SEd Maste 44219261079SEd Maste if (attest == NULL) 44319261079SEd Maste return 0; /* nothing to do */ 44419261079SEd Maste if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 || 44519261079SEd Maste (r = sshbuf_put_string(attest, 44619261079SEd Maste resp->attestation_cert, resp->attestation_cert_len)) != 0 || 44719261079SEd Maste (r = sshbuf_put_string(attest, 44819261079SEd Maste resp->signature, resp->signature_len)) != 0 || 44919261079SEd Maste (r = sshbuf_put_string(attest, 45019261079SEd Maste resp->authdata, resp->authdata_len)) != 0 || 45119261079SEd Maste (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ 45219261079SEd Maste (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { 45319261079SEd Maste error_fr(r, "compose"); 45419261079SEd Maste return r; 45519261079SEd Maste } 45619261079SEd Maste /* success */ 45719261079SEd Maste return 0; 45819261079SEd Maste } 45919261079SEd Maste 46019261079SEd Maste int 46119261079SEd Maste sshsk_enroll(int type, const char *provider_path, const char *device, 46219261079SEd Maste const char *application, const char *userid, uint8_t flags, 46319261079SEd Maste const char *pin, struct sshbuf *challenge_buf, 46419261079SEd Maste struct sshkey **keyp, struct sshbuf *attest) 46519261079SEd Maste { 46619261079SEd Maste struct sshsk_provider *skp = NULL; 46719261079SEd Maste struct sshkey *key = NULL; 46819261079SEd Maste u_char randchall[32]; 46919261079SEd Maste const u_char *challenge; 47019261079SEd Maste size_t challenge_len; 47119261079SEd Maste struct sk_enroll_response *resp = NULL; 47219261079SEd Maste struct sk_option **opts = NULL; 47319261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 47419261079SEd Maste int alg; 47519261079SEd Maste 47619261079SEd Maste debug_f("provider \"%s\", device \"%s\", application \"%s\", " 47719261079SEd Maste "userid \"%s\", flags 0x%02x, challenge len %zu%s", 47819261079SEd Maste provider_path, device, application, userid, flags, 47919261079SEd Maste challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), 48019261079SEd Maste (pin != NULL && *pin != '\0') ? " with-pin" : ""); 48119261079SEd Maste 48219261079SEd Maste *keyp = NULL; 48319261079SEd Maste if (attest) 48419261079SEd Maste sshbuf_reset(attest); 48519261079SEd Maste 48619261079SEd Maste if ((r = make_options(device, userid, &opts)) != 0) 48719261079SEd Maste goto out; 48819261079SEd Maste 48919261079SEd Maste switch (type) { 49019261079SEd Maste #ifdef WITH_OPENSSL 49119261079SEd Maste case KEY_ECDSA_SK: 49219261079SEd Maste alg = SSH_SK_ECDSA; 49319261079SEd Maste break; 49419261079SEd Maste #endif /* WITH_OPENSSL */ 49519261079SEd Maste case KEY_ED25519_SK: 49619261079SEd Maste alg = SSH_SK_ED25519; 49719261079SEd Maste break; 49819261079SEd Maste default: 49919261079SEd Maste error_f("unsupported key type"); 50019261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 50119261079SEd Maste goto out; 50219261079SEd Maste } 50319261079SEd Maste if (provider_path == NULL) { 50419261079SEd Maste error_f("missing provider"); 50519261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 50619261079SEd Maste goto out; 50719261079SEd Maste } 50819261079SEd Maste if (application == NULL || *application == '\0') { 50919261079SEd Maste error_f("missing application"); 51019261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 51119261079SEd Maste goto out; 51219261079SEd Maste } 51319261079SEd Maste if (challenge_buf == NULL) { 51419261079SEd Maste debug_f("using random challenge"); 51519261079SEd Maste arc4random_buf(randchall, sizeof(randchall)); 51619261079SEd Maste challenge = randchall; 51719261079SEd Maste challenge_len = sizeof(randchall); 51819261079SEd Maste } else if (sshbuf_len(challenge_buf) == 0) { 51919261079SEd Maste error("Missing enrollment challenge"); 52019261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 52119261079SEd Maste goto out; 52219261079SEd Maste } else { 52319261079SEd Maste challenge = sshbuf_ptr(challenge_buf); 52419261079SEd Maste challenge_len = sshbuf_len(challenge_buf); 52519261079SEd Maste debug3_f("using explicit challenge len=%zd", challenge_len); 52619261079SEd Maste } 52719261079SEd Maste if ((skp = sshsk_open(provider_path)) == NULL) { 52819261079SEd Maste r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 52919261079SEd Maste goto out; 53019261079SEd Maste } 53119261079SEd Maste /* XXX validate flags? */ 53219261079SEd Maste /* enroll key */ 53319261079SEd Maste if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, 53419261079SEd Maste flags, pin, opts, &resp)) != 0) { 53519261079SEd Maste debug_f("provider \"%s\" failure %d", provider_path, r); 53619261079SEd Maste r = skerr_to_ssherr(r); 53719261079SEd Maste goto out; 53819261079SEd Maste } 53919261079SEd Maste 5401323ec57SEd Maste if ((r = sshsk_key_from_response(alg, application, resp->flags, 54119261079SEd Maste resp, &key)) != 0) 54219261079SEd Maste goto out; 54319261079SEd Maste 54419261079SEd Maste /* Optionally fill in the attestation information */ 54519261079SEd Maste if ((r = fill_attestation_blob(resp, attest)) != 0) 54619261079SEd Maste goto out; 54719261079SEd Maste 54819261079SEd Maste /* success */ 54919261079SEd Maste *keyp = key; 55019261079SEd Maste key = NULL; /* transferred */ 55119261079SEd Maste r = 0; 55219261079SEd Maste out: 55319261079SEd Maste sshsk_free_options(opts); 55419261079SEd Maste sshsk_free(skp); 55519261079SEd Maste sshkey_free(key); 55619261079SEd Maste sshsk_free_enroll_response(resp); 55719261079SEd Maste explicit_bzero(randchall, sizeof(randchall)); 55819261079SEd Maste return r; 55919261079SEd Maste } 56019261079SEd Maste 56119261079SEd Maste #ifdef WITH_OPENSSL 56219261079SEd Maste static int 56319261079SEd Maste sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig) 56419261079SEd Maste { 56519261079SEd Maste struct sshbuf *inner_sig = NULL; 56619261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 56719261079SEd Maste 56819261079SEd Maste /* Check response validity */ 56919261079SEd Maste if (resp->sig_r == NULL || resp->sig_s == NULL) { 57019261079SEd Maste error_f("sk_sign response invalid"); 57119261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 57219261079SEd Maste goto out; 57319261079SEd Maste } 57419261079SEd Maste if ((inner_sig = sshbuf_new()) == NULL) { 57519261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 57619261079SEd Maste goto out; 57719261079SEd Maste } 57819261079SEd Maste /* Prepare and append inner signature object */ 57919261079SEd Maste if ((r = sshbuf_put_bignum2_bytes(inner_sig, 58019261079SEd Maste resp->sig_r, resp->sig_r_len)) != 0 || 58119261079SEd Maste (r = sshbuf_put_bignum2_bytes(inner_sig, 58219261079SEd Maste resp->sig_s, resp->sig_s_len)) != 0) { 58319261079SEd Maste error_fr(r, "compose inner"); 58419261079SEd Maste goto out; 58519261079SEd Maste } 58619261079SEd Maste if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 || 58719261079SEd Maste (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 58819261079SEd Maste (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 58919261079SEd Maste error_fr(r, "compose"); 59019261079SEd Maste goto out; 59119261079SEd Maste } 59219261079SEd Maste #ifdef DEBUG_SK 59319261079SEd Maste fprintf(stderr, "%s: sig_r:\n", __func__); 59419261079SEd Maste sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 59519261079SEd Maste fprintf(stderr, "%s: sig_s:\n", __func__); 59619261079SEd Maste sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr); 59719261079SEd Maste fprintf(stderr, "%s: inner:\n", __func__); 59819261079SEd Maste sshbuf_dump(inner_sig, stderr); 59919261079SEd Maste #endif 60019261079SEd Maste r = 0; 60119261079SEd Maste out: 60219261079SEd Maste sshbuf_free(inner_sig); 60319261079SEd Maste return r; 60419261079SEd Maste } 60519261079SEd Maste #endif /* WITH_OPENSSL */ 60619261079SEd Maste 60719261079SEd Maste static int 60819261079SEd Maste sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig) 60919261079SEd Maste { 61019261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 61119261079SEd Maste 61219261079SEd Maste /* Check response validity */ 61319261079SEd Maste if (resp->sig_r == NULL) { 61419261079SEd Maste error_f("sk_sign response invalid"); 61519261079SEd Maste r = SSH_ERR_INVALID_FORMAT; 61619261079SEd Maste goto out; 61719261079SEd Maste } 61819261079SEd Maste if ((r = sshbuf_put_string(sig, 61919261079SEd Maste resp->sig_r, resp->sig_r_len)) != 0 || 62019261079SEd Maste (r = sshbuf_put_u8(sig, resp->flags)) != 0 || 62119261079SEd Maste (r = sshbuf_put_u32(sig, resp->counter)) != 0) { 62219261079SEd Maste error_fr(r, "compose"); 62319261079SEd Maste goto out; 62419261079SEd Maste } 62519261079SEd Maste #ifdef DEBUG_SK 62619261079SEd Maste fprintf(stderr, "%s: sig_r:\n", __func__); 62719261079SEd Maste sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); 62819261079SEd Maste #endif 62919261079SEd Maste r = 0; 63019261079SEd Maste out: 63119261079SEd Maste return r; 63219261079SEd Maste } 63319261079SEd Maste 63419261079SEd Maste int 63519261079SEd Maste sshsk_sign(const char *provider_path, struct sshkey *key, 63619261079SEd Maste u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, 63719261079SEd Maste u_int compat, const char *pin) 63819261079SEd Maste { 63919261079SEd Maste struct sshsk_provider *skp = NULL; 64019261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 64119261079SEd Maste int type, alg; 64219261079SEd Maste struct sk_sign_response *resp = NULL; 64319261079SEd Maste struct sshbuf *inner_sig = NULL, *sig = NULL; 64419261079SEd Maste struct sk_option **opts = NULL; 64519261079SEd Maste 64619261079SEd Maste debug_f("provider \"%s\", key %s, flags 0x%02x%s", 64719261079SEd Maste provider_path, sshkey_type(key), key->sk_flags, 64819261079SEd Maste (pin != NULL && *pin != '\0') ? " with-pin" : ""); 64919261079SEd Maste 65019261079SEd Maste if (sigp != NULL) 65119261079SEd Maste *sigp = NULL; 65219261079SEd Maste if (lenp != NULL) 65319261079SEd Maste *lenp = 0; 65419261079SEd Maste type = sshkey_type_plain(key->type); 65519261079SEd Maste switch (type) { 65619261079SEd Maste #ifdef WITH_OPENSSL 65719261079SEd Maste case KEY_ECDSA_SK: 65819261079SEd Maste alg = SSH_SK_ECDSA; 65919261079SEd Maste break; 66019261079SEd Maste #endif /* WITH_OPENSSL */ 66119261079SEd Maste case KEY_ED25519_SK: 66219261079SEd Maste alg = SSH_SK_ED25519; 66319261079SEd Maste break; 66419261079SEd Maste default: 66519261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 66619261079SEd Maste } 66719261079SEd Maste if (provider_path == NULL || 66819261079SEd Maste key->sk_key_handle == NULL || 66919261079SEd Maste key->sk_application == NULL || *key->sk_application == '\0') { 67019261079SEd Maste r = SSH_ERR_INVALID_ARGUMENT; 67119261079SEd Maste goto out; 67219261079SEd Maste } 67319261079SEd Maste if ((skp = sshsk_open(provider_path)) == NULL) { 67419261079SEd Maste r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 67519261079SEd Maste goto out; 67619261079SEd Maste } 67719261079SEd Maste #ifdef DEBUG_SK 67819261079SEd Maste fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n", 67919261079SEd Maste __func__, key->sk_flags, key->sk_application); 68019261079SEd Maste fprintf(stderr, "%s: sk_key_handle:\n", __func__); 68119261079SEd Maste sshbuf_dump(key->sk_key_handle, stderr); 68219261079SEd Maste #endif 68319261079SEd Maste if ((r = skp->sk_sign(alg, data, datalen, key->sk_application, 68419261079SEd Maste sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), 68519261079SEd Maste key->sk_flags, pin, opts, &resp)) != 0) { 68619261079SEd Maste debug_f("sk_sign failed with code %d", r); 68719261079SEd Maste r = skerr_to_ssherr(r); 68819261079SEd Maste goto out; 68919261079SEd Maste } 69019261079SEd Maste /* Assemble signature */ 69119261079SEd Maste if ((sig = sshbuf_new()) == NULL) { 69219261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 69319261079SEd Maste goto out; 69419261079SEd Maste } 69519261079SEd Maste if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) { 69619261079SEd Maste error_fr(r, "compose outer"); 69719261079SEd Maste goto out; 69819261079SEd Maste } 69919261079SEd Maste switch (type) { 70019261079SEd Maste #ifdef WITH_OPENSSL 70119261079SEd Maste case KEY_ECDSA_SK: 70219261079SEd Maste if ((r = sshsk_ecdsa_sig(resp, sig)) != 0) 70319261079SEd Maste goto out; 70419261079SEd Maste break; 70519261079SEd Maste #endif /* WITH_OPENSSL */ 70619261079SEd Maste case KEY_ED25519_SK: 70719261079SEd Maste if ((r = sshsk_ed25519_sig(resp, sig)) != 0) 70819261079SEd Maste goto out; 70919261079SEd Maste break; 71019261079SEd Maste } 71119261079SEd Maste #ifdef DEBUG_SK 71219261079SEd Maste fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 71319261079SEd Maste __func__, resp->flags, resp->counter); 71419261079SEd Maste fprintf(stderr, "%s: data to sign:\n", __func__); 71519261079SEd Maste sshbuf_dump_data(data, datalen, stderr); 71619261079SEd Maste fprintf(stderr, "%s: sigbuf:\n", __func__); 71719261079SEd Maste sshbuf_dump(sig, stderr); 71819261079SEd Maste #endif 71919261079SEd Maste if (sigp != NULL) { 72019261079SEd Maste if ((*sigp = malloc(sshbuf_len(sig))) == NULL) { 72119261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 72219261079SEd Maste goto out; 72319261079SEd Maste } 72419261079SEd Maste memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig)); 72519261079SEd Maste } 72619261079SEd Maste if (lenp != NULL) 72719261079SEd Maste *lenp = sshbuf_len(sig); 72819261079SEd Maste /* success */ 72919261079SEd Maste r = 0; 73019261079SEd Maste out: 73119261079SEd Maste sshsk_free_options(opts); 73219261079SEd Maste sshsk_free(skp); 73319261079SEd Maste sshsk_free_sign_response(resp); 73419261079SEd Maste sshbuf_free(sig); 73519261079SEd Maste sshbuf_free(inner_sig); 73619261079SEd Maste return r; 73719261079SEd Maste } 73819261079SEd Maste 73919261079SEd Maste static void 74019261079SEd Maste sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) 74119261079SEd Maste { 74219261079SEd Maste size_t i; 74319261079SEd Maste 74419261079SEd Maste if (nrks == 0 || rks == NULL) 74519261079SEd Maste return; 74619261079SEd Maste for (i = 0; i < nrks; i++) { 74719261079SEd Maste free(rks[i]->application); 7481323ec57SEd Maste freezero(rks[i]->user_id, rks[i]->user_id_len); 74919261079SEd Maste freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); 75019261079SEd Maste freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); 75119261079SEd Maste freezero(rks[i]->key.signature, rks[i]->key.signature_len); 75219261079SEd Maste freezero(rks[i]->key.attestation_cert, 75319261079SEd Maste rks[i]->key.attestation_cert_len); 75419261079SEd Maste freezero(rks[i], sizeof(**rks)); 75519261079SEd Maste } 75619261079SEd Maste free(rks); 75719261079SEd Maste } 75819261079SEd Maste 7591323ec57SEd Maste static void 7601323ec57SEd Maste sshsk_free_resident_key(struct sshsk_resident_key *srk) 7611323ec57SEd Maste { 7621323ec57SEd Maste if (srk == NULL) 7631323ec57SEd Maste return; 7641323ec57SEd Maste sshkey_free(srk->key); 7651323ec57SEd Maste freezero(srk->user_id, srk->user_id_len); 7661323ec57SEd Maste free(srk); 7671323ec57SEd Maste } 7681323ec57SEd Maste 7691323ec57SEd Maste 7701323ec57SEd Maste void 7711323ec57SEd Maste sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks) 7721323ec57SEd Maste { 7731323ec57SEd Maste size_t i; 7741323ec57SEd Maste 7751323ec57SEd Maste if (srks == NULL || nsrks == 0) 7761323ec57SEd Maste return; 7771323ec57SEd Maste 7781323ec57SEd Maste for (i = 0; i < nsrks; i++) 7791323ec57SEd Maste sshsk_free_resident_key(srks[i]); 7801323ec57SEd Maste free(srks); 7811323ec57SEd Maste } 7821323ec57SEd Maste 78319261079SEd Maste int 78419261079SEd Maste sshsk_load_resident(const char *provider_path, const char *device, 7851323ec57SEd Maste const char *pin, u_int flags, struct sshsk_resident_key ***srksp, 7861323ec57SEd Maste size_t *nsrksp) 78719261079SEd Maste { 78819261079SEd Maste struct sshsk_provider *skp = NULL; 78919261079SEd Maste int r = SSH_ERR_INTERNAL_ERROR; 79019261079SEd Maste struct sk_resident_key **rks = NULL; 7911323ec57SEd Maste size_t i, nrks = 0, nsrks = 0; 7921323ec57SEd Maste struct sshkey *key = NULL; 7931323ec57SEd Maste struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp; 7941323ec57SEd Maste uint8_t sk_flags; 79519261079SEd Maste struct sk_option **opts = NULL; 79619261079SEd Maste 79719261079SEd Maste debug_f("provider \"%s\"%s", provider_path, 79819261079SEd Maste (pin != NULL && *pin != '\0') ? ", have-pin": ""); 79919261079SEd Maste 8001323ec57SEd Maste if (srksp == NULL || nsrksp == NULL) 80119261079SEd Maste return SSH_ERR_INVALID_ARGUMENT; 8021323ec57SEd Maste *srksp = NULL; 8031323ec57SEd Maste *nsrksp = 0; 80419261079SEd Maste 80519261079SEd Maste if ((r = make_options(device, NULL, &opts)) != 0) 80619261079SEd Maste goto out; 80719261079SEd Maste if ((skp = sshsk_open(provider_path)) == NULL) { 80819261079SEd Maste r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ 80919261079SEd Maste goto out; 81019261079SEd Maste } 81119261079SEd Maste if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) { 81219261079SEd Maste error("Provider \"%s\" returned failure %d", provider_path, r); 81319261079SEd Maste r = skerr_to_ssherr(r); 81419261079SEd Maste goto out; 81519261079SEd Maste } 81619261079SEd Maste for (i = 0; i < nrks; i++) { 8171323ec57SEd Maste debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu", 8181323ec57SEd Maste i, rks[i]->slot, rks[i]->alg, rks[i]->application, 8191323ec57SEd Maste rks[i]->user_id_len); 82019261079SEd Maste /* XXX need better filter here */ 82119261079SEd Maste if (strncmp(rks[i]->application, "ssh:", 4) != 0) 82219261079SEd Maste continue; 82319261079SEd Maste switch (rks[i]->alg) { 82419261079SEd Maste case SSH_SK_ECDSA: 82519261079SEd Maste case SSH_SK_ED25519: 82619261079SEd Maste break; 82719261079SEd Maste default: 82819261079SEd Maste continue; 82919261079SEd Maste } 8301323ec57SEd Maste sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY; 83119261079SEd Maste if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD)) 8321323ec57SEd Maste sk_flags |= SSH_SK_USER_VERIFICATION_REQD; 83319261079SEd Maste if ((r = sshsk_key_from_response(rks[i]->alg, 8341323ec57SEd Maste rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0) 83519261079SEd Maste goto out; 8361323ec57SEd Maste if ((srk = calloc(1, sizeof(*srk))) == NULL) { 8371323ec57SEd Maste error_f("calloc failed"); 8381323ec57SEd Maste r = SSH_ERR_ALLOC_FAIL; 8391323ec57SEd Maste goto out; 8401323ec57SEd Maste } 8411323ec57SEd Maste srk->key = key; 8421323ec57SEd Maste key = NULL; /* transferred */ 8431323ec57SEd Maste if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) { 8441323ec57SEd Maste error_f("calloc failed"); 8451323ec57SEd Maste r = SSH_ERR_ALLOC_FAIL; 8461323ec57SEd Maste goto out; 8471323ec57SEd Maste } 8481323ec57SEd Maste memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len); 8491323ec57SEd Maste srk->user_id_len = rks[i]->user_id_len; 8501323ec57SEd Maste if ((tmp = recallocarray(srks, nsrks, nsrks + 1, 85119261079SEd Maste sizeof(*tmp))) == NULL) { 85219261079SEd Maste error_f("recallocarray failed"); 85319261079SEd Maste r = SSH_ERR_ALLOC_FAIL; 85419261079SEd Maste goto out; 85519261079SEd Maste } 8561323ec57SEd Maste srks = tmp; 8571323ec57SEd Maste srks[nsrks++] = srk; 8581323ec57SEd Maste srk = NULL; 85919261079SEd Maste /* XXX synthesise comment */ 86019261079SEd Maste } 86119261079SEd Maste /* success */ 8621323ec57SEd Maste *srksp = srks; 8631323ec57SEd Maste *nsrksp = nsrks; 8641323ec57SEd Maste srks = NULL; 8651323ec57SEd Maste nsrks = 0; 86619261079SEd Maste r = 0; 86719261079SEd Maste out: 86819261079SEd Maste sshsk_free_options(opts); 86919261079SEd Maste sshsk_free(skp); 87019261079SEd Maste sshsk_free_sk_resident_keys(rks, nrks); 87119261079SEd Maste sshkey_free(key); 8721323ec57SEd Maste sshsk_free_resident_key(srk); 8731323ec57SEd Maste sshsk_free_resident_keys(srks, nsrks); 87419261079SEd Maste return r; 87519261079SEd Maste } 87619261079SEd Maste 87719261079SEd Maste #endif /* ENABLE_SK */ 878