[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.30

1.30    ! djm         1: /* $OpenBSD: ssh-sk.c,v 1.29 2020/03/06 18:25:48 markus 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) {
                    102:                error("%s: calloc failed", __func__);
                    103:                return NULL;
                    104:        }
                    105:        if ((ret->path = strdup(path)) == NULL) {
                    106:                error("%s: strdup failed", __func__);
                    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:        }
                    116:        if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
1.27      naddy     117:                error("Provider \"%s\" dlopen failed: %s", path, dlerror());
1.1       djm       118:                goto fail;
                    119:        }
                    120:        if ((ret->sk_api_version = dlsym(ret->dlhandle,
                    121:            "sk_api_version")) == NULL) {
1.27      naddy     122:                error("Provider \"%s\" dlsym(sk_api_version) failed: %s",
                    123:                    path, dlerror());
1.1       djm       124:                goto fail;
                    125:        }
                    126:        version = ret->sk_api_version();
                    127:        debug("%s: provider %s implements version 0x%08lx", __func__,
                    128:            ret->path, (u_long)version);
                    129:        if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
1.27      naddy     130:                error("Provider \"%s\" implements unsupported "
1.22      djm       131:                    "version 0x%08lx (supported: 0x%08lx)",
                    132:                    path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR);
1.1       djm       133:                goto fail;
                    134:        }
                    135:        if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
1.27      naddy     136:                error("Provider %s dlsym(sk_enroll) failed: %s",
                    137:                    path, dlerror());
1.1       djm       138:                goto fail;
                    139:        }
                    140:        if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
1.27      naddy     141:                error("Provider \"%s\" dlsym(sk_sign) failed: %s",
1.1       djm       142:                    path, dlerror());
                    143:                goto fail;
                    144:        }
1.20      djm       145:        if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle,
                    146:            "sk_load_resident_keys")) == NULL) {
1.27      naddy     147:                error("Provider \"%s\" dlsym(sk_load_resident_keys) "
                    148:                    "failed: %s", path, dlerror());
1.20      djm       149:                goto fail;
                    150:        }
1.1       djm       151:        /* success */
                    152:        return ret;
                    153: fail:
                    154:        sshsk_free(ret);
                    155:        return NULL;
                    156: }
                    157:
                    158: static void
                    159: sshsk_free_enroll_response(struct sk_enroll_response *r)
                    160: {
                    161:        if (r == NULL)
                    162:                return;
                    163:        freezero(r->key_handle, r->key_handle_len);
                    164:        freezero(r->public_key, r->public_key_len);
                    165:        freezero(r->signature, r->signature_len);
                    166:        freezero(r->attestation_cert, r->attestation_cert_len);
                    167:        freezero(r, sizeof(*r));
1.17      djm       168: }
1.1       djm       169:
                    170: static void
                    171: sshsk_free_sign_response(struct sk_sign_response *r)
                    172: {
                    173:        if (r == NULL)
                    174:                return;
                    175:        freezero(r->sig_r, r->sig_r_len);
                    176:        freezero(r->sig_s, r->sig_s_len);
                    177:        freezero(r, sizeof(*r));
1.17      djm       178: }
1.1       djm       179:
1.15      naddy     180: #ifdef WITH_OPENSSL
1.2       markus    181: /* Assemble key from response */
                    182: static int
                    183: sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
                    184: {
                    185:        struct sshkey *key = NULL;
                    186:        struct sshbuf *b = NULL;
                    187:        EC_POINT *q = NULL;
                    188:        int r;
                    189:
                    190:        *keyp = NULL;
                    191:        if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
                    192:                error("%s: sshkey_new failed", __func__);
                    193:                r = SSH_ERR_ALLOC_FAIL;
                    194:                goto out;
                    195:        }
                    196:        key->ecdsa_nid = NID_X9_62_prime256v1;
                    197:        if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
                    198:            (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
                    199:            (b = sshbuf_new()) == NULL) {
                    200:                error("%s: allocation failed", __func__);
                    201:                r = SSH_ERR_ALLOC_FAIL;
                    202:                goto out;
                    203:        }
                    204:        if ((r = sshbuf_put_string(b,
                    205:            resp->public_key, resp->public_key_len)) != 0) {
                    206:                error("%s: buffer error: %s", __func__, ssh_err(r));
                    207:                goto out;
                    208:        }
                    209:        if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
                    210:                error("%s: parse key: %s", __func__, ssh_err(r));
                    211:                r = SSH_ERR_INVALID_FORMAT;
                    212:                goto out;
                    213:        }
                    214:        if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
1.27      naddy     215:                error("Authenticator returned invalid ECDSA key");
1.2       markus    216:                r = SSH_ERR_KEY_INVALID_EC_VALUE;
                    217:                goto out;
                    218:        }
                    219:        if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
                    220:                /* XXX assume it is a allocation error */
                    221:                error("%s: allocation failed", __func__);
                    222:                r = SSH_ERR_ALLOC_FAIL;
                    223:                goto out;
                    224:        }
                    225:        /* success */
                    226:        *keyp = key;
                    227:        key = NULL; /* transferred */
                    228:        r = 0;
                    229:  out:
                    230:        EC_POINT_free(q);
                    231:        sshkey_free(key);
                    232:        sshbuf_free(b);
                    233:        return r;
                    234: }
1.15      naddy     235: #endif /* WITH_OPENSSL */
1.2       markus    236:
1.6       markus    237: static int
                    238: sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
                    239: {
                    240:        struct sshkey *key = NULL;
                    241:        int r;
                    242:
                    243:        *keyp = NULL;
                    244:        if (resp->public_key_len != ED25519_PK_SZ) {
                    245:                error("%s: invalid size: %zu", __func__, resp->public_key_len);
                    246:                r = SSH_ERR_INVALID_FORMAT;
                    247:                goto out;
                    248:        }
                    249:        if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
                    250:                error("%s: sshkey_new failed", __func__);
                    251:                r = SSH_ERR_ALLOC_FAIL;
                    252:                goto out;
                    253:        }
                    254:        if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
                    255:                error("%s: malloc failed", __func__);
                    256:                r = SSH_ERR_ALLOC_FAIL;
                    257:                goto out;
                    258:        }
                    259:        memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
                    260:        /* success */
                    261:        *keyp = key;
                    262:        key = NULL; /* transferred */
                    263:        r = 0;
                    264:  out:
                    265:        sshkey_free(key);
                    266:        return r;
                    267: }
                    268:
1.19      djm       269: static int
                    270: sshsk_key_from_response(int alg, const char *application, uint8_t flags,
                    271:     struct sk_enroll_response *resp, struct sshkey **keyp)
                    272: {
                    273:        struct sshkey *key = NULL;
                    274:        int r = SSH_ERR_INTERNAL_ERROR;
                    275:
                    276:        *keyp = NULL;
                    277:
                    278:        /* Check response validity */
1.20      djm       279:        if (resp->public_key == NULL || resp->key_handle == NULL) {
1.19      djm       280:                error("%s: sk_enroll response invalid", __func__);
                    281:                r = SSH_ERR_INVALID_FORMAT;
                    282:                goto out;
                    283:        }
                    284:        switch (alg) {
                    285: #ifdef WITH_OPENSSL
                    286:        case SSH_SK_ECDSA:
                    287:                if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
                    288:                        goto out;
                    289:                break;
                    290: #endif /* WITH_OPENSSL */
                    291:        case SSH_SK_ED25519:
                    292:                if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
                    293:                        goto out;
                    294:                break;
                    295:        default:
                    296:                error("%s: unsupported algorithm %d", __func__, alg);
                    297:                r = SSH_ERR_INVALID_ARGUMENT;
                    298:                goto out;
                    299:        }
                    300:        key->sk_flags = flags;
                    301:        if ((key->sk_key_handle = sshbuf_new()) == NULL ||
                    302:            (key->sk_reserved = sshbuf_new()) == NULL) {
                    303:                error("%s: allocation failed", __func__);
                    304:                r = SSH_ERR_ALLOC_FAIL;
                    305:                goto out;
                    306:        }
                    307:        if ((key->sk_application = strdup(application)) == NULL) {
                    308:                error("%s: strdup application failed", __func__);
                    309:                r = SSH_ERR_ALLOC_FAIL;
                    310:                goto out;
                    311:        }
                    312:        if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
                    313:            resp->key_handle_len)) != 0) {
                    314:                error("%s: buffer error: %s", __func__, ssh_err(r));
                    315:                goto out;
                    316:        }
                    317:        /* success */
                    318:        r = 0;
                    319:        *keyp = key;
                    320:        key = NULL;
                    321:  out:
                    322:        sshkey_free(key);
                    323:        return r;
                    324: }
                    325:
1.23      djm       326: static int
                    327: skerr_to_ssherr(int skerr)
                    328: {
                    329:        switch (skerr) {
                    330:        case SSH_SK_ERR_UNSUPPORTED:
                    331:                return SSH_ERR_FEATURE_UNSUPPORTED;
                    332:        case SSH_SK_ERR_PIN_REQUIRED:
                    333:                return SSH_ERR_KEY_WRONG_PASSPHRASE;
1.25      djm       334:        case SSH_SK_ERR_DEVICE_NOT_FOUND:
                    335:                return SSH_ERR_DEVICE_NOT_FOUND;
1.23      djm       336:        case SSH_SK_ERR_GENERAL:
                    337:        default:
                    338:                return SSH_ERR_INVALID_FORMAT;
                    339:        }
                    340: }
                    341:
1.24      djm       342: static void
                    343: sshsk_free_options(struct sk_option **opts)
                    344: {
                    345:        size_t i;
                    346:
                    347:        if (opts == NULL)
                    348:                return;
                    349:        for (i = 0; opts[i] != NULL; i++) {
                    350:                free(opts[i]->name);
                    351:                free(opts[i]->value);
                    352:                free(opts[i]);
                    353:        }
                    354:        free(opts);
                    355: }
                    356:
                    357: static int
                    358: sshsk_add_option(struct sk_option ***optsp, size_t *noptsp,
                    359:     const char *name, const char *value, uint8_t required)
                    360: {
                    361:        struct sk_option **opts = *optsp;
                    362:        size_t nopts = *noptsp;
                    363:
                    364:        if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */
                    365:            sizeof(*opts))) == NULL) {
                    366:                error("%s: array alloc failed", __func__);
                    367:                return SSH_ERR_ALLOC_FAIL;
                    368:        }
                    369:        *optsp = opts;
                    370:        *noptsp = nopts + 1;
                    371:        if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) {
                    372:                error("%s: alloc failed", __func__);
                    373:                return SSH_ERR_ALLOC_FAIL;
                    374:        }
                    375:        if ((opts[nopts]->name = strdup(name)) == NULL ||
                    376:            (opts[nopts]->value = strdup(value)) == NULL) {
                    377:                error("%s: alloc failed", __func__);
                    378:                return SSH_ERR_ALLOC_FAIL;
                    379:        }
                    380:        opts[nopts]->required = required;
                    381:        return 0;
                    382: }
                    383:
                    384: static int
                    385: make_options(const char *device, const char *user_id,
                    386:     struct sk_option ***optsp)
                    387: {
                    388:        struct sk_option **opts = NULL;
                    389:        size_t nopts = 0;
                    390:        int r, ret = SSH_ERR_INTERNAL_ERROR;
                    391:
                    392:        if (device != NULL &&
                    393:            (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) {
                    394:                ret = r;
                    395:                goto out;
                    396:        }
                    397:        if (user_id != NULL &&
                    398:            (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) {
                    399:                ret = r;
                    400:                goto out;
                    401:        }
                    402:        /* success */
                    403:        *optsp = opts;
                    404:        opts = NULL;
                    405:        nopts = 0;
                    406:        ret = 0;
                    407:  out:
                    408:        sshsk_free_options(opts);
                    409:        return ret;
                    410: }
                    411:
1.1       djm       412: int
1.24      djm       413: sshsk_enroll(int type, const char *provider_path, const char *device,
                    414:     const char *application, const char *userid, uint8_t flags,
                    415:     const char *pin, struct sshbuf *challenge_buf,
1.21      djm       416:     struct sshkey **keyp, struct sshbuf *attest)
1.1       djm       417: {
                    418:        struct sshsk_provider *skp = NULL;
                    419:        struct sshkey *key = NULL;
                    420:        u_char randchall[32];
                    421:        const u_char *challenge;
                    422:        size_t challenge_len;
                    423:        struct sk_enroll_response *resp = NULL;
1.24      djm       424:        struct sk_option **opts = NULL;
1.1       djm       425:        int r = SSH_ERR_INTERNAL_ERROR;
1.7       markus    426:        int alg;
1.1       djm       427:
1.24      djm       428:        debug("%s: provider \"%s\", device \"%s\", application \"%s\", "
                    429:            "userid \"%s\", flags 0x%02x, challenge len %zu%s", __func__,
                    430:            provider_path, device, application, userid, flags,
                    431:            challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf),
1.21      djm       432:            (pin != NULL && *pin != '\0') ? " with-pin" : "");
1.13      djm       433:
1.1       djm       434:        *keyp = NULL;
                    435:        if (attest)
                    436:                sshbuf_reset(attest);
1.24      djm       437:
                    438:        if ((r = make_options(device, userid, &opts)) != 0)
                    439:                goto out;
                    440:
1.6       markus    441:        switch (type) {
1.15      naddy     442: #ifdef WITH_OPENSSL
1.6       markus    443:        case KEY_ECDSA_SK:
1.7       markus    444:                alg = SSH_SK_ECDSA;
                    445:                break;
1.15      naddy     446: #endif /* WITH_OPENSSL */
1.6       markus    447:        case KEY_ED25519_SK:
1.7       markus    448:                alg = SSH_SK_ED25519;
1.6       markus    449:                break;
                    450:        default:
                    451:                error("%s: unsupported key type", __func__);
                    452:                r = SSH_ERR_INVALID_ARGUMENT;
                    453:                goto out;
                    454:        }
1.1       djm       455:        if (provider_path == NULL) {
                    456:                error("%s: missing provider", __func__);
                    457:                r = SSH_ERR_INVALID_ARGUMENT;
                    458:                goto out;
                    459:        }
                    460:        if (application == NULL || *application == '\0') {
                    461:                error("%s: missing application", __func__);
                    462:                r = SSH_ERR_INVALID_ARGUMENT;
                    463:                goto out;
                    464:        }
                    465:        if (challenge_buf == NULL) {
                    466:                debug("%s: using random challenge", __func__);
                    467:                arc4random_buf(randchall, sizeof(randchall));
                    468:                challenge = randchall;
                    469:                challenge_len = sizeof(randchall);
                    470:        } else if (sshbuf_len(challenge_buf) == 0) {
                    471:                error("Missing enrollment challenge");
                    472:                r = SSH_ERR_INVALID_ARGUMENT;
                    473:                goto out;
                    474:        } else {
                    475:                challenge = sshbuf_ptr(challenge_buf);
                    476:                challenge_len = sshbuf_len(challenge_buf);
                    477:                debug3("%s: using explicit challenge len=%zd",
                    478:                    __func__, challenge_len);
                    479:        }
                    480:        if ((skp = sshsk_open(provider_path)) == NULL) {
                    481:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
                    482:                goto out;
                    483:        }
                    484:        /* XXX validate flags? */
                    485:        /* enroll key */
1.7       markus    486:        if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
1.24      djm       487:            flags, pin, opts, &resp)) != 0) {
1.25      djm       488:                debug("%s: provider \"%s\" returned failure %d", __func__,
1.1       djm       489:                    provider_path, r);
1.23      djm       490:                r = skerr_to_ssherr(r);
1.1       djm       491:                goto out;
                    492:        }
1.19      djm       493:
                    494:        if ((r = sshsk_key_from_response(alg, application, flags,
                    495:            resp, &key)) != 0)
1.1       djm       496:                goto out;
1.19      djm       497:
1.1       djm       498:        /* Optionally fill in the attestation information */
                    499:        if (attest != NULL) {
1.26      djm       500:                if ((r = sshbuf_put_cstring(attest,
                    501:                    "ssh-sk-attest-v00")) != 0 ||
1.1       djm       502:                    (r = sshbuf_put_string(attest,
                    503:                    resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
                    504:                    (r = sshbuf_put_string(attest,
                    505:                    resp->signature, resp->signature_len)) != 0 ||
1.26      djm       506:                    (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */
                    507:                    (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) {
1.1       djm       508:                        error("%s: buffer error: %s", __func__, ssh_err(r));
                    509:                        goto out;
                    510:                }
                    511:        }
                    512:        /* success */
                    513:        *keyp = key;
                    514:        key = NULL; /* transferred */
                    515:        r = 0;
                    516:  out:
1.24      djm       517:        sshsk_free_options(opts);
1.1       djm       518:        sshsk_free(skp);
                    519:        sshkey_free(key);
                    520:        sshsk_free_enroll_response(resp);
                    521:        explicit_bzero(randchall, sizeof(randchall));
                    522:        return r;
                    523: }
                    524:
1.15      naddy     525: #ifdef WITH_OPENSSL
1.3       markus    526: static int
1.9       markus    527: sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.3       markus    528: {
                    529:        struct sshbuf *inner_sig = NULL;
                    530:        int r = SSH_ERR_INTERNAL_ERROR;
                    531:
1.8       markus    532:        /* Check response validity */
1.11      markus    533:        if (resp->sig_r == NULL || resp->sig_s == NULL) {
1.8       markus    534:                error("%s: sk_sign response invalid", __func__);
                    535:                r = SSH_ERR_INVALID_FORMAT;
                    536:                goto out;
                    537:        }
1.3       markus    538:        if ((inner_sig = sshbuf_new()) == NULL) {
                    539:                r = SSH_ERR_ALLOC_FAIL;
                    540:                goto out;
                    541:        }
1.9       markus    542:        /* Prepare and append inner signature object */
1.3       markus    543:        if ((r = sshbuf_put_bignum2_bytes(inner_sig,
                    544:            resp->sig_r, resp->sig_r_len)) != 0 ||
                    545:            (r = sshbuf_put_bignum2_bytes(inner_sig,
1.16      djm       546:            resp->sig_s, resp->sig_s_len)) != 0) {
1.3       markus    547:                debug("%s: buffer error: %s", __func__, ssh_err(r));
                    548:                goto out;
                    549:        }
1.16      djm       550:        if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
                    551:            (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
                    552:            (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.9       markus    553:                debug("%s: buffer error: %s", __func__, ssh_err(r));
                    554:                goto out;
                    555:        }
1.3       markus    556: #ifdef DEBUG_SK
                    557:        fprintf(stderr, "%s: sig_r:\n", __func__);
                    558:        sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
                    559:        fprintf(stderr, "%s: sig_s:\n", __func__);
                    560:        sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
1.9       markus    561:        fprintf(stderr, "%s: inner:\n", __func__);
                    562:        sshbuf_dump(inner_sig, stderr);
1.5       markus    563: #endif
                    564:        r = 0;
1.9       markus    565:  out:
1.5       markus    566:        sshbuf_free(inner_sig);
                    567:        return r;
                    568: }
1.15      naddy     569: #endif /* WITH_OPENSSL */
1.5       markus    570:
                    571: static int
1.9       markus    572: sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.5       markus    573: {
                    574:        int r = SSH_ERR_INTERNAL_ERROR;
                    575:
1.8       markus    576:        /* Check response validity */
                    577:        if (resp->sig_r == NULL) {
                    578:                error("%s: sk_sign response invalid", __func__);
                    579:                r = SSH_ERR_INVALID_FORMAT;
                    580:                goto out;
                    581:        }
1.9       markus    582:        if ((r = sshbuf_put_string(sig,
1.5       markus    583:            resp->sig_r, resp->sig_r_len)) != 0 ||
1.9       markus    584:            (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
                    585:            (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.5       markus    586:                debug("%s: buffer error: %s", __func__, ssh_err(r));
                    587:                goto out;
                    588:        }
                    589: #ifdef DEBUG_SK
                    590:        fprintf(stderr, "%s: sig_r:\n", __func__);
                    591:        sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
1.3       markus    592: #endif
                    593:        r = 0;
1.9       markus    594:  out:
1.29      markus    595:        return r;
1.3       markus    596: }
                    597:
1.1       djm       598: int
1.18      djm       599: sshsk_sign(const char *provider_path, struct sshkey *key,
1.1       djm       600:     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
1.21      djm       601:     u_int compat, const char *pin)
1.1       djm       602: {
                    603:        struct sshsk_provider *skp = NULL;
                    604:        int r = SSH_ERR_INTERNAL_ERROR;
1.7       markus    605:        int type, alg;
1.1       djm       606:        struct sk_sign_response *resp = NULL;
                    607:        struct sshbuf *inner_sig = NULL, *sig = NULL;
1.24      djm       608:        struct sk_option **opts = NULL;
1.13      djm       609:
1.21      djm       610:        debug("%s: provider \"%s\", key %s, flags 0x%02x%s", __func__,
                    611:            provider_path, sshkey_type(key), key->sk_flags,
                    612:            (pin != NULL && *pin != '\0') ? " with-pin" : "");
1.1       djm       613:
                    614:        if (sigp != NULL)
                    615:                *sigp = NULL;
                    616:        if (lenp != NULL)
                    617:                *lenp = 0;
1.5       markus    618:        type = sshkey_type_plain(key->type);
                    619:        switch (type) {
1.15      naddy     620: #ifdef WITH_OPENSSL
1.5       markus    621:        case KEY_ECDSA_SK:
1.7       markus    622:                alg = SSH_SK_ECDSA;
                    623:                break;
1.15      naddy     624: #endif /* WITH_OPENSSL */
1.5       markus    625:        case KEY_ED25519_SK:
1.7       markus    626:                alg = SSH_SK_ED25519;
1.5       markus    627:                break;
                    628:        default:
                    629:                return SSH_ERR_INVALID_ARGUMENT;
                    630:        }
1.1       djm       631:        if (provider_path == NULL ||
                    632:            key->sk_key_handle == NULL ||
                    633:            key->sk_application == NULL || *key->sk_application == '\0') {
                    634:                r = SSH_ERR_INVALID_ARGUMENT;
                    635:                goto out;
                    636:        }
                    637:        if ((skp = sshsk_open(provider_path)) == NULL) {
                    638:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
                    639:                goto out;
                    640:        }
                    641:
1.30    ! djm       642:        if ((r = skp->sk_sign(alg, data, datalen, key->sk_application,
1.1       djm       643:            sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
1.24      djm       644:            key->sk_flags, pin, opts, &resp)) != 0) {
1.1       djm       645:                debug("%s: sk_sign failed with code %d", __func__, r);
1.23      djm       646:                r = skerr_to_ssherr(r);
1.1       djm       647:                goto out;
                    648:        }
1.9       markus    649:        /* Assemble signature */
                    650:        if ((sig = sshbuf_new()) == NULL) {
                    651:                r = SSH_ERR_ALLOC_FAIL;
                    652:                goto out;
                    653:        }
                    654:        if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
                    655:                debug("%s: buffer error (outer): %s", __func__, ssh_err(r));
                    656:                goto out;
                    657:        }
1.5       markus    658:        switch (type) {
1.15      naddy     659: #ifdef WITH_OPENSSL
1.5       markus    660:        case KEY_ECDSA_SK:
1.9       markus    661:                if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
1.5       markus    662:                        goto out;
                    663:                break;
1.15      naddy     664: #endif /* WITH_OPENSSL */
1.5       markus    665:        case KEY_ED25519_SK:
1.9       markus    666:                if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
1.5       markus    667:                        goto out;
                    668:                break;
                    669:        }
1.1       djm       670: #ifdef DEBUG_SK
1.5       markus    671:        fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
                    672:            __func__, resp->flags, resp->counter);
1.1       djm       673:        fprintf(stderr, "%s: hashed message:\n", __func__);
                    674:        sshbuf_dump_data(message, sizeof(message), stderr);
                    675:        fprintf(stderr, "%s: sigbuf:\n", __func__);
                    676:        sshbuf_dump(sig, stderr);
                    677: #endif
                    678:        if (sigp != NULL) {
                    679:                if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
                    680:                        r = SSH_ERR_ALLOC_FAIL;
                    681:                        goto out;
                    682:                }
                    683:                memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
                    684:        }
                    685:        if (lenp != NULL)
                    686:                *lenp = sshbuf_len(sig);
                    687:        /* success */
                    688:        r = 0;
                    689:  out:
1.24      djm       690:        sshsk_free_options(opts);
1.1       djm       691:        sshsk_free(skp);
                    692:        sshsk_free_sign_response(resp);
                    693:        sshbuf_free(sig);
                    694:        sshbuf_free(inner_sig);
                    695:        return r;
                    696: }
1.20      djm       697:
                    698: static void
                    699: sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
                    700: {
                    701:        size_t i;
                    702:
                    703:        if (nrks == 0 || rks == NULL)
                    704:                return;
                    705:        for (i = 0; i < nrks; i++) {
                    706:                free(rks[i]->application);
                    707:                freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
                    708:                freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
                    709:                freezero(rks[i]->key.signature, rks[i]->key.signature_len);
                    710:                freezero(rks[i]->key.attestation_cert,
                    711:                    rks[i]->key.attestation_cert_len);
                    712:                freezero(rks[i], sizeof(**rks));
                    713:        }
                    714:        free(rks);
                    715: }
                    716:
                    717: int
1.24      djm       718: sshsk_load_resident(const char *provider_path, const char *device,
                    719:     const char *pin, struct sshkey ***keysp, size_t *nkeysp)
1.20      djm       720: {
                    721:        struct sshsk_provider *skp = NULL;
                    722:        int r = SSH_ERR_INTERNAL_ERROR;
                    723:        struct sk_resident_key **rks = NULL;
                    724:        size_t i, nrks = 0, nkeys = 0;
                    725:        struct sshkey *key = NULL, **keys = NULL, **tmp;
                    726:        uint8_t flags;
1.24      djm       727:        struct sk_option **opts = NULL;
1.20      djm       728:
                    729:        debug("%s: provider \"%s\"%s", __func__, provider_path,
                    730:            (pin != NULL && *pin != '\0') ? ", have-pin": "");
                    731:
                    732:        if (keysp == NULL || nkeysp == NULL)
                    733:                return SSH_ERR_INVALID_ARGUMENT;
                    734:        *keysp = NULL;
                    735:        *nkeysp = 0;
                    736:
1.24      djm       737:        if ((r = make_options(device, NULL, &opts)) != 0)
                    738:                goto out;
1.20      djm       739:        if ((skp = sshsk_open(provider_path)) == NULL) {
                    740:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
                    741:                goto out;
                    742:        }
1.24      djm       743:        if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) {
1.27      naddy     744:                error("Provider \"%s\" returned failure %d", provider_path, r);
1.23      djm       745:                r = skerr_to_ssherr(r);
1.20      djm       746:                goto out;
                    747:        }
                    748:        for (i = 0; i < nrks; i++) {
                    749:                debug3("%s: rk %zu: slot = %zu, alg = %d, application = \"%s\"",
                    750:                    __func__, i, rks[i]->slot, rks[i]->alg,
                    751:                    rks[i]->application);
                    752:                /* XXX need better filter here */
                    753:                if (strncmp(rks[i]->application, "ssh:", 4) != 0)
                    754:                        continue;
                    755:                switch (rks[i]->alg) {
                    756:                case SSH_SK_ECDSA:
                    757:                case SSH_SK_ED25519:
                    758:                        break;
                    759:                default:
                    760:                        continue;
                    761:                }
                    762:                /* XXX where to get flags? */
                    763:                flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY;
                    764:                if ((r = sshsk_key_from_response(rks[i]->alg,
                    765:                    rks[i]->application, flags, &rks[i]->key, &key)) != 0)
                    766:                        goto out;
                    767:                if ((tmp = recallocarray(keys, nkeys, nkeys + 1,
                    768:                    sizeof(*tmp))) == NULL) {
                    769:                        error("%s: recallocarray failed", __func__);
                    770:                        r = SSH_ERR_ALLOC_FAIL;
                    771:                        goto out;
                    772:                }
                    773:                keys = tmp;
                    774:                keys[nkeys++] = key;
                    775:                key = NULL;
                    776:                /* XXX synthesise comment */
                    777:        }
                    778:        /* success */
                    779:        *keysp = keys;
                    780:        *nkeysp = nkeys;
                    781:        keys = NULL;
                    782:        nkeys = 0;
                    783:        r = 0;
                    784:  out:
1.24      djm       785:        sshsk_free_options(opts);
1.20      djm       786:        sshsk_free(skp);
                    787:        sshsk_free_sk_resident_keys(rks, nrks);
                    788:        sshkey_free(key);
                    789:        if (nkeys != 0) {
                    790:                for (i = 0; i < nkeys; i++)
                    791:                        sshkey_free(keys[i]);
                    792:                free(keys);
                    793:        }
                    794:        return r;
                    795: }
                    796: