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

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

1.1     ! djm         1: /*
        !             2:  * Copyright (c) 2019 Markus Friedl
        !             3:  *
        !             4:  * Permission to use, copy, modify, and distribute this software for any
        !             5:  * purpose with or without fee is hereby granted, provided that the above
        !             6:  * copyright notice and this permission notice appear in all copies.
        !             7:  *
        !             8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !             9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            15:  */
        !            16:
        !            17: #include <stdint.h>
        !            18: #include <stdlib.h>
        !            19: #include <string.h>
        !            20: #include <stdio.h>
        !            21: #include <stddef.h>
        !            22: #include <stdarg.h>
        !            23:
        !            24: #include <openssl/opensslv.h>
        !            25: #include <openssl/crypto.h>
        !            26: #include <openssl/bn.h>
        !            27: #include <openssl/ec.h>
        !            28: #include <openssl/ecdsa.h>
        !            29:
        !            30: #include <fido.h>
        !            31:
        !            32: #ifndef SK_STANDALONE
        !            33: #include "log.h"
        !            34: #include "xmalloc.h"
        !            35: #endif
        !            36:
        !            37: /* #define SK_DEBUG 1 */
        !            38:
        !            39: #define MAX_FIDO_DEVICES       256
        !            40:
        !            41: /* Compatibility with OpenSSH 1.0.x */
        !            42: #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
        !            43: #define ECDSA_SIG_get0(sig, pr, ps) \
        !            44:        do { \
        !            45:                (*pr) = sig->r; \
        !            46:                (*ps) = sig->s; \
        !            47:        } while (0)
        !            48: #endif
        !            49:
        !            50: #define SK_VERSION_MAJOR       0x00020000 /* current API version */
        !            51:
        !            52: /* Flags */
        !            53: #define SK_USER_PRESENCE_REQD  0x01
        !            54:
        !            55: /* Algs */
        !            56: #define        SK_ECDSA                0x00
        !            57: #define        SK_ED25519              0x01
        !            58:
        !            59: struct sk_enroll_response {
        !            60:        uint8_t *public_key;
        !            61:        size_t public_key_len;
        !            62:        uint8_t *key_handle;
        !            63:        size_t key_handle_len;
        !            64:        uint8_t *signature;
        !            65:        size_t signature_len;
        !            66:        uint8_t *attestation_cert;
        !            67:        size_t attestation_cert_len;
        !            68: };
        !            69:
        !            70: struct sk_sign_response {
        !            71:        uint8_t flags;
        !            72:        uint32_t counter;
        !            73:        uint8_t *sig_r;
        !            74:        size_t sig_r_len;
        !            75:        uint8_t *sig_s;
        !            76:        size_t sig_s_len;
        !            77: };
        !            78:
        !            79: /* If building as part of OpenSSH, then rename exported functions */
        !            80: #if !defined(SK_STANDALONE)
        !            81: #define sk_api_version ssh_sk_api_version
        !            82: #define sk_enroll      ssh_sk_enroll
        !            83: #define sk_sign                ssh_sk_sign
        !            84: #endif
        !            85:
        !            86: /* Return the version of the middleware API */
        !            87: uint32_t sk_api_version(void);
        !            88:
        !            89: /* Enroll a U2F key (private key generation) */
        !            90: int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
        !            91:     const char *application, uint8_t flags,
        !            92:     struct sk_enroll_response **enroll_response);
        !            93:
        !            94: /* Sign a challenge */
        !            95: int sk_sign(int alg, const uint8_t *message, size_t message_len,
        !            96:     const char *application, const uint8_t *key_handle, size_t key_handle_len,
        !            97:     uint8_t flags, struct sk_sign_response **sign_response);
        !            98:
        !            99: static void skdebug(const char *func, const char *fmt, ...)
        !           100:     __attribute__((__format__ (printf, 2, 3)));
        !           101:
        !           102: static void
        !           103: skdebug(const char *func, const char *fmt, ...)
        !           104: {
        !           105: #if !defined(SK_STANDALONE)
        !           106:        char *msg;
        !           107:        va_list ap;
        !           108:
        !           109:        va_start(ap, fmt);
        !           110:        xvasprintf(&msg, fmt, ap);
        !           111:        va_end(ap);
        !           112:        debug("%s: %s", __func__, msg);
        !           113:        free(msg);
        !           114: #elif defined(SK_DEBUG)
        !           115:        va_list ap;
        !           116:
        !           117:        va_start(ap, fmt);
        !           118:        fprintf(stderr, "%s: ", func);
        !           119:        vfprintf(stderr, fmt, ap);
        !           120:        fputc('\n', stderr);
        !           121:        va_end(ap);
        !           122: #else
        !           123:        (void)func; /* XXX */
        !           124:        (void)fmt; /* XXX */
        !           125: #endif
        !           126: }
        !           127:
        !           128: uint32_t
        !           129: sk_api_version(void)
        !           130: {
        !           131:        return SK_VERSION_MAJOR;
        !           132: }
        !           133:
        !           134: /* Select the first identified FIDO device attached to the system */
        !           135: static char *
        !           136: pick_first_device(void)
        !           137: {
        !           138:        char *ret = NULL;
        !           139:        fido_dev_info_t *devlist = NULL;
        !           140:        size_t olen = 0;
        !           141:        int r;
        !           142:        const fido_dev_info_t *di;
        !           143:
        !           144:        if ((devlist = fido_dev_info_new(1)) == NULL) {
        !           145:                skdebug(__func__, "fido_dev_info_new failed");
        !           146:                goto out;
        !           147:        }
        !           148:        if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) {
        !           149:                skdebug(__func__, "fido_dev_info_manifest failed: %s",
        !           150:                    fido_strerr(r));
        !           151:                goto out;
        !           152:        }
        !           153:        if (olen != 1) {
        !           154:                skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen);
        !           155:                goto out;
        !           156:        }
        !           157:        di = fido_dev_info_ptr(devlist, 0);
        !           158:        if ((ret = strdup(fido_dev_info_path(di))) == NULL) {
        !           159:                skdebug(__func__, "fido_dev_info_path failed");
        !           160:                goto out;
        !           161:        }
        !           162:  out:
        !           163:        fido_dev_info_free(&devlist, 1);
        !           164:        return ret;
        !           165: }
        !           166:
        !           167: /* Check if the specified key handle exists on a given device. */
        !           168: static int
        !           169: try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len,
        !           170:     const char *application, const uint8_t *key_handle, size_t key_handle_len)
        !           171: {
        !           172:        fido_assert_t *assert = NULL;
        !           173:        int r = FIDO_ERR_INTERNAL;
        !           174:
        !           175:        if ((assert = fido_assert_new()) == NULL) {
        !           176:                skdebug(__func__, "fido_assert_new failed");
        !           177:                goto out;
        !           178:        }
        !           179:        if ((r = fido_assert_set_clientdata_hash(assert, message,
        !           180:            message_len)) != FIDO_OK) {
        !           181:                skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
        !           182:                    fido_strerr(r));
        !           183:                goto out;
        !           184:        }
        !           185:        if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
        !           186:                skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
        !           187:                goto out;
        !           188:        }
        !           189:        if ((r = fido_assert_allow_cred(assert, key_handle,
        !           190:            key_handle_len)) != FIDO_OK) {
        !           191:                skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
        !           192:                goto out;
        !           193:        }
        !           194:        if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
        !           195:                skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
        !           196:                goto out;
        !           197:        }
        !           198:        r = fido_dev_get_assert(dev, assert, NULL);
        !           199:        skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
        !           200:  out:
        !           201:        fido_assert_free(&assert);
        !           202:
        !           203:        return r != FIDO_OK ? -1 : 0;
        !           204: }
        !           205:
        !           206: /* Iterate over configured devices looking for a specific key handle */
        !           207: static fido_dev_t *
        !           208: find_device(const uint8_t *message, size_t message_len, const char *application,
        !           209:     const uint8_t *key_handle, size_t key_handle_len)
        !           210: {
        !           211:        fido_dev_info_t *devlist = NULL;
        !           212:        fido_dev_t *dev = NULL;
        !           213:        size_t devlist_len = 0;
        !           214:        const char *path;
        !           215:        int r;
        !           216:
        !           217:        if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
        !           218:                skdebug(__func__, "fido_dev_info_new failed");
        !           219:                goto out;
        !           220:        }
        !           221:        if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
        !           222:            &devlist_len)) != FIDO_OK) {
        !           223:                skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r));
        !           224:                goto out;
        !           225:        }
        !           226:
        !           227:        skdebug(__func__, "found %zu device(s)", devlist_len);
        !           228:
        !           229:        for (size_t i = 0; i < devlist_len; i++) {
        !           230:                const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
        !           231:
        !           232:                if (di == NULL) {
        !           233:                        skdebug(__func__, "fido_dev_info_ptr %zu failed", i);
        !           234:                        continue;
        !           235:                }
        !           236:                if ((path = fido_dev_info_path(di)) == NULL) {
        !           237:                        skdebug(__func__, "fido_dev_info_path %zu failed", i);
        !           238:                        continue;
        !           239:                }
        !           240:                skdebug(__func__, "trying device %zu: %s", i, path);
        !           241:                if ((dev = fido_dev_new()) == NULL) {
        !           242:                        skdebug(__func__, "fido_dev_new failed");
        !           243:                        continue;
        !           244:                }
        !           245:                if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
        !           246:                        skdebug(__func__, "fido_dev_open failed");
        !           247:                        fido_dev_free(&dev);
        !           248:                        continue;
        !           249:                }
        !           250:                if (try_device(dev, message, message_len, application,
        !           251:                    key_handle, key_handle_len) == 0) {
        !           252:                        skdebug(__func__, "found key");
        !           253:                        break;
        !           254:                }
        !           255:                fido_dev_close(dev);
        !           256:                fido_dev_free(&dev);
        !           257:        }
        !           258:
        !           259:  out:
        !           260:        if (devlist != NULL)
        !           261:                fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
        !           262:
        !           263:        return dev;
        !           264: }
        !           265:
        !           266: /*
        !           267:  * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
        !           268:  * but the API expects a SEC1 octet string.
        !           269:  */
        !           270: static int
        !           271: pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response)
        !           272: {
        !           273:        const uint8_t *ptr;
        !           274:        BIGNUM *x = NULL, *y = NULL;
        !           275:        EC_POINT *q = NULL;
        !           276:        EC_GROUP *g = NULL;
        !           277:        BN_CTX *bn_ctx = NULL;
        !           278:        int ret = -1;
        !           279:
        !           280:        response->public_key = NULL;
        !           281:        response->public_key_len = 0;
        !           282:
        !           283:        if ((bn_ctx = BN_CTX_new()) == NULL ||
        !           284:            (x = BN_CTX_get(bn_ctx)) == NULL ||
        !           285:            (y = BN_CTX_get(bn_ctx)) == NULL ||
        !           286:            (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
        !           287:            (q = EC_POINT_new(g)) == NULL) {
        !           288:                skdebug(__func__, "libcrypto setup failed");
        !           289:                goto out;
        !           290:        }
        !           291:        if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
        !           292:                skdebug(__func__, "fido_cred_pubkey_ptr failed");
        !           293:                goto out;
        !           294:        }
        !           295:        if (fido_cred_pubkey_len(cred) != 64) {
        !           296:                skdebug(__func__, "bad fido_cred_pubkey_len %zu",
        !           297:                    fido_cred_pubkey_len(cred));
        !           298:                goto out;
        !           299:        }
        !           300:
        !           301:        if (BN_bin2bn(ptr, 32, x) == NULL ||
        !           302:            BN_bin2bn(ptr + 32, 32, y) == NULL) {
        !           303:                skdebug(__func__, "BN_bin2bn failed");
        !           304:                goto out;
        !           305:        }
        !           306:        if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bn_ctx) != 1) {
        !           307:                skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
        !           308:                goto out;
        !           309:        }
        !           310:        response->public_key_len = EC_POINT_point2oct(g, q,
        !           311:            POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx);
        !           312:        if (response->public_key_len == 0 || response->public_key_len > 2048) {
        !           313:                skdebug(__func__, "bad pubkey length %zu",
        !           314:                    response->public_key_len);
        !           315:                goto out;
        !           316:        }
        !           317:        if ((response->public_key = malloc(response->public_key_len)) == NULL) {
        !           318:                skdebug(__func__, "malloc pubkey failed");
        !           319:                goto out;
        !           320:        }
        !           321:        if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
        !           322:            response->public_key, response->public_key_len, bn_ctx) == 0) {
        !           323:                skdebug(__func__, "EC_POINT_point2oct failed");
        !           324:                goto out;
        !           325:        }
        !           326:        /* success */
        !           327:        ret = 0;
        !           328:  out:
        !           329:        if (ret != 0 && response->public_key != NULL) {
        !           330:                memset(response->public_key, 0, response->public_key_len);
        !           331:                free(response->public_key);
        !           332:                response->public_key = NULL;
        !           333:        }
        !           334:        EC_POINT_free(q);
        !           335:        EC_GROUP_free(g);
        !           336:        BN_CTX_free(bn_ctx);
        !           337:        return ret;
        !           338: }
        !           339:
        !           340: static int
        !           341: pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response)
        !           342: {
        !           343:        const uint8_t *ptr;
        !           344:        size_t len;
        !           345:        int ret = -1;
        !           346:
        !           347:        response->public_key = NULL;
        !           348:        response->public_key_len = 0;
        !           349:
        !           350:        if ((len = fido_cred_pubkey_len(cred)) != 32) {
        !           351:                skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
        !           352:                goto out;
        !           353:        }
        !           354:        if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
        !           355:                skdebug(__func__, "fido_cred_pubkey_ptr failed");
        !           356:                goto out;
        !           357:        }
        !           358:        response->public_key_len = len;
        !           359:        if ((response->public_key = malloc(response->public_key_len)) == NULL) {
        !           360:                skdebug(__func__, "malloc pubkey failed");
        !           361:                goto out;
        !           362:        }
        !           363:        memcpy(response->public_key, ptr, len);
        !           364:        ret = 0;
        !           365:  out:
        !           366:        if (ret != 0)
        !           367:                free(response->public_key);
        !           368:        return ret;
        !           369: }
        !           370:
        !           371: static int
        !           372: pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response)
        !           373: {
        !           374:        switch(alg) {
        !           375:        case SK_ECDSA:
        !           376:                return pack_public_key_ecdsa(cred, response);
        !           377:        case SK_ED25519:
        !           378:                return pack_public_key_ed25519(cred, response);
        !           379:        default:
        !           380:                return -1;
        !           381:        }
        !           382: }
        !           383:
        !           384: int
        !           385: sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
        !           386:     const char *application, uint8_t flags,
        !           387:     struct sk_enroll_response **enroll_reponse)
        !           388: {
        !           389:        fido_cred_t *cred = NULL;
        !           390:        fido_dev_t *dev = NULL;
        !           391:        const uint8_t *ptr;
        !           392:        uint8_t user_id[32];
        !           393:        struct sk_enroll_response *response = NULL;
        !           394:        size_t len;
        !           395:        int cose_alg;
        !           396:        int ret = -1;
        !           397:        int r;
        !           398:        char *device = NULL;
        !           399:
        !           400:        (void)flags; /* XXX; unused */
        !           401: #ifdef SK_DEBUG
        !           402:        fido_init(FIDO_DEBUG);
        !           403: #endif
        !           404:        if (enroll_reponse == NULL) {
        !           405:                skdebug(__func__, "enroll_reponse == NULL");
        !           406:                goto out;
        !           407:        }
        !           408:        *enroll_reponse = NULL;
        !           409:        switch(alg) {
        !           410:        case SK_ECDSA:
        !           411:                cose_alg = COSE_ES256;
        !           412:                break;
        !           413:        case SK_ED25519:
        !           414:                cose_alg = COSE_EDDSA;
        !           415:                break;
        !           416:        default:
        !           417:                skdebug(__func__, "unsupported key type %d", alg);
        !           418:                goto out;
        !           419:        }
        !           420:        if ((device = pick_first_device()) == NULL) {
        !           421:                skdebug(__func__, "pick_first_device failed");
        !           422:                goto out;
        !           423:        }
        !           424:        skdebug(__func__, "using device %s", device);
        !           425:        if ((cred = fido_cred_new()) == NULL) {
        !           426:                skdebug(__func__, "fido_cred_new failed");
        !           427:                goto out;
        !           428:        }
        !           429:        memset(user_id, 0, sizeof(user_id));
        !           430:        if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
        !           431:                skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
        !           432:                goto out;
        !           433:        }
        !           434:        if ((r = fido_cred_set_clientdata_hash(cred, challenge,
        !           435:            challenge_len)) != FIDO_OK) {
        !           436:                skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
        !           437:                    fido_strerr(r));
        !           438:                goto out;
        !           439:        }
        !           440:        if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
        !           441:            "openssh", "openssh", NULL)) != FIDO_OK) {
        !           442:                skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
        !           443:                goto out;
        !           444:        }
        !           445:        if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
        !           446:                skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
        !           447:                goto out;
        !           448:        }
        !           449:        if ((dev = fido_dev_new()) == NULL) {
        !           450:                skdebug(__func__, "fido_dev_new failed");
        !           451:                goto out;
        !           452:        }
        !           453:        if ((r = fido_dev_open(dev, device)) != FIDO_OK) {
        !           454:                skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));
        !           455:                goto out;
        !           456:        }
        !           457:        if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) {
        !           458:                skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
        !           459:                goto out;
        !           460:        }
        !           461:        if (fido_cred_x5c_ptr(cred) != NULL) {
        !           462:                if ((r = fido_cred_verify(cred)) != FIDO_OK) {
        !           463:                        skdebug(__func__, "fido_cred_verify: %s",
        !           464:                            fido_strerr(r));
        !           465:                        goto out;
        !           466:                }
        !           467:        } else {
        !           468:                skdebug(__func__, "self-attested credential");
        !           469:                if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
        !           470:                        skdebug(__func__, "fido_cred_verify_self: %s",
        !           471:                            fido_strerr(r));
        !           472:                        goto out;
        !           473:                }
        !           474:        }
        !           475:        if ((response = calloc(1, sizeof(*response))) == NULL) {
        !           476:                skdebug(__func__, "calloc response failed");
        !           477:                goto out;
        !           478:        }
        !           479:        if (pack_public_key(alg, cred, response) != 0) {
        !           480:                skdebug(__func__, "pack_public_key failed");
        !           481:                goto out;
        !           482:        }
        !           483:        if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
        !           484:                len = fido_cred_id_len(cred);
        !           485:                if ((response->key_handle = calloc(1, len)) == NULL) {
        !           486:                        skdebug(__func__, "calloc key handle failed");
        !           487:                        goto out;
        !           488:                }
        !           489:                memcpy(response->key_handle, ptr, len);
        !           490:                response->key_handle_len = len;
        !           491:        }
        !           492:        if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
        !           493:                len = fido_cred_sig_len(cred);
        !           494:                if ((response->signature = calloc(1, len)) == NULL) {
        !           495:                        skdebug(__func__, "calloc signature failed");
        !           496:                        goto out;
        !           497:                }
        !           498:                memcpy(response->signature, ptr, len);
        !           499:                response->signature_len = len;
        !           500:        }
        !           501:        if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
        !           502:                len = fido_cred_x5c_len(cred);
        !           503:                if ((response->attestation_cert = calloc(1, len)) == NULL) {
        !           504:                        skdebug(__func__, "calloc attestation cert failed");
        !           505:                        goto out;
        !           506:                }
        !           507:                memcpy(response->attestation_cert, ptr, len);
        !           508:                response->attestation_cert_len = len;
        !           509:        }
        !           510:        *enroll_reponse = response;
        !           511:        response = NULL;
        !           512:        ret = 0;
        !           513:  out:
        !           514:        free(device);
        !           515:        if (response != NULL) {
        !           516:                free(response->public_key);
        !           517:                free(response->key_handle);
        !           518:                free(response->signature);
        !           519:                free(response->attestation_cert);
        !           520:                free(response);
        !           521:        }
        !           522:        if (dev != NULL) {
        !           523:                fido_dev_close(dev);
        !           524:                fido_dev_free(&dev);
        !           525:        }
        !           526:        if (cred != NULL) {
        !           527:                fido_cred_free(&cred);
        !           528:        }
        !           529:        return ret;
        !           530: }
        !           531:
        !           532: static int
        !           533: pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
        !           534: {
        !           535:        ECDSA_SIG *sig = NULL;
        !           536:        const BIGNUM *sig_r, *sig_s;
        !           537:        const unsigned char *cp;
        !           538:        size_t sig_len;
        !           539:        int ret = -1;
        !           540:
        !           541:        cp = fido_assert_sig_ptr(assert, 0);
        !           542:        sig_len = fido_assert_sig_len(assert, 0);
        !           543:        if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
        !           544:                skdebug(__func__, "d2i_ECDSA_SIG failed");
        !           545:                goto out;
        !           546:        }
        !           547:        ECDSA_SIG_get0(sig, &sig_r, &sig_s);
        !           548:        response->sig_r_len = BN_num_bytes(sig_r);
        !           549:        response->sig_s_len = BN_num_bytes(sig_s);
        !           550:        if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
        !           551:            (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
        !           552:                skdebug(__func__, "calloc signature failed");
        !           553:                goto out;
        !           554:        }
        !           555:        BN_bn2bin(sig_r, response->sig_r);
        !           556:        BN_bn2bin(sig_s, response->sig_s);
        !           557:        ret = 0;
        !           558:  out:
        !           559:        ECDSA_SIG_free(sig);
        !           560:        if (ret != 0) {
        !           561:                free(response->sig_r);
        !           562:                free(response->sig_s);
        !           563:                response->sig_r = NULL;
        !           564:                response->sig_s = NULL;
        !           565:        }
        !           566:        return ret;
        !           567: }
        !           568:
        !           569: static int
        !           570: pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
        !           571: {
        !           572:        const unsigned char *ptr;
        !           573:        size_t len;
        !           574:        int ret = -1;
        !           575:
        !           576:        ptr = fido_assert_sig_ptr(assert, 0);
        !           577:        len = fido_assert_sig_len(assert, 0);
        !           578:        if (len != 64) {
        !           579:                skdebug(__func__, "bad length %zu", len);
        !           580:                goto out;
        !           581:        }
        !           582:        response->sig_r_len = len;
        !           583:        if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
        !           584:                skdebug(__func__, "calloc signature failed");
        !           585:                goto out;
        !           586:        }
        !           587:        memcpy(response->sig_r, ptr, len);
        !           588:        ret = 0;
        !           589:  out:
        !           590:        if (ret != 0) {
        !           591:                free(response->sig_r);
        !           592:                response->sig_r = NULL;
        !           593:        }
        !           594:        return ret;
        !           595: }
        !           596:
        !           597: static int
        !           598: pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response)
        !           599: {
        !           600:        switch(alg) {
        !           601:        case SK_ECDSA:
        !           602:                return pack_sig_ecdsa(assert, response);
        !           603:        case SK_ED25519:
        !           604:                return pack_sig_ed25519(assert, response);
        !           605:        default:
        !           606:                return -1;
        !           607:        }
        !           608: }
        !           609:
        !           610: int
        !           611: sk_sign(int alg, const uint8_t *message, size_t message_len,
        !           612:     const char *application,
        !           613:     const uint8_t *key_handle, size_t key_handle_len,
        !           614:     uint8_t flags, struct sk_sign_response **sign_response)
        !           615: {
        !           616:        fido_assert_t *assert = NULL;
        !           617:        fido_dev_t *dev = NULL;
        !           618:        struct sk_sign_response *response = NULL;
        !           619:        int ret = -1;
        !           620:        int r;
        !           621:
        !           622: #ifdef SK_DEBUG
        !           623:        fido_init(FIDO_DEBUG);
        !           624: #endif
        !           625:
        !           626:        if (sign_response == NULL) {
        !           627:                skdebug(__func__, "sign_response == NULL");
        !           628:                goto out;
        !           629:        }
        !           630:        *sign_response = NULL;
        !           631:        if ((dev = find_device(message, message_len, application, key_handle,
        !           632:            key_handle_len)) == NULL) {
        !           633:                skdebug(__func__, "couldn't find device for key handle");
        !           634:                goto out;
        !           635:        }
        !           636:        if ((assert = fido_assert_new()) == NULL) {
        !           637:                skdebug(__func__, "fido_assert_new failed");
        !           638:                goto out;
        !           639:        }
        !           640:        if ((r = fido_assert_set_clientdata_hash(assert, message,
        !           641:            message_len)) != FIDO_OK) {
        !           642:                skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
        !           643:                    fido_strerr(r));
        !           644:                goto out;
        !           645:        }
        !           646:        if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
        !           647:                skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
        !           648:                goto out;
        !           649:        }
        !           650:        if ((r = fido_assert_allow_cred(assert, key_handle,
        !           651:            key_handle_len)) != FIDO_OK) {
        !           652:                skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
        !           653:                goto out;
        !           654:        }
        !           655:        if ((r = fido_assert_set_up(assert,
        !           656:            (flags & SK_USER_PRESENCE_REQD) ?
        !           657:            FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
        !           658:                skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
        !           659:                goto out;
        !           660:        }
        !           661:        if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
        !           662:                skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
        !           663:                goto out;
        !           664:        }
        !           665:        if ((response = calloc(1, sizeof(*response))) == NULL) {
        !           666:                skdebug(__func__, "calloc response failed");
        !           667:                goto out;
        !           668:        }
        !           669:        response->flags = fido_assert_flags(assert, 0);
        !           670:        response->counter = fido_assert_sigcount(assert, 0);
        !           671:        if (pack_sig(alg, assert, response) != 0) {
        !           672:                skdebug(__func__, "pack_sig failed");
        !           673:                goto out;
        !           674:        }
        !           675:        *sign_response = response;
        !           676:        response = NULL;
        !           677:        ret = 0;
        !           678:  out:
        !           679:        if (response != NULL) {
        !           680:                free(response->sig_r);
        !           681:                free(response->sig_s);
        !           682:                free(response);
        !           683:        }
        !           684:        if (dev != NULL) {
        !           685:                fido_dev_close(dev);
        !           686:                fido_dev_free(&dev);
        !           687:        }
        !           688:        if (assert != NULL) {
        !           689:                fido_assert_free(&assert);
        !           690:        }
        !           691:        return ret;
        !           692: }