[BACK]Return to ssh-sk.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/ssh-sk.c, Revision 1.40

1.40    ! djm         1: /* $OpenBSD: ssh-sk.c,v 1.39 2022/07/20 03:29:14 djm Exp $ */
1.1       djm         2: /*
                      3:  * Copyright (c) 2019 Google LLC
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17:
                     18: /* #define DEBUG_SK 1 */
                     19:
                     20: #include <dlfcn.h>
                     21: #include <stddef.h>
                     22: #include <stdint.h>
                     23: #include <string.h>
                     24: #include <stdio.h>
                     25:
1.15      naddy      26: #ifdef WITH_OPENSSL
1.1       djm        27: #include <openssl/objects.h>
                     28: #include <openssl/ec.h>
1.15      naddy      29: #endif /* WITH_OPENSSL */
1.1       djm        30:
                     31: #include "log.h"
                     32: #include "misc.h"
                     33: #include "sshbuf.h"
                     34: #include "sshkey.h"
                     35: #include "ssherr.h"
                     36: #include "digest.h"
                     37:
                     38: #include "ssh-sk.h"
                     39: #include "sk-api.h"
1.6       markus     40: #include "crypto_api.h"
1.1       djm        41:
                     42: struct sshsk_provider {
                     43:        char *path;
                     44:        void *dlhandle;
                     45:
                     46:        /* Return the version of the middleware API */
                     47:        uint32_t (*sk_api_version)(void);
                     48:
                     49:        /* Enroll a U2F key (private key generation) */
1.7       markus     50:        int (*sk_enroll)(int alg, const uint8_t *challenge,
                     51:            size_t challenge_len, const char *application, uint8_t flags,
1.24      djm        52:            const char *pin, struct sk_option **opts,
                     53:            struct sk_enroll_response **enroll_response);
1.1       djm        54:
                     55:        /* Sign a challenge */
1.7       markus     56:        int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
1.1       djm        57:            const char *application,
                     58:            const uint8_t *key_handle, size_t key_handle_len,
1.24      djm        59:            uint8_t flags, const char *pin, struct sk_option **opts,
1.21      djm        60:            struct sk_sign_response **sign_response);
1.20      djm        61:
                     62:        /* Enumerate resident keys */
1.24      djm        63:        int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts,
1.20      djm        64:            struct sk_resident_key ***rks, size_t *nrks);
1.1       djm        65: };
                     66:
1.12      djm        67: /* Built-in version */
                     68: int ssh_sk_enroll(int alg, const uint8_t *challenge,
                     69:     size_t challenge_len, const char *application, uint8_t flags,
1.24      djm        70:     const char *pin, struct sk_option **opts,
                     71:     struct sk_enroll_response **enroll_response);
1.12      djm        72: int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
                     73:     const char *application,
                     74:     const uint8_t *key_handle, size_t key_handle_len,
1.24      djm        75:     uint8_t flags, const char *pin, struct sk_option **opts,
                     76:     struct sk_sign_response **sign_response);
                     77: int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts,
1.20      djm        78:     struct sk_resident_key ***rks, size_t *nrks);
1.12      djm        79:
1.1       djm        80: static void
                     81: sshsk_free(struct sshsk_provider *p)
                     82: {
                     83:        if (p == NULL)
                     84:                return;
                     85:        free(p->path);
                     86:        if (p->dlhandle != NULL)
                     87:                dlclose(p->dlhandle);
                     88:        free(p);
                     89: }
                     90:
                     91: static struct sshsk_provider *
                     92: sshsk_open(const char *path)
                     93: {
                     94:        struct sshsk_provider *ret = NULL;
                     95:        uint32_t version;
                     96:
1.28      djm        97:        if (path == NULL || *path == '\0') {
                     98:                error("No FIDO SecurityKeyProvider specified");
                     99:                return NULL;
                    100:        }
1.1       djm       101:        if ((ret = calloc(1, sizeof(*ret))) == NULL) {
1.33      djm       102:                error_f("calloc failed");
1.1       djm       103:                return NULL;
                    104:        }
                    105:        if ((ret->path = strdup(path)) == NULL) {
1.33      djm       106:                error_f("strdup failed");
1.1       djm       107:                goto fail;
1.12      djm       108:        }
                    109:        /* Skip the rest if we're using the linked in middleware */
                    110:        if (strcasecmp(ret->path, "internal") == 0) {
                    111:                ret->sk_enroll = ssh_sk_enroll;
                    112:                ret->sk_sign = ssh_sk_sign;
1.20      djm       113:                ret->sk_load_resident_keys = ssh_sk_load_resident_keys;
1.12      djm       114:                return ret;
1.1       djm       115:        }
1.40    ! djm       116:        if (lib_contains_symbol(path, "sk_api_version") != 0) {
        !           117:                error("provider %s is not an OpenSSH FIDO library", path);
        !           118:                goto fail;
        !           119:        }
1.1       djm       120:        if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
1.27      naddy     121:                error("Provider \"%s\" dlopen failed: %s", path, dlerror());
1.1       djm       122:                goto fail;
                    123:        }
                    124:        if ((ret->sk_api_version = dlsym(ret->dlhandle,
                    125:            "sk_api_version")) == NULL) {
1.40    ! djm       126:                fatal("Provider \"%s\" dlsym(sk_api_version) failed: %s",
1.27      naddy     127:                    path, dlerror());
1.1       djm       128:        }
                    129:        version = ret->sk_api_version();
1.33      djm       130:        debug_f("provider %s implements version 0x%08lx", ret->path,
                    131:            (u_long)version);
1.1       djm       132:        if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
1.27      naddy     133:                error("Provider \"%s\" implements unsupported "
1.22      djm       134:                    "version 0x%08lx (supported: 0x%08lx)",
                    135:                    path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR);
1.1       djm       136:                goto fail;
                    137:        }
                    138:        if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
1.27      naddy     139:                error("Provider %s dlsym(sk_enroll) failed: %s",
                    140:                    path, dlerror());
1.1       djm       141:                goto fail;
                    142:        }
                    143:        if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
1.27      naddy     144:                error("Provider \"%s\" dlsym(sk_sign) failed: %s",
1.1       djm       145:                    path, dlerror());
                    146:                goto fail;
                    147:        }
1.20      djm       148:        if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle,
                    149:            "sk_load_resident_keys")) == NULL) {
1.27      naddy     150:                error("Provider \"%s\" dlsym(sk_load_resident_keys) "
                    151:                    "failed: %s", path, dlerror());
1.20      djm       152:                goto fail;
                    153:        }
1.1       djm       154:        /* success */
                    155:        return ret;
                    156: fail:
                    157:        sshsk_free(ret);
                    158:        return NULL;
                    159: }
                    160:
                    161: static void
                    162: sshsk_free_enroll_response(struct sk_enroll_response *r)
                    163: {
                    164:        if (r == NULL)
                    165:                return;
                    166:        freezero(r->key_handle, r->key_handle_len);
                    167:        freezero(r->public_key, r->public_key_len);
                    168:        freezero(r->signature, r->signature_len);
                    169:        freezero(r->attestation_cert, r->attestation_cert_len);
1.32      djm       170:        freezero(r->authdata, r->authdata_len);
1.1       djm       171:        freezero(r, sizeof(*r));
1.17      djm       172: }
1.1       djm       173:
                    174: static void
                    175: sshsk_free_sign_response(struct sk_sign_response *r)
                    176: {
                    177:        if (r == NULL)
                    178:                return;
                    179:        freezero(r->sig_r, r->sig_r_len);
                    180:        freezero(r->sig_s, r->sig_s_len);
                    181:        freezero(r, sizeof(*r));
1.17      djm       182: }
1.1       djm       183:
1.15      naddy     184: #ifdef WITH_OPENSSL
1.2       markus    185: /* Assemble key from response */
                    186: static int
                    187: sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
                    188: {
                    189:        struct sshkey *key = NULL;
                    190:        struct sshbuf *b = NULL;
                    191:        EC_POINT *q = NULL;
                    192:        int r;
                    193:
                    194:        *keyp = NULL;
                    195:        if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
1.33      djm       196:                error_f("sshkey_new failed");
1.2       markus    197:                r = SSH_ERR_ALLOC_FAIL;
                    198:                goto out;
                    199:        }
                    200:        key->ecdsa_nid = NID_X9_62_prime256v1;
                    201:        if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
                    202:            (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
                    203:            (b = sshbuf_new()) == NULL) {
1.33      djm       204:                error_f("allocation failed");
1.2       markus    205:                r = SSH_ERR_ALLOC_FAIL;
                    206:                goto out;
                    207:        }
                    208:        if ((r = sshbuf_put_string(b,
                    209:            resp->public_key, resp->public_key_len)) != 0) {
1.33      djm       210:                error_fr(r, "sshbuf_put_string");
1.2       markus    211:                goto out;
                    212:        }
                    213:        if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
1.33      djm       214:                error_fr(r, "parse");
1.2       markus    215:                r = SSH_ERR_INVALID_FORMAT;
                    216:                goto out;
                    217:        }
                    218:        if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
1.27      naddy     219:                error("Authenticator returned invalid ECDSA key");
1.2       markus    220:                r = SSH_ERR_KEY_INVALID_EC_VALUE;
                    221:                goto out;
                    222:        }
                    223:        if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
                    224:                /* XXX assume it is a allocation error */
1.33      djm       225:                error_f("allocation failed");
1.2       markus    226:                r = SSH_ERR_ALLOC_FAIL;
                    227:                goto out;
                    228:        }
                    229:        /* success */
                    230:        *keyp = key;
                    231:        key = NULL; /* transferred */
                    232:        r = 0;
                    233:  out:
                    234:        EC_POINT_free(q);
                    235:        sshkey_free(key);
                    236:        sshbuf_free(b);
                    237:        return r;
                    238: }
1.15      naddy     239: #endif /* WITH_OPENSSL */
1.2       markus    240:
1.6       markus    241: static int
                    242: sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
                    243: {
                    244:        struct sshkey *key = NULL;
                    245:        int r;
                    246:
                    247:        *keyp = NULL;
                    248:        if (resp->public_key_len != ED25519_PK_SZ) {
1.33      djm       249:                error_f("invalid size: %zu", resp->public_key_len);
1.6       markus    250:                r = SSH_ERR_INVALID_FORMAT;
                    251:                goto out;
                    252:        }
                    253:        if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
1.33      djm       254:                error_f("sshkey_new failed");
1.6       markus    255:                r = SSH_ERR_ALLOC_FAIL;
                    256:                goto out;
                    257:        }
                    258:        if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
1.33      djm       259:                error_f("malloc failed");
1.6       markus    260:                r = SSH_ERR_ALLOC_FAIL;
                    261:                goto out;
                    262:        }
                    263:        memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
                    264:        /* success */
                    265:        *keyp = key;
                    266:        key = NULL; /* transferred */
                    267:        r = 0;
                    268:  out:
                    269:        sshkey_free(key);
                    270:        return r;
                    271: }
                    272:
1.19      djm       273: static int
                    274: sshsk_key_from_response(int alg, const char *application, uint8_t flags,
                    275:     struct sk_enroll_response *resp, struct sshkey **keyp)
                    276: {
                    277:        struct sshkey *key = NULL;
                    278:        int r = SSH_ERR_INTERNAL_ERROR;
                    279:
                    280:        *keyp = NULL;
                    281:
                    282:        /* Check response validity */
1.20      djm       283:        if (resp->public_key == NULL || resp->key_handle == NULL) {
1.33      djm       284:                error_f("sk_enroll response invalid");
1.19      djm       285:                r = SSH_ERR_INVALID_FORMAT;
                    286:                goto out;
                    287:        }
                    288:        switch (alg) {
                    289: #ifdef WITH_OPENSSL
                    290:        case SSH_SK_ECDSA:
                    291:                if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
                    292:                        goto out;
                    293:                break;
                    294: #endif /* WITH_OPENSSL */
                    295:        case SSH_SK_ED25519:
                    296:                if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
                    297:                        goto out;
                    298:                break;
                    299:        default:
1.33      djm       300:                error_f("unsupported algorithm %d", alg);
1.19      djm       301:                r = SSH_ERR_INVALID_ARGUMENT;
                    302:                goto out;
                    303:        }
                    304:        key->sk_flags = flags;
                    305:        if ((key->sk_key_handle = sshbuf_new()) == NULL ||
                    306:            (key->sk_reserved = sshbuf_new()) == NULL) {
1.33      djm       307:                error_f("allocation failed");
1.19      djm       308:                r = SSH_ERR_ALLOC_FAIL;
                    309:                goto out;
                    310:        }
                    311:        if ((key->sk_application = strdup(application)) == NULL) {
1.33      djm       312:                error_f("strdup application failed");
1.19      djm       313:                r = SSH_ERR_ALLOC_FAIL;
                    314:                goto out;
                    315:        }
                    316:        if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
                    317:            resp->key_handle_len)) != 0) {
1.33      djm       318:                error_fr(r, "put key handle");
1.19      djm       319:                goto out;
                    320:        }
                    321:        /* success */
                    322:        r = 0;
                    323:        *keyp = key;
                    324:        key = NULL;
                    325:  out:
                    326:        sshkey_free(key);
                    327:        return r;
                    328: }
                    329:
1.23      djm       330: static int
                    331: skerr_to_ssherr(int skerr)
                    332: {
                    333:        switch (skerr) {
                    334:        case SSH_SK_ERR_UNSUPPORTED:
                    335:                return SSH_ERR_FEATURE_UNSUPPORTED;
                    336:        case SSH_SK_ERR_PIN_REQUIRED:
                    337:                return SSH_ERR_KEY_WRONG_PASSPHRASE;
1.25      djm       338:        case SSH_SK_ERR_DEVICE_NOT_FOUND:
                    339:                return SSH_ERR_DEVICE_NOT_FOUND;
1.39      djm       340:        case SSH_SK_ERR_CREDENTIAL_EXISTS:
                    341:                return SSH_ERR_KEY_BAD_PERMISSIONS;
1.23      djm       342:        case SSH_SK_ERR_GENERAL:
                    343:        default:
                    344:                return SSH_ERR_INVALID_FORMAT;
                    345:        }
                    346: }
                    347:
1.24      djm       348: static void
                    349: sshsk_free_options(struct sk_option **opts)
                    350: {
                    351:        size_t i;
                    352:
                    353:        if (opts == NULL)
                    354:                return;
                    355:        for (i = 0; opts[i] != NULL; i++) {
                    356:                free(opts[i]->name);
                    357:                free(opts[i]->value);
                    358:                free(opts[i]);
                    359:        }
                    360:        free(opts);
                    361: }
                    362:
                    363: static int
                    364: sshsk_add_option(struct sk_option ***optsp, size_t *noptsp,
                    365:     const char *name, const char *value, uint8_t required)
                    366: {
                    367:        struct sk_option **opts = *optsp;
                    368:        size_t nopts = *noptsp;
                    369:
                    370:        if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */
                    371:            sizeof(*opts))) == NULL) {
1.33      djm       372:                error_f("array alloc failed");
1.24      djm       373:                return SSH_ERR_ALLOC_FAIL;
                    374:        }
                    375:        *optsp = opts;
                    376:        *noptsp = nopts + 1;
                    377:        if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) {
1.33      djm       378:                error_f("alloc failed");
1.24      djm       379:                return SSH_ERR_ALLOC_FAIL;
                    380:        }
                    381:        if ((opts[nopts]->name = strdup(name)) == NULL ||
                    382:            (opts[nopts]->value = strdup(value)) == NULL) {
1.33      djm       383:                error_f("alloc failed");
1.24      djm       384:                return SSH_ERR_ALLOC_FAIL;
                    385:        }
                    386:        opts[nopts]->required = required;
                    387:        return 0;
                    388: }
                    389:
                    390: static int
                    391: make_options(const char *device, const char *user_id,
                    392:     struct sk_option ***optsp)
                    393: {
                    394:        struct sk_option **opts = NULL;
                    395:        size_t nopts = 0;
                    396:        int r, ret = SSH_ERR_INTERNAL_ERROR;
                    397:
                    398:        if (device != NULL &&
                    399:            (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) {
                    400:                ret = r;
                    401:                goto out;
                    402:        }
                    403:        if (user_id != NULL &&
                    404:            (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) {
                    405:                ret = r;
                    406:                goto out;
                    407:        }
                    408:        /* success */
                    409:        *optsp = opts;
                    410:        opts = NULL;
                    411:        nopts = 0;
                    412:        ret = 0;
                    413:  out:
                    414:        sshsk_free_options(opts);
                    415:        return ret;
                    416: }
                    417:
1.32      djm       418:
                    419: static int
                    420: fill_attestation_blob(const struct sk_enroll_response *resp,
                    421:     struct sshbuf *attest)
                    422: {
                    423:        int r;
                    424:
                    425:        if (attest == NULL)
                    426:                return 0; /* nothing to do */
                    427:        if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 ||
                    428:            (r = sshbuf_put_string(attest,
                    429:            resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
                    430:            (r = sshbuf_put_string(attest,
                    431:            resp->signature, resp->signature_len)) != 0 ||
                    432:            (r = sshbuf_put_string(attest,
                    433:            resp->authdata, resp->authdata_len)) != 0 ||
                    434:            (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */
                    435:            (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) {
1.33      djm       436:                error_fr(r, "compose");
1.32      djm       437:                return r;
                    438:        }
                    439:        /* success */
                    440:        return 0;
                    441: }
                    442:
1.1       djm       443: int
1.24      djm       444: sshsk_enroll(int type, const char *provider_path, const char *device,
                    445:     const char *application, const char *userid, uint8_t flags,
                    446:     const char *pin, struct sshbuf *challenge_buf,
1.21      djm       447:     struct sshkey **keyp, struct sshbuf *attest)
1.1       djm       448: {
                    449:        struct sshsk_provider *skp = NULL;
                    450:        struct sshkey *key = NULL;
                    451:        u_char randchall[32];
                    452:        const u_char *challenge;
                    453:        size_t challenge_len;
                    454:        struct sk_enroll_response *resp = NULL;
1.24      djm       455:        struct sk_option **opts = NULL;
1.1       djm       456:        int r = SSH_ERR_INTERNAL_ERROR;
1.7       markus    457:        int alg;
1.1       djm       458:
1.33      djm       459:        debug_f("provider \"%s\", device \"%s\", application \"%s\", "
                    460:            "userid \"%s\", flags 0x%02x, challenge len %zu%s",
1.24      djm       461:            provider_path, device, application, userid, flags,
                    462:            challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
1.21      djm       463:            (pin != NULL && *pin != '\0') ? " with-pin" : "");
1.13      djm       464:
1.1       djm       465:        *keyp = NULL;
                    466:        if (attest)
                    467:                sshbuf_reset(attest);
1.24      djm       468:
                    469:        if ((r = make_options(device, userid, &opts)) != 0)
                    470:                goto out;
                    471:
1.6       markus    472:        switch (type) {
1.15      naddy     473: #ifdef WITH_OPENSSL
1.6       markus    474:        case KEY_ECDSA_SK:
1.7       markus    475:                alg = SSH_SK_ECDSA;
                    476:                break;
1.15      naddy     477: #endif /* WITH_OPENSSL */
1.6       markus    478:        case KEY_ED25519_SK:
1.7       markus    479:                alg = SSH_SK_ED25519;
1.6       markus    480:                break;
                    481:        default:
1.33      djm       482:                error_f("unsupported key type");
1.6       markus    483:                r = SSH_ERR_INVALID_ARGUMENT;
                    484:                goto out;
                    485:        }
1.1       djm       486:        if (provider_path == NULL) {
1.33      djm       487:                error_f("missing provider");
1.1       djm       488:                r = SSH_ERR_INVALID_ARGUMENT;
                    489:                goto out;
                    490:        }
                    491:        if (application == NULL || *application == '\0') {
1.33      djm       492:                error_f("missing application");
1.1       djm       493:                r = SSH_ERR_INVALID_ARGUMENT;
                    494:                goto out;
                    495:        }
                    496:        if (challenge_buf == NULL) {
1.33      djm       497:                debug_f("using random challenge");
1.1       djm       498:                arc4random_buf(randchall, sizeof(randchall));
                    499:                challenge = randchall;
                    500:                challenge_len = sizeof(randchall);
                    501:        } else if (sshbuf_len(challenge_buf) == 0) {
                    502:                error("Missing enrollment challenge");
                    503:                r = SSH_ERR_INVALID_ARGUMENT;
                    504:                goto out;
                    505:        } else {
                    506:                challenge = sshbuf_ptr(challenge_buf);
                    507:                challenge_len = sshbuf_len(challenge_buf);
1.33      djm       508:                debug3_f("using explicit challenge len=%zd", challenge_len);
1.1       djm       509:        }
                    510:        if ((skp = sshsk_open(provider_path)) == NULL) {
                    511:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
                    512:                goto out;
                    513:        }
                    514:        /* XXX validate flags? */
                    515:        /* enroll key */
1.7       markus    516:        if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
1.24      djm       517:            flags, pin, opts, &resp)) != 0) {
1.33      djm       518:                debug_f("provider \"%s\" failure %d", provider_path, r);
1.23      djm       519:                r = skerr_to_ssherr(r);
1.1       djm       520:                goto out;
                    521:        }
1.19      djm       522:
1.37      djm       523:        if ((r = sshsk_key_from_response(alg, application, resp->flags,
1.19      djm       524:            resp, &key)) != 0)
1.1       djm       525:                goto out;
1.19      djm       526:
1.1       djm       527:        /* Optionally fill in the attestation information */
1.32      djm       528:        if ((r = fill_attestation_blob(resp, attest)) != 0)
                    529:                goto out;
                    530:
1.1       djm       531:        /* success */
                    532:        *keyp = key;
                    533:        key = NULL; /* transferred */
                    534:        r = 0;
                    535:  out:
1.24      djm       536:        sshsk_free_options(opts);
1.1       djm       537:        sshsk_free(skp);
                    538:        sshkey_free(key);
                    539:        sshsk_free_enroll_response(resp);
                    540:        explicit_bzero(randchall, sizeof(randchall));
                    541:        return r;
                    542: }
                    543:
1.15      naddy     544: #ifdef WITH_OPENSSL
1.3       markus    545: static int
1.9       markus    546: sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.3       markus    547: {
                    548:        struct sshbuf *inner_sig = NULL;
                    549:        int r = SSH_ERR_INTERNAL_ERROR;
                    550:
1.8       markus    551:        /* Check response validity */
1.11      markus    552:        if (resp->sig_r == NULL || resp->sig_s == NULL) {
1.33      djm       553:                error_f("sk_sign response invalid");
1.8       markus    554:                r = SSH_ERR_INVALID_FORMAT;
                    555:                goto out;
                    556:        }
1.3       markus    557:        if ((inner_sig = sshbuf_new()) == NULL) {
                    558:                r = SSH_ERR_ALLOC_FAIL;
                    559:                goto out;
                    560:        }
1.9       markus    561:        /* Prepare and append inner signature object */
1.3       markus    562:        if ((r = sshbuf_put_bignum2_bytes(inner_sig,
                    563:            resp->sig_r, resp->sig_r_len)) != 0 ||
                    564:            (r = sshbuf_put_bignum2_bytes(inner_sig,
1.16      djm       565:            resp->sig_s, resp->sig_s_len)) != 0) {
1.33      djm       566:                error_fr(r, "compose inner");
1.3       markus    567:                goto out;
                    568:        }
1.16      djm       569:        if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
                    570:            (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
                    571:            (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.33      djm       572:                error_fr(r, "compose");
1.9       markus    573:                goto out;
                    574:        }
1.3       markus    575: #ifdef DEBUG_SK
                    576:        fprintf(stderr, "%s: sig_r:\n", __func__);
                    577:        sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
                    578:        fprintf(stderr, "%s: sig_s:\n", __func__);
                    579:        sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
1.9       markus    580:        fprintf(stderr, "%s: inner:\n", __func__);
                    581:        sshbuf_dump(inner_sig, stderr);
1.5       markus    582: #endif
                    583:        r = 0;
1.9       markus    584:  out:
1.5       markus    585:        sshbuf_free(inner_sig);
                    586:        return r;
                    587: }
1.15      naddy     588: #endif /* WITH_OPENSSL */
1.5       markus    589:
                    590: static int
1.9       markus    591: sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.5       markus    592: {
                    593:        int r = SSH_ERR_INTERNAL_ERROR;
                    594:
1.8       markus    595:        /* Check response validity */
                    596:        if (resp->sig_r == NULL) {
1.33      djm       597:                error_f("sk_sign response invalid");
1.8       markus    598:                r = SSH_ERR_INVALID_FORMAT;
                    599:                goto out;
                    600:        }
1.9       markus    601:        if ((r = sshbuf_put_string(sig,
1.5       markus    602:            resp->sig_r, resp->sig_r_len)) != 0 ||
1.9       markus    603:            (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
                    604:            (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.33      djm       605:                error_fr(r, "compose");
1.5       markus    606:                goto out;
                    607:        }
                    608: #ifdef DEBUG_SK
                    609:        fprintf(stderr, "%s: sig_r:\n", __func__);
                    610:        sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
1.3       markus    611: #endif
                    612:        r = 0;
1.9       markus    613:  out:
1.29      markus    614:        return r;
1.3       markus    615: }
                    616:
1.1       djm       617: int
1.18      djm       618: sshsk_sign(const char *provider_path, struct sshkey *key,
1.1       djm       619:     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
1.21      djm       620:     u_int compat, const char *pin)
1.1       djm       621: {
                    622:        struct sshsk_provider *skp = NULL;
                    623:        int r = SSH_ERR_INTERNAL_ERROR;
1.7       markus    624:        int type, alg;
1.1       djm       625:        struct sk_sign_response *resp = NULL;
                    626:        struct sshbuf *inner_sig = NULL, *sig = NULL;
1.24      djm       627:        struct sk_option **opts = NULL;
1.13      djm       628:
1.33      djm       629:        debug_f("provider \"%s\", key %s, flags 0x%02x%s",
1.21      djm       630:            provider_path, sshkey_type(key), key->sk_flags,
                    631:            (pin != NULL && *pin != '\0') ? " with-pin" : "");
1.1       djm       632:
                    633:        if (sigp != NULL)
                    634:                *sigp = NULL;
                    635:        if (lenp != NULL)
                    636:                *lenp = 0;
1.5       markus    637:        type = sshkey_type_plain(key->type);
                    638:        switch (type) {
1.15      naddy     639: #ifdef WITH_OPENSSL
1.5       markus    640:        case KEY_ECDSA_SK:
1.7       markus    641:                alg = SSH_SK_ECDSA;
                    642:                break;
1.15      naddy     643: #endif /* WITH_OPENSSL */
1.5       markus    644:        case KEY_ED25519_SK:
1.7       markus    645:                alg = SSH_SK_ED25519;
1.5       markus    646:                break;
                    647:        default:
                    648:                return SSH_ERR_INVALID_ARGUMENT;
                    649:        }
1.1       djm       650:        if (provider_path == NULL ||
                    651:            key->sk_key_handle == NULL ||
                    652:            key->sk_application == NULL || *key->sk_application == '\0') {
                    653:                r = SSH_ERR_INVALID_ARGUMENT;
                    654:                goto out;
                    655:        }
                    656:        if ((skp = sshsk_open(provider_path)) == NULL) {
                    657:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
                    658:                goto out;
                    659:        }
1.35      djm       660: #ifdef DEBUG_SK
                    661:        fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n",
                    662:            __func__, key->sk_flags, key->sk_application);
                    663:        fprintf(stderr, "%s: sk_key_handle:\n", __func__);
                    664:        sshbuf_dump(key->sk_key_handle, stderr);
                    665: #endif
1.30      djm       666:        if ((r = skp->sk_sign(alg, data, datalen, key->sk_application,
1.1       djm       667:            sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
1.24      djm       668:            key->sk_flags, pin, opts, &resp)) != 0) {
1.33      djm       669:                debug_f("sk_sign failed with code %d", r);
1.23      djm       670:                r = skerr_to_ssherr(r);
1.1       djm       671:                goto out;
                    672:        }
1.9       markus    673:        /* Assemble signature */
                    674:        if ((sig = sshbuf_new()) == NULL) {
                    675:                r = SSH_ERR_ALLOC_FAIL;
                    676:                goto out;
                    677:        }
                    678:        if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
1.33      djm       679:                error_fr(r, "compose outer");
1.9       markus    680:                goto out;
                    681:        }
1.5       markus    682:        switch (type) {
1.15      naddy     683: #ifdef WITH_OPENSSL
1.5       markus    684:        case KEY_ECDSA_SK:
1.9       markus    685:                if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
1.5       markus    686:                        goto out;
                    687:                break;
1.15      naddy     688: #endif /* WITH_OPENSSL */
1.5       markus    689:        case KEY_ED25519_SK:
1.9       markus    690:                if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
1.5       markus    691:                        goto out;
                    692:                break;
                    693:        }
1.1       djm       694: #ifdef DEBUG_SK
1.5       markus    695:        fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
                    696:            __func__, resp->flags, resp->counter);
1.34      djm       697:        fprintf(stderr, "%s: data to sign:\n", __func__);
                    698:        sshbuf_dump_data(data, datalen, stderr);
1.1       djm       699:        fprintf(stderr, "%s: sigbuf:\n", __func__);
                    700:        sshbuf_dump(sig, stderr);
                    701: #endif
                    702:        if (sigp != NULL) {
                    703:                if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
                    704:                        r = SSH_ERR_ALLOC_FAIL;
                    705:                        goto out;
                    706:                }
                    707:                memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
                    708:        }
                    709:        if (lenp != NULL)
                    710:                *lenp = sshbuf_len(sig);
                    711:        /* success */
                    712:        r = 0;
                    713:  out:
1.24      djm       714:        sshsk_free_options(opts);
1.1       djm       715:        sshsk_free(skp);
                    716:        sshsk_free_sign_response(resp);
                    717:        sshbuf_free(sig);
                    718:        sshbuf_free(inner_sig);
                    719:        return r;
                    720: }
1.20      djm       721:
                    722: static void
                    723: sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
                    724: {
                    725:        size_t i;
                    726:
                    727:        if (nrks == 0 || rks == NULL)
                    728:                return;
                    729:        for (i = 0; i < nrks; i++) {
                    730:                free(rks[i]->application);
1.38      djm       731:                freezero(rks[i]->user_id, rks[i]->user_id_len);
1.20      djm       732:                freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
                    733:                freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
                    734:                freezero(rks[i]->key.signature, rks[i]->key.signature_len);
                    735:                freezero(rks[i]->key.attestation_cert,
                    736:                    rks[i]->key.attestation_cert_len);
                    737:                freezero(rks[i], sizeof(**rks));
                    738:        }
                    739:        free(rks);
                    740: }
                    741:
1.36      djm       742: static void
                    743: sshsk_free_resident_key(struct sshsk_resident_key *srk)
                    744: {
                    745:        if (srk == NULL)
                    746:                return;
                    747:        sshkey_free(srk->key);
                    748:        freezero(srk->user_id, srk->user_id_len);
                    749:        free(srk);
                    750: }
                    751:
                    752:
                    753: void
                    754: sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks)
                    755: {
                    756:        size_t i;
                    757:
                    758:        if (srks == NULL || nsrks == 0)
                    759:                return;
                    760:
                    761:        for (i = 0; i < nsrks; i++)
                    762:                sshsk_free_resident_key(srks[i]);
                    763:        free(srks);
                    764: }
                    765:
1.20      djm       766: int
1.24      djm       767: sshsk_load_resident(const char *provider_path, const char *device,
1.36      djm       768:     const char *pin, u_int flags, struct sshsk_resident_key ***srksp,
                    769:     size_t *nsrksp)
1.20      djm       770: {
                    771:        struct sshsk_provider *skp = NULL;
                    772:        int r = SSH_ERR_INTERNAL_ERROR;
                    773:        struct sk_resident_key **rks = NULL;
1.36      djm       774:        size_t i, nrks = 0, nsrks = 0;
                    775:        struct sshkey *key = NULL;
                    776:        struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp;
                    777:        uint8_t sk_flags;
1.24      djm       778:        struct sk_option **opts = NULL;
1.20      djm       779:
1.33      djm       780:        debug_f("provider \"%s\"%s", provider_path,
1.20      djm       781:            (pin != NULL && *pin != '\0') ? ", have-pin": "");
                    782:
1.36      djm       783:        if (srksp == NULL || nsrksp == NULL)
1.20      djm       784:                return SSH_ERR_INVALID_ARGUMENT;
1.36      djm       785:        *srksp = NULL;
                    786:        *nsrksp = 0;
1.20      djm       787:
1.24      djm       788:        if ((r = make_options(device, NULL, &opts)) != 0)
                    789:                goto out;
1.20      djm       790:        if ((skp = sshsk_open(provider_path)) == NULL) {
                    791:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
                    792:                goto out;
                    793:        }
1.24      djm       794:        if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) {
1.27      naddy     795:                error("Provider \"%s\" returned failure %d", provider_path, r);
1.23      djm       796:                r = skerr_to_ssherr(r);
1.20      djm       797:                goto out;
                    798:        }
                    799:        for (i = 0; i < nrks; i++) {
1.36      djm       800:                debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu",
                    801:                    i, rks[i]->slot, rks[i]->alg, rks[i]->application,
                    802:                    rks[i]->user_id_len);
1.20      djm       803:                /* XXX need better filter here */
                    804:                if (strncmp(rks[i]->application, "ssh:", 4) != 0)
                    805:                        continue;
                    806:                switch (rks[i]->alg) {
                    807:                case SSH_SK_ECDSA:
                    808:                case SSH_SK_ED25519:
                    809:                        break;
                    810:                default:
                    811:                        continue;
                    812:                }
1.36      djm       813:                sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY;
1.31      djm       814:                if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD))
1.36      djm       815:                        sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
1.20      djm       816:                if ((r = sshsk_key_from_response(rks[i]->alg,
1.36      djm       817:                    rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0)
                    818:                        goto out;
                    819:                if ((srk = calloc(1, sizeof(*srk))) == NULL) {
                    820:                        error_f("calloc failed");
                    821:                        r = SSH_ERR_ALLOC_FAIL;
1.20      djm       822:                        goto out;
1.36      djm       823:                }
                    824:                srk->key = key;
                    825:                key = NULL; /* transferred */
                    826:                if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) {
                    827:                        error_f("calloc failed");
                    828:                        r = SSH_ERR_ALLOC_FAIL;
                    829:                        goto out;
                    830:                }
                    831:                memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len);
                    832:                srk->user_id_len = rks[i]->user_id_len;
                    833:                if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
1.20      djm       834:                    sizeof(*tmp))) == NULL) {
1.33      djm       835:                        error_f("recallocarray failed");
1.20      djm       836:                        r = SSH_ERR_ALLOC_FAIL;
                    837:                        goto out;
                    838:                }
1.36      djm       839:                srks = tmp;
                    840:                srks[nsrks++] = srk;
                    841:                srk = NULL;
1.20      djm       842:                /* XXX synthesise comment */
                    843:        }
                    844:        /* success */
1.36      djm       845:        *srksp = srks;
                    846:        *nsrksp = nsrks;
                    847:        srks = NULL;
                    848:        nsrks = 0;
1.20      djm       849:        r = 0;
                    850:  out:
1.24      djm       851:        sshsk_free_options(opts);
1.20      djm       852:        sshsk_free(skp);
                    853:        sshsk_free_sk_resident_keys(rks, nrks);
                    854:        sshkey_free(key);
1.36      djm       855:        sshsk_free_resident_key(srk);
                    856:        sshsk_free_resident_keys(srks, nsrks);
1.20      djm       857:        return r;
                    858: }
                    859: