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

1.20    ! djm         1: /* $OpenBSD: ssh-sk.c,v 1.19 2019/12/30 09:20:36 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.1       djm        52:            struct sk_enroll_response **enroll_response);
                     53:
                     54:        /* Sign a challenge */
1.7       markus     55:        int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
1.1       djm        56:            const char *application,
                     57:            const uint8_t *key_handle, size_t key_handle_len,
                     58:            uint8_t flags, struct sk_sign_response **sign_response);
1.20    ! djm        59:
        !            60:        /* Enumerate resident keys */
        !            61:        int (*sk_load_resident_keys)(const char *pin,
        !            62:            struct sk_resident_key ***rks, size_t *nrks);
1.1       djm        63: };
                     64:
1.12      djm        65: /* Built-in version */
                     66: int ssh_sk_enroll(int alg, const uint8_t *challenge,
                     67:     size_t challenge_len, const char *application, uint8_t flags,
                     68:     struct sk_enroll_response **enroll_response);
                     69: int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
                     70:     const char *application,
                     71:     const uint8_t *key_handle, size_t key_handle_len,
                     72:     uint8_t flags, struct sk_sign_response **sign_response);
1.20    ! djm        73: int ssh_sk_load_resident_keys(const char *pin,
        !            74:     struct sk_resident_key ***rks, size_t *nrks);
1.12      djm        75:
1.1       djm        76: static void
                     77: sshsk_free(struct sshsk_provider *p)
                     78: {
                     79:        if (p == NULL)
                     80:                return;
                     81:        free(p->path);
                     82:        if (p->dlhandle != NULL)
                     83:                dlclose(p->dlhandle);
                     84:        free(p);
                     85: }
                     86:
                     87: static struct sshsk_provider *
                     88: sshsk_open(const char *path)
                     89: {
                     90:        struct sshsk_provider *ret = NULL;
                     91:        uint32_t version;
                     92:
                     93:        if ((ret = calloc(1, sizeof(*ret))) == NULL) {
                     94:                error("%s: calloc failed", __func__);
                     95:                return NULL;
                     96:        }
                     97:        if ((ret->path = strdup(path)) == NULL) {
                     98:                error("%s: strdup failed", __func__);
                     99:                goto fail;
1.12      djm       100:        }
                    101:        /* Skip the rest if we're using the linked in middleware */
                    102:        if (strcasecmp(ret->path, "internal") == 0) {
                    103:                ret->sk_enroll = ssh_sk_enroll;
                    104:                ret->sk_sign = ssh_sk_sign;
1.20    ! djm       105:                ret->sk_load_resident_keys = ssh_sk_load_resident_keys;
1.12      djm       106:                return ret;
1.1       djm       107:        }
                    108:        if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
                    109:                error("Security key provider %s dlopen failed: %s",
                    110:                    path, dlerror());
                    111:                goto fail;
                    112:        }
                    113:        if ((ret->sk_api_version = dlsym(ret->dlhandle,
                    114:            "sk_api_version")) == NULL) {
                    115:                error("Security key provider %s dlsym(sk_api_version) "
                    116:                    "failed: %s", path, dlerror());
                    117:                goto fail;
                    118:        }
                    119:        version = ret->sk_api_version();
                    120:        debug("%s: provider %s implements version 0x%08lx", __func__,
                    121:            ret->path, (u_long)version);
                    122:        if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
                    123:                error("Security key provider %s implements unsupported version "
                    124:                    "0x%08lx (supported: 0x%08lx)", path, (u_long)version,
                    125:                    (u_long)SSH_SK_VERSION_MAJOR);
                    126:                goto fail;
                    127:        }
                    128:        if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
                    129:                error("Security key  provider %s dlsym(sk_enroll) "
                    130:                    "failed: %s", path, dlerror());
                    131:                goto fail;
                    132:        }
                    133:        if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
                    134:                error("Security key provider %s dlsym(sk_sign) failed: %s",
                    135:                    path, dlerror());
                    136:                goto fail;
                    137:        }
1.20    ! djm       138:        if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle,
        !           139:            "sk_load_resident_keys")) == NULL) {
        !           140:                error("Security key provider %s dlsym(sk_load_resident_keys) "
        !           141:                    "failed: %s", path, dlerror());
        !           142:                goto fail;
        !           143:        }
1.1       djm       144:        /* success */
                    145:        return ret;
                    146: fail:
                    147:        sshsk_free(ret);
                    148:        return NULL;
                    149: }
                    150:
                    151: static void
                    152: sshsk_free_enroll_response(struct sk_enroll_response *r)
                    153: {
                    154:        if (r == NULL)
                    155:                return;
                    156:        freezero(r->key_handle, r->key_handle_len);
                    157:        freezero(r->public_key, r->public_key_len);
                    158:        freezero(r->signature, r->signature_len);
                    159:        freezero(r->attestation_cert, r->attestation_cert_len);
                    160:        freezero(r, sizeof(*r));
1.17      djm       161: }
1.1       djm       162:
                    163: static void
                    164: sshsk_free_sign_response(struct sk_sign_response *r)
                    165: {
                    166:        if (r == NULL)
                    167:                return;
                    168:        freezero(r->sig_r, r->sig_r_len);
                    169:        freezero(r->sig_s, r->sig_s_len);
                    170:        freezero(r, sizeof(*r));
1.17      djm       171: }
1.1       djm       172:
1.15      naddy     173: #ifdef WITH_OPENSSL
1.2       markus    174: /* Assemble key from response */
                    175: static int
                    176: sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
                    177: {
                    178:        struct sshkey *key = NULL;
                    179:        struct sshbuf *b = NULL;
                    180:        EC_POINT *q = NULL;
                    181:        int r;
                    182:
                    183:        *keyp = NULL;
                    184:        if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
                    185:                error("%s: sshkey_new failed", __func__);
                    186:                r = SSH_ERR_ALLOC_FAIL;
                    187:                goto out;
                    188:        }
                    189:        key->ecdsa_nid = NID_X9_62_prime256v1;
                    190:        if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
                    191:            (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
                    192:            (b = sshbuf_new()) == NULL) {
                    193:                error("%s: allocation failed", __func__);
                    194:                r = SSH_ERR_ALLOC_FAIL;
                    195:                goto out;
                    196:        }
                    197:        if ((r = sshbuf_put_string(b,
                    198:            resp->public_key, resp->public_key_len)) != 0) {
                    199:                error("%s: buffer error: %s", __func__, ssh_err(r));
                    200:                goto out;
                    201:        }
                    202:        if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
                    203:                error("%s: parse key: %s", __func__, ssh_err(r));
                    204:                r = SSH_ERR_INVALID_FORMAT;
                    205:                goto out;
                    206:        }
                    207:        if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
                    208:                error("Security key returned invalid ECDSA key");
                    209:                r = SSH_ERR_KEY_INVALID_EC_VALUE;
                    210:                goto out;
                    211:        }
                    212:        if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
                    213:                /* XXX assume it is a allocation error */
                    214:                error("%s: allocation failed", __func__);
                    215:                r = SSH_ERR_ALLOC_FAIL;
                    216:                goto out;
                    217:        }
                    218:        /* success */
                    219:        *keyp = key;
                    220:        key = NULL; /* transferred */
                    221:        r = 0;
                    222:  out:
                    223:        EC_POINT_free(q);
                    224:        sshkey_free(key);
                    225:        sshbuf_free(b);
                    226:        return r;
                    227: }
1.15      naddy     228: #endif /* WITH_OPENSSL */
1.2       markus    229:
1.6       markus    230: static int
                    231: sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
                    232: {
                    233:        struct sshkey *key = NULL;
                    234:        int r;
                    235:
                    236:        *keyp = NULL;
                    237:        if (resp->public_key_len != ED25519_PK_SZ) {
                    238:                error("%s: invalid size: %zu", __func__, resp->public_key_len);
                    239:                r = SSH_ERR_INVALID_FORMAT;
                    240:                goto out;
                    241:        }
                    242:        if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
                    243:                error("%s: sshkey_new failed", __func__);
                    244:                r = SSH_ERR_ALLOC_FAIL;
                    245:                goto out;
                    246:        }
                    247:        if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
                    248:                error("%s: malloc failed", __func__);
                    249:                r = SSH_ERR_ALLOC_FAIL;
                    250:                goto out;
                    251:        }
                    252:        memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
                    253:        /* success */
                    254:        *keyp = key;
                    255:        key = NULL; /* transferred */
                    256:        r = 0;
                    257:  out:
                    258:        sshkey_free(key);
                    259:        return r;
                    260: }
                    261:
1.19      djm       262: static int
                    263: sshsk_key_from_response(int alg, const char *application, uint8_t flags,
                    264:     struct sk_enroll_response *resp, struct sshkey **keyp)
                    265: {
                    266:        struct sshkey *key = NULL;
                    267:        int r = SSH_ERR_INTERNAL_ERROR;
                    268:
                    269:        *keyp = NULL;
                    270:
                    271:        /* Check response validity */
1.20    ! djm       272:        if (resp->public_key == NULL || resp->key_handle == NULL) {
1.19      djm       273:                error("%s: sk_enroll response invalid", __func__);
                    274:                r = SSH_ERR_INVALID_FORMAT;
                    275:                goto out;
                    276:        }
                    277:        switch (alg) {
                    278: #ifdef WITH_OPENSSL
                    279:        case SSH_SK_ECDSA:
                    280:                if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
                    281:                        goto out;
                    282:                break;
                    283: #endif /* WITH_OPENSSL */
                    284:        case SSH_SK_ED25519:
                    285:                if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
                    286:                        goto out;
                    287:                break;
                    288:        default:
                    289:                error("%s: unsupported algorithm %d", __func__, alg);
                    290:                r = SSH_ERR_INVALID_ARGUMENT;
                    291:                goto out;
                    292:        }
                    293:        key->sk_flags = flags;
                    294:        if ((key->sk_key_handle = sshbuf_new()) == NULL ||
                    295:            (key->sk_reserved = sshbuf_new()) == NULL) {
                    296:                error("%s: allocation failed", __func__);
                    297:                r = SSH_ERR_ALLOC_FAIL;
                    298:                goto out;
                    299:        }
                    300:        if ((key->sk_application = strdup(application)) == NULL) {
                    301:                error("%s: strdup application failed", __func__);
                    302:                r = SSH_ERR_ALLOC_FAIL;
                    303:                goto out;
                    304:        }
                    305:        if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
                    306:            resp->key_handle_len)) != 0) {
                    307:                error("%s: buffer error: %s", __func__, ssh_err(r));
                    308:                goto out;
                    309:        }
                    310:        /* success */
                    311:        r = 0;
                    312:        *keyp = key;
                    313:        key = NULL;
                    314:  out:
                    315:        sshkey_free(key);
                    316:        return r;
                    317: }
                    318:
1.1       djm       319: int
1.6       markus    320: sshsk_enroll(int type, const char *provider_path, const char *application,
1.1       djm       321:     uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp,
                    322:     struct sshbuf *attest)
                    323: {
                    324:        struct sshsk_provider *skp = NULL;
                    325:        struct sshkey *key = NULL;
                    326:        u_char randchall[32];
                    327:        const u_char *challenge;
                    328:        size_t challenge_len;
                    329:        struct sk_enroll_response *resp = NULL;
                    330:        int r = SSH_ERR_INTERNAL_ERROR;
1.7       markus    331:        int alg;
1.1       djm       332:
1.13      djm       333:        debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, "
                    334:            "challenge len %zu", __func__, provider_path, application,
                    335:            flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf));
                    336:
1.1       djm       337:        *keyp = NULL;
                    338:        if (attest)
                    339:                sshbuf_reset(attest);
1.6       markus    340:        switch (type) {
1.15      naddy     341: #ifdef WITH_OPENSSL
1.6       markus    342:        case KEY_ECDSA_SK:
1.7       markus    343:                alg = SSH_SK_ECDSA;
                    344:                break;
1.15      naddy     345: #endif /* WITH_OPENSSL */
1.6       markus    346:        case KEY_ED25519_SK:
1.7       markus    347:                alg = SSH_SK_ED25519;
1.6       markus    348:                break;
                    349:        default:
                    350:                error("%s: unsupported key type", __func__);
                    351:                r = SSH_ERR_INVALID_ARGUMENT;
                    352:                goto out;
                    353:        }
1.1       djm       354:        if (provider_path == NULL) {
                    355:                error("%s: missing provider", __func__);
                    356:                r = SSH_ERR_INVALID_ARGUMENT;
                    357:                goto out;
                    358:        }
                    359:        if (application == NULL || *application == '\0') {
                    360:                error("%s: missing application", __func__);
                    361:                r = SSH_ERR_INVALID_ARGUMENT;
                    362:                goto out;
                    363:        }
                    364:        if (challenge_buf == NULL) {
                    365:                debug("%s: using random challenge", __func__);
                    366:                arc4random_buf(randchall, sizeof(randchall));
                    367:                challenge = randchall;
                    368:                challenge_len = sizeof(randchall);
                    369:        } else if (sshbuf_len(challenge_buf) == 0) {
                    370:                error("Missing enrollment challenge");
                    371:                r = SSH_ERR_INVALID_ARGUMENT;
                    372:                goto out;
                    373:        } else {
                    374:                challenge = sshbuf_ptr(challenge_buf);
                    375:                challenge_len = sshbuf_len(challenge_buf);
                    376:                debug3("%s: using explicit challenge len=%zd",
                    377:                    __func__, challenge_len);
                    378:        }
                    379:        if ((skp = sshsk_open(provider_path)) == NULL) {
                    380:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
                    381:                goto out;
                    382:        }
                    383:        /* XXX validate flags? */
                    384:        /* enroll key */
1.7       markus    385:        if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
1.1       djm       386:            flags, &resp)) != 0) {
                    387:                error("Security key provider %s returned failure %d",
                    388:                    provider_path, r);
                    389:                r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */
                    390:                goto out;
                    391:        }
1.19      djm       392:
                    393:        if ((r = sshsk_key_from_response(alg, application, flags,
                    394:            resp, &key)) != 0)
1.1       djm       395:                goto out;
1.19      djm       396:
1.1       djm       397:        /* Optionally fill in the attestation information */
                    398:        if (attest != NULL) {
                    399:                if ((r = sshbuf_put_cstring(attest, "sk-attest-v00")) != 0 ||
                    400:                    (r = sshbuf_put_u32(attest, 1)) != 0 || /* XXX U2F ver */
                    401:                    (r = sshbuf_put_string(attest,
                    402:                    resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
                    403:                    (r = sshbuf_put_string(attest,
                    404:                    resp->signature, resp->signature_len)) != 0 ||
                    405:                    (r = sshbuf_put_u32(attest, flags)) != 0 || /* XXX right? */
                    406:                    (r = sshbuf_put_string(attest, NULL, 0)) != 0) {
                    407:                        error("%s: buffer error: %s", __func__, ssh_err(r));
                    408:                        goto out;
                    409:                }
                    410:        }
                    411:        /* success */
                    412:        *keyp = key;
                    413:        key = NULL; /* transferred */
                    414:        r = 0;
                    415:  out:
                    416:        sshsk_free(skp);
                    417:        sshkey_free(key);
                    418:        sshsk_free_enroll_response(resp);
                    419:        explicit_bzero(randchall, sizeof(randchall));
                    420:        return r;
                    421: }
                    422:
1.15      naddy     423: #ifdef WITH_OPENSSL
1.3       markus    424: static int
1.9       markus    425: sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.3       markus    426: {
                    427:        struct sshbuf *inner_sig = NULL;
                    428:        int r = SSH_ERR_INTERNAL_ERROR;
                    429:
1.8       markus    430:        /* Check response validity */
1.11      markus    431:        if (resp->sig_r == NULL || resp->sig_s == NULL) {
1.8       markus    432:                error("%s: sk_sign response invalid", __func__);
                    433:                r = SSH_ERR_INVALID_FORMAT;
                    434:                goto out;
                    435:        }
1.3       markus    436:        if ((inner_sig = sshbuf_new()) == NULL) {
                    437:                r = SSH_ERR_ALLOC_FAIL;
                    438:                goto out;
                    439:        }
1.9       markus    440:        /* Prepare and append inner signature object */
1.3       markus    441:        if ((r = sshbuf_put_bignum2_bytes(inner_sig,
                    442:            resp->sig_r, resp->sig_r_len)) != 0 ||
                    443:            (r = sshbuf_put_bignum2_bytes(inner_sig,
1.16      djm       444:            resp->sig_s, resp->sig_s_len)) != 0) {
1.3       markus    445:                debug("%s: buffer error: %s", __func__, ssh_err(r));
                    446:                goto out;
                    447:        }
1.16      djm       448:        if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
                    449:            (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
                    450:            (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.9       markus    451:                debug("%s: buffer error: %s", __func__, ssh_err(r));
                    452:                goto out;
                    453:        }
1.3       markus    454: #ifdef DEBUG_SK
                    455:        fprintf(stderr, "%s: sig_r:\n", __func__);
                    456:        sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
                    457:        fprintf(stderr, "%s: sig_s:\n", __func__);
                    458:        sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
1.9       markus    459:        fprintf(stderr, "%s: inner:\n", __func__);
                    460:        sshbuf_dump(inner_sig, stderr);
1.5       markus    461: #endif
                    462:        r = 0;
1.9       markus    463:  out:
1.5       markus    464:        sshbuf_free(inner_sig);
                    465:        return r;
                    466: }
1.15      naddy     467: #endif /* WITH_OPENSSL */
1.5       markus    468:
                    469: static int
1.9       markus    470: sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.5       markus    471: {
                    472:        int r = SSH_ERR_INTERNAL_ERROR;
                    473:
1.8       markus    474:        /* Check response validity */
                    475:        if (resp->sig_r == NULL) {
                    476:                error("%s: sk_sign response invalid", __func__);
                    477:                r = SSH_ERR_INVALID_FORMAT;
                    478:                goto out;
                    479:        }
1.9       markus    480:        if ((r = sshbuf_put_string(sig,
1.5       markus    481:            resp->sig_r, resp->sig_r_len)) != 0 ||
1.9       markus    482:            (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
                    483:            (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.5       markus    484:                debug("%s: buffer error: %s", __func__, ssh_err(r));
                    485:                goto out;
                    486:        }
                    487: #ifdef DEBUG_SK
                    488:        fprintf(stderr, "%s: sig_r:\n", __func__);
                    489:        sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
1.3       markus    490: #endif
                    491:        r = 0;
1.9       markus    492:  out:
                    493:        return 0;
1.3       markus    494: }
                    495:
1.1       djm       496: int
1.18      djm       497: sshsk_sign(const char *provider_path, struct sshkey *key,
1.1       djm       498:     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
                    499:     u_int compat)
                    500: {
                    501:        struct sshsk_provider *skp = NULL;
                    502:        int r = SSH_ERR_INTERNAL_ERROR;
1.7       markus    503:        int type, alg;
1.1       djm       504:        struct sk_sign_response *resp = NULL;
                    505:        struct sshbuf *inner_sig = NULL, *sig = NULL;
                    506:        uint8_t message[32];
1.13      djm       507:
1.14      djm       508:        debug("%s: provider \"%s\", key %s, flags 0x%02x", __func__,
1.13      djm       509:            provider_path, sshkey_type(key), key->sk_flags);
1.1       djm       510:
                    511:        if (sigp != NULL)
                    512:                *sigp = NULL;
                    513:        if (lenp != NULL)
                    514:                *lenp = 0;
1.5       markus    515:        type = sshkey_type_plain(key->type);
                    516:        switch (type) {
1.15      naddy     517: #ifdef WITH_OPENSSL
1.5       markus    518:        case KEY_ECDSA_SK:
1.7       markus    519:                alg = SSH_SK_ECDSA;
                    520:                break;
1.15      naddy     521: #endif /* WITH_OPENSSL */
1.5       markus    522:        case KEY_ED25519_SK:
1.7       markus    523:                alg = SSH_SK_ED25519;
1.5       markus    524:                break;
                    525:        default:
                    526:                return SSH_ERR_INVALID_ARGUMENT;
                    527:        }
1.1       djm       528:        if (provider_path == NULL ||
                    529:            key->sk_key_handle == NULL ||
                    530:            key->sk_application == NULL || *key->sk_application == '\0') {
                    531:                r = SSH_ERR_INVALID_ARGUMENT;
                    532:                goto out;
                    533:        }
                    534:        if ((skp = sshsk_open(provider_path)) == NULL) {
                    535:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
                    536:                goto out;
                    537:        }
                    538:
                    539:        /* hash data to be signed before it goes to the security key */
                    540:        if ((r = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen,
                    541:            message, sizeof(message))) != 0) {
                    542:                error("%s: hash application failed: %s", __func__, ssh_err(r));
                    543:                r = SSH_ERR_INTERNAL_ERROR;
                    544:                goto out;
                    545:        }
1.7       markus    546:        if ((r = skp->sk_sign(alg, message, sizeof(message),
1.1       djm       547:            key->sk_application,
                    548:            sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
                    549:            key->sk_flags, &resp)) != 0) {
                    550:                debug("%s: sk_sign failed with code %d", __func__, r);
                    551:                goto out;
                    552:        }
1.9       markus    553:        /* Assemble signature */
                    554:        if ((sig = sshbuf_new()) == NULL) {
                    555:                r = SSH_ERR_ALLOC_FAIL;
                    556:                goto out;
                    557:        }
                    558:        if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
                    559:                debug("%s: buffer error (outer): %s", __func__, ssh_err(r));
                    560:                goto out;
                    561:        }
1.5       markus    562:        switch (type) {
1.15      naddy     563: #ifdef WITH_OPENSSL
1.5       markus    564:        case KEY_ECDSA_SK:
1.9       markus    565:                if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
1.5       markus    566:                        goto out;
                    567:                break;
1.15      naddy     568: #endif /* WITH_OPENSSL */
1.5       markus    569:        case KEY_ED25519_SK:
1.9       markus    570:                if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
1.5       markus    571:                        goto out;
                    572:                break;
                    573:        }
1.1       djm       574: #ifdef DEBUG_SK
1.5       markus    575:        fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
                    576:            __func__, resp->flags, resp->counter);
1.1       djm       577:        fprintf(stderr, "%s: hashed message:\n", __func__);
                    578:        sshbuf_dump_data(message, sizeof(message), stderr);
                    579:        fprintf(stderr, "%s: sigbuf:\n", __func__);
                    580:        sshbuf_dump(sig, stderr);
                    581: #endif
                    582:        if (sigp != NULL) {
                    583:                if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
                    584:                        r = SSH_ERR_ALLOC_FAIL;
                    585:                        goto out;
                    586:                }
                    587:                memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
                    588:        }
                    589:        if (lenp != NULL)
                    590:                *lenp = sshbuf_len(sig);
                    591:        /* success */
                    592:        r = 0;
                    593:  out:
                    594:        explicit_bzero(message, sizeof(message));
                    595:        sshsk_free(skp);
                    596:        sshsk_free_sign_response(resp);
                    597:        sshbuf_free(sig);
                    598:        sshbuf_free(inner_sig);
                    599:        return r;
                    600: }
1.20    ! djm       601:
        !           602: static void
        !           603: sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks)
        !           604: {
        !           605:        size_t i;
        !           606:
        !           607:        if (nrks == 0 || rks == NULL)
        !           608:                return;
        !           609:        for (i = 0; i < nrks; i++) {
        !           610:                free(rks[i]->application);
        !           611:                freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
        !           612:                freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
        !           613:                freezero(rks[i]->key.signature, rks[i]->key.signature_len);
        !           614:                freezero(rks[i]->key.attestation_cert,
        !           615:                    rks[i]->key.attestation_cert_len);
        !           616:                freezero(rks[i], sizeof(**rks));
        !           617:        }
        !           618:        free(rks);
        !           619: }
        !           620:
        !           621: int
        !           622: sshsk_load_resident(const char *provider_path, const char *pin,
        !           623:     struct sshkey ***keysp, size_t *nkeysp)
        !           624: {
        !           625:        struct sshsk_provider *skp = NULL;
        !           626:        int r = SSH_ERR_INTERNAL_ERROR;
        !           627:        struct sk_resident_key **rks = NULL;
        !           628:        size_t i, nrks = 0, nkeys = 0;
        !           629:        struct sshkey *key = NULL, **keys = NULL, **tmp;
        !           630:        uint8_t flags;
        !           631:
        !           632:        debug("%s: provider \"%s\"%s", __func__, provider_path,
        !           633:            (pin != NULL && *pin != '\0') ? ", have-pin": "");
        !           634:
        !           635:        if (keysp == NULL || nkeysp == NULL)
        !           636:                return SSH_ERR_INVALID_ARGUMENT;
        !           637:        *keysp = NULL;
        !           638:        *nkeysp = 0;
        !           639:
        !           640:        if ((skp = sshsk_open(provider_path)) == NULL) {
        !           641:                r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
        !           642:                goto out;
        !           643:        }
        !           644:        if ((r = skp->sk_load_resident_keys(pin, &rks, &nrks)) != 0) {
        !           645:                error("Security key provider %s returned failure %d",
        !           646:                    provider_path, r);
        !           647:                r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */
        !           648:                goto out;
        !           649:        }
        !           650:        for (i = 0; i < nrks; i++) {
        !           651:                debug3("%s: rk %zu: slot = %zu, alg = %d, application = \"%s\"",
        !           652:                    __func__, i, rks[i]->slot, rks[i]->alg,
        !           653:                    rks[i]->application);
        !           654:                /* XXX need better filter here */
        !           655:                if (strncmp(rks[i]->application, "ssh:", 4) != 0)
        !           656:                        continue;
        !           657:                switch (rks[i]->alg) {
        !           658:                case SSH_SK_ECDSA:
        !           659:                case SSH_SK_ED25519:
        !           660:                        break;
        !           661:                default:
        !           662:                        continue;
        !           663:                }
        !           664:                /* XXX where to get flags? */
        !           665:                flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY;
        !           666:                if ((r = sshsk_key_from_response(rks[i]->alg,
        !           667:                    rks[i]->application, flags, &rks[i]->key, &key)) != 0)
        !           668:                        goto out;
        !           669:                if ((tmp = recallocarray(keys, nkeys, nkeys + 1,
        !           670:                    sizeof(*tmp))) == NULL) {
        !           671:                        error("%s: recallocarray failed", __func__);
        !           672:                        r = SSH_ERR_ALLOC_FAIL;
        !           673:                        goto out;
        !           674:                }
        !           675:                keys = tmp;
        !           676:                keys[nkeys++] = key;
        !           677:                key = NULL;
        !           678:                /* XXX synthesise comment */
        !           679:        }
        !           680:        /* success */
        !           681:        *keysp = keys;
        !           682:        *nkeysp = nkeys;
        !           683:        keys = NULL;
        !           684:        nkeys = 0;
        !           685:        r = 0;
        !           686:  out:
        !           687:        sshsk_free(skp);
        !           688:        sshsk_free_sk_resident_keys(rks, nrks);
        !           689:        sshkey_free(key);
        !           690:        if (nkeys != 0) {
        !           691:                for (i = 0; i < nkeys; i++)
        !           692:                        sshkey_free(keys[i]);
        !           693:                free(keys);
        !           694:        }
        !           695:        return r;
        !           696: }
        !           697: