1094c80e0Sdjm /* 2094c80e0Sdjm * Copyright (c) 2019 Markus Friedl 3094c80e0Sdjm * 4094c80e0Sdjm * Permission to use, copy, modify, and distribute this software for any 5094c80e0Sdjm * purpose with or without fee is hereby granted, provided that the above 6094c80e0Sdjm * copyright notice and this permission notice appear in all copies. 7094c80e0Sdjm * 8094c80e0Sdjm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9094c80e0Sdjm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10094c80e0Sdjm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11094c80e0Sdjm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12094c80e0Sdjm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13094c80e0Sdjm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14094c80e0Sdjm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15094c80e0Sdjm */ 16094c80e0Sdjm 17094c80e0Sdjm #include <stdint.h> 18094c80e0Sdjm #include <stdlib.h> 19094c80e0Sdjm #include <string.h> 20094c80e0Sdjm #include <stdio.h> 21094c80e0Sdjm #include <stddef.h> 22094c80e0Sdjm #include <stdarg.h> 23094c80e0Sdjm 24094c80e0Sdjm #include <openssl/opensslv.h> 25094c80e0Sdjm #include <openssl/crypto.h> 26094c80e0Sdjm #include <openssl/bn.h> 27094c80e0Sdjm #include <openssl/ec.h> 28094c80e0Sdjm #include <openssl/ecdsa.h> 29094c80e0Sdjm 30094c80e0Sdjm #include <fido.h> 31094c80e0Sdjm 32094c80e0Sdjm #ifndef SK_STANDALONE 33094c80e0Sdjm #include "log.h" 34094c80e0Sdjm #include "xmalloc.h" 35094c80e0Sdjm #endif 36094c80e0Sdjm 37094c80e0Sdjm /* #define SK_DEBUG 1 */ 38094c80e0Sdjm 39094c80e0Sdjm #define MAX_FIDO_DEVICES 256 40094c80e0Sdjm 41094c80e0Sdjm /* Compatibility with OpenSSH 1.0.x */ 42094c80e0Sdjm #if (OPENSSL_VERSION_NUMBER < 0x10100000L) 43094c80e0Sdjm #define ECDSA_SIG_get0(sig, pr, ps) \ 44094c80e0Sdjm do { \ 45094c80e0Sdjm (*pr) = sig->r; \ 46094c80e0Sdjm (*ps) = sig->s; \ 47094c80e0Sdjm } while (0) 48094c80e0Sdjm #endif 49094c80e0Sdjm 50094c80e0Sdjm #define SK_VERSION_MAJOR 0x00020000 /* current API version */ 51094c80e0Sdjm 52094c80e0Sdjm /* Flags */ 53094c80e0Sdjm #define SK_USER_PRESENCE_REQD 0x01 54094c80e0Sdjm 55094c80e0Sdjm /* Algs */ 56094c80e0Sdjm #define SK_ECDSA 0x00 57094c80e0Sdjm #define SK_ED25519 0x01 58094c80e0Sdjm 59094c80e0Sdjm struct sk_enroll_response { 60094c80e0Sdjm uint8_t *public_key; 61094c80e0Sdjm size_t public_key_len; 62094c80e0Sdjm uint8_t *key_handle; 63094c80e0Sdjm size_t key_handle_len; 64094c80e0Sdjm uint8_t *signature; 65094c80e0Sdjm size_t signature_len; 66094c80e0Sdjm uint8_t *attestation_cert; 67094c80e0Sdjm size_t attestation_cert_len; 68094c80e0Sdjm }; 69094c80e0Sdjm 70094c80e0Sdjm struct sk_sign_response { 71094c80e0Sdjm uint8_t flags; 72094c80e0Sdjm uint32_t counter; 73094c80e0Sdjm uint8_t *sig_r; 74094c80e0Sdjm size_t sig_r_len; 75094c80e0Sdjm uint8_t *sig_s; 76094c80e0Sdjm size_t sig_s_len; 77094c80e0Sdjm }; 78094c80e0Sdjm 79094c80e0Sdjm /* If building as part of OpenSSH, then rename exported functions */ 80094c80e0Sdjm #if !defined(SK_STANDALONE) 81094c80e0Sdjm #define sk_api_version ssh_sk_api_version 82094c80e0Sdjm #define sk_enroll ssh_sk_enroll 83094c80e0Sdjm #define sk_sign ssh_sk_sign 84094c80e0Sdjm #endif 85094c80e0Sdjm 86094c80e0Sdjm /* Return the version of the middleware API */ 87094c80e0Sdjm uint32_t sk_api_version(void); 88094c80e0Sdjm 89094c80e0Sdjm /* Enroll a U2F key (private key generation) */ 90094c80e0Sdjm int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, 91094c80e0Sdjm const char *application, uint8_t flags, 92094c80e0Sdjm struct sk_enroll_response **enroll_response); 93094c80e0Sdjm 94094c80e0Sdjm /* Sign a challenge */ 95094c80e0Sdjm int sk_sign(int alg, const uint8_t *message, size_t message_len, 96094c80e0Sdjm const char *application, const uint8_t *key_handle, size_t key_handle_len, 97094c80e0Sdjm uint8_t flags, struct sk_sign_response **sign_response); 98094c80e0Sdjm 99094c80e0Sdjm static void skdebug(const char *func, const char *fmt, ...) 100094c80e0Sdjm __attribute__((__format__ (printf, 2, 3))); 101094c80e0Sdjm 102094c80e0Sdjm static void 103094c80e0Sdjm skdebug(const char *func, const char *fmt, ...) 104094c80e0Sdjm { 105094c80e0Sdjm #if !defined(SK_STANDALONE) 106094c80e0Sdjm char *msg; 107094c80e0Sdjm va_list ap; 108094c80e0Sdjm 109094c80e0Sdjm va_start(ap, fmt); 110094c80e0Sdjm xvasprintf(&msg, fmt, ap); 111094c80e0Sdjm va_end(ap); 11259959935Sdjm debug("%s: %s", func, msg); 113094c80e0Sdjm free(msg); 114094c80e0Sdjm #elif defined(SK_DEBUG) 115094c80e0Sdjm va_list ap; 116094c80e0Sdjm 117094c80e0Sdjm va_start(ap, fmt); 118094c80e0Sdjm fprintf(stderr, "%s: ", func); 119094c80e0Sdjm vfprintf(stderr, fmt, ap); 120094c80e0Sdjm fputc('\n', stderr); 121094c80e0Sdjm va_end(ap); 122094c80e0Sdjm #else 123094c80e0Sdjm (void)func; /* XXX */ 124094c80e0Sdjm (void)fmt; /* XXX */ 125094c80e0Sdjm #endif 126094c80e0Sdjm } 127094c80e0Sdjm 128094c80e0Sdjm uint32_t 129094c80e0Sdjm sk_api_version(void) 130094c80e0Sdjm { 131094c80e0Sdjm return SK_VERSION_MAJOR; 132094c80e0Sdjm } 133094c80e0Sdjm 134094c80e0Sdjm /* Select the first identified FIDO device attached to the system */ 135094c80e0Sdjm static char * 136094c80e0Sdjm pick_first_device(void) 137094c80e0Sdjm { 138094c80e0Sdjm char *ret = NULL; 139094c80e0Sdjm fido_dev_info_t *devlist = NULL; 140094c80e0Sdjm size_t olen = 0; 141094c80e0Sdjm int r; 142094c80e0Sdjm const fido_dev_info_t *di; 143094c80e0Sdjm 144094c80e0Sdjm if ((devlist = fido_dev_info_new(1)) == NULL) { 145094c80e0Sdjm skdebug(__func__, "fido_dev_info_new failed"); 146094c80e0Sdjm goto out; 147094c80e0Sdjm } 148094c80e0Sdjm if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) { 149094c80e0Sdjm skdebug(__func__, "fido_dev_info_manifest failed: %s", 150094c80e0Sdjm fido_strerr(r)); 151094c80e0Sdjm goto out; 152094c80e0Sdjm } 153094c80e0Sdjm if (olen != 1) { 154094c80e0Sdjm skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen); 155094c80e0Sdjm goto out; 156094c80e0Sdjm } 157094c80e0Sdjm di = fido_dev_info_ptr(devlist, 0); 158094c80e0Sdjm if ((ret = strdup(fido_dev_info_path(di))) == NULL) { 159094c80e0Sdjm skdebug(__func__, "fido_dev_info_path failed"); 160094c80e0Sdjm goto out; 161094c80e0Sdjm } 162094c80e0Sdjm out: 163094c80e0Sdjm fido_dev_info_free(&devlist, 1); 164094c80e0Sdjm return ret; 165094c80e0Sdjm } 166094c80e0Sdjm 167094c80e0Sdjm /* Check if the specified key handle exists on a given device. */ 168094c80e0Sdjm static int 169094c80e0Sdjm try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, 170094c80e0Sdjm const char *application, const uint8_t *key_handle, size_t key_handle_len) 171094c80e0Sdjm { 172094c80e0Sdjm fido_assert_t *assert = NULL; 173094c80e0Sdjm int r = FIDO_ERR_INTERNAL; 174094c80e0Sdjm 175094c80e0Sdjm if ((assert = fido_assert_new()) == NULL) { 176094c80e0Sdjm skdebug(__func__, "fido_assert_new failed"); 177094c80e0Sdjm goto out; 178094c80e0Sdjm } 179094c80e0Sdjm if ((r = fido_assert_set_clientdata_hash(assert, message, 180094c80e0Sdjm message_len)) != FIDO_OK) { 181094c80e0Sdjm skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 182094c80e0Sdjm fido_strerr(r)); 183094c80e0Sdjm goto out; 184094c80e0Sdjm } 185094c80e0Sdjm if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 186094c80e0Sdjm skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 187094c80e0Sdjm goto out; 188094c80e0Sdjm } 189094c80e0Sdjm if ((r = fido_assert_allow_cred(assert, key_handle, 190094c80e0Sdjm key_handle_len)) != FIDO_OK) { 191094c80e0Sdjm skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 192094c80e0Sdjm goto out; 193094c80e0Sdjm } 194094c80e0Sdjm if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { 195094c80e0Sdjm skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); 196094c80e0Sdjm goto out; 197094c80e0Sdjm } 198094c80e0Sdjm r = fido_dev_get_assert(dev, assert, NULL); 199094c80e0Sdjm skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 200*7cf8e58dSdjm if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { 201*7cf8e58dSdjm /* U2F tokens may return this */ 202*7cf8e58dSdjm r = FIDO_OK; 203*7cf8e58dSdjm } 204094c80e0Sdjm out: 205094c80e0Sdjm fido_assert_free(&assert); 206094c80e0Sdjm 207094c80e0Sdjm return r != FIDO_OK ? -1 : 0; 208094c80e0Sdjm } 209094c80e0Sdjm 210094c80e0Sdjm /* Iterate over configured devices looking for a specific key handle */ 211094c80e0Sdjm static fido_dev_t * 212094c80e0Sdjm find_device(const uint8_t *message, size_t message_len, const char *application, 213094c80e0Sdjm const uint8_t *key_handle, size_t key_handle_len) 214094c80e0Sdjm { 215094c80e0Sdjm fido_dev_info_t *devlist = NULL; 216094c80e0Sdjm fido_dev_t *dev = NULL; 217094c80e0Sdjm size_t devlist_len = 0; 218094c80e0Sdjm const char *path; 219094c80e0Sdjm int r; 220094c80e0Sdjm 221094c80e0Sdjm if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { 222094c80e0Sdjm skdebug(__func__, "fido_dev_info_new failed"); 223094c80e0Sdjm goto out; 224094c80e0Sdjm } 225094c80e0Sdjm if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, 226094c80e0Sdjm &devlist_len)) != FIDO_OK) { 227094c80e0Sdjm skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r)); 228094c80e0Sdjm goto out; 229094c80e0Sdjm } 230094c80e0Sdjm 231094c80e0Sdjm skdebug(__func__, "found %zu device(s)", devlist_len); 232094c80e0Sdjm 233094c80e0Sdjm for (size_t i = 0; i < devlist_len; i++) { 234094c80e0Sdjm const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); 235094c80e0Sdjm 236094c80e0Sdjm if (di == NULL) { 237094c80e0Sdjm skdebug(__func__, "fido_dev_info_ptr %zu failed", i); 238094c80e0Sdjm continue; 239094c80e0Sdjm } 240094c80e0Sdjm if ((path = fido_dev_info_path(di)) == NULL) { 241094c80e0Sdjm skdebug(__func__, "fido_dev_info_path %zu failed", i); 242094c80e0Sdjm continue; 243094c80e0Sdjm } 244094c80e0Sdjm skdebug(__func__, "trying device %zu: %s", i, path); 245094c80e0Sdjm if ((dev = fido_dev_new()) == NULL) { 246094c80e0Sdjm skdebug(__func__, "fido_dev_new failed"); 247094c80e0Sdjm continue; 248094c80e0Sdjm } 249094c80e0Sdjm if ((r = fido_dev_open(dev, path)) != FIDO_OK) { 250094c80e0Sdjm skdebug(__func__, "fido_dev_open failed"); 251094c80e0Sdjm fido_dev_free(&dev); 252094c80e0Sdjm continue; 253094c80e0Sdjm } 254094c80e0Sdjm if (try_device(dev, message, message_len, application, 255094c80e0Sdjm key_handle, key_handle_len) == 0) { 256094c80e0Sdjm skdebug(__func__, "found key"); 257094c80e0Sdjm break; 258094c80e0Sdjm } 259094c80e0Sdjm fido_dev_close(dev); 260094c80e0Sdjm fido_dev_free(&dev); 261094c80e0Sdjm } 262094c80e0Sdjm 263094c80e0Sdjm out: 264094c80e0Sdjm if (devlist != NULL) 265094c80e0Sdjm fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); 266094c80e0Sdjm 267094c80e0Sdjm return dev; 268094c80e0Sdjm } 269094c80e0Sdjm 270094c80e0Sdjm /* 271094c80e0Sdjm * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, 272094c80e0Sdjm * but the API expects a SEC1 octet string. 273094c80e0Sdjm */ 274094c80e0Sdjm static int 275094c80e0Sdjm pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response) 276094c80e0Sdjm { 277094c80e0Sdjm const uint8_t *ptr; 278094c80e0Sdjm BIGNUM *x = NULL, *y = NULL; 279094c80e0Sdjm EC_POINT *q = NULL; 280094c80e0Sdjm EC_GROUP *g = NULL; 281094c80e0Sdjm BN_CTX *bn_ctx = NULL; 282094c80e0Sdjm int ret = -1; 283094c80e0Sdjm 284094c80e0Sdjm response->public_key = NULL; 285094c80e0Sdjm response->public_key_len = 0; 286094c80e0Sdjm 287094c80e0Sdjm if ((bn_ctx = BN_CTX_new()) == NULL || 288094c80e0Sdjm (x = BN_CTX_get(bn_ctx)) == NULL || 289094c80e0Sdjm (y = BN_CTX_get(bn_ctx)) == NULL || 290094c80e0Sdjm (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || 291094c80e0Sdjm (q = EC_POINT_new(g)) == NULL) { 292094c80e0Sdjm skdebug(__func__, "libcrypto setup failed"); 293094c80e0Sdjm goto out; 294094c80e0Sdjm } 295094c80e0Sdjm if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 296094c80e0Sdjm skdebug(__func__, "fido_cred_pubkey_ptr failed"); 297094c80e0Sdjm goto out; 298094c80e0Sdjm } 299094c80e0Sdjm if (fido_cred_pubkey_len(cred) != 64) { 300094c80e0Sdjm skdebug(__func__, "bad fido_cred_pubkey_len %zu", 301094c80e0Sdjm fido_cred_pubkey_len(cred)); 302094c80e0Sdjm goto out; 303094c80e0Sdjm } 304094c80e0Sdjm 305094c80e0Sdjm if (BN_bin2bn(ptr, 32, x) == NULL || 306094c80e0Sdjm BN_bin2bn(ptr + 32, 32, y) == NULL) { 307094c80e0Sdjm skdebug(__func__, "BN_bin2bn failed"); 308094c80e0Sdjm goto out; 309094c80e0Sdjm } 310094c80e0Sdjm if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bn_ctx) != 1) { 311094c80e0Sdjm skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); 312094c80e0Sdjm goto out; 313094c80e0Sdjm } 314094c80e0Sdjm response->public_key_len = EC_POINT_point2oct(g, q, 315094c80e0Sdjm POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx); 316094c80e0Sdjm if (response->public_key_len == 0 || response->public_key_len > 2048) { 317094c80e0Sdjm skdebug(__func__, "bad pubkey length %zu", 318094c80e0Sdjm response->public_key_len); 319094c80e0Sdjm goto out; 320094c80e0Sdjm } 321094c80e0Sdjm if ((response->public_key = malloc(response->public_key_len)) == NULL) { 322094c80e0Sdjm skdebug(__func__, "malloc pubkey failed"); 323094c80e0Sdjm goto out; 324094c80e0Sdjm } 325094c80e0Sdjm if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, 326094c80e0Sdjm response->public_key, response->public_key_len, bn_ctx) == 0) { 327094c80e0Sdjm skdebug(__func__, "EC_POINT_point2oct failed"); 328094c80e0Sdjm goto out; 329094c80e0Sdjm } 330094c80e0Sdjm /* success */ 331094c80e0Sdjm ret = 0; 332094c80e0Sdjm out: 333094c80e0Sdjm if (ret != 0 && response->public_key != NULL) { 334094c80e0Sdjm memset(response->public_key, 0, response->public_key_len); 335094c80e0Sdjm free(response->public_key); 336094c80e0Sdjm response->public_key = NULL; 337094c80e0Sdjm } 338094c80e0Sdjm EC_POINT_free(q); 339094c80e0Sdjm EC_GROUP_free(g); 340094c80e0Sdjm BN_CTX_free(bn_ctx); 341094c80e0Sdjm return ret; 342094c80e0Sdjm } 343094c80e0Sdjm 344094c80e0Sdjm static int 345094c80e0Sdjm pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response) 346094c80e0Sdjm { 347094c80e0Sdjm const uint8_t *ptr; 348094c80e0Sdjm size_t len; 349094c80e0Sdjm int ret = -1; 350094c80e0Sdjm 351094c80e0Sdjm response->public_key = NULL; 352094c80e0Sdjm response->public_key_len = 0; 353094c80e0Sdjm 354094c80e0Sdjm if ((len = fido_cred_pubkey_len(cred)) != 32) { 355094c80e0Sdjm skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); 356094c80e0Sdjm goto out; 357094c80e0Sdjm } 358094c80e0Sdjm if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { 359094c80e0Sdjm skdebug(__func__, "fido_cred_pubkey_ptr failed"); 360094c80e0Sdjm goto out; 361094c80e0Sdjm } 362094c80e0Sdjm response->public_key_len = len; 363094c80e0Sdjm if ((response->public_key = malloc(response->public_key_len)) == NULL) { 364094c80e0Sdjm skdebug(__func__, "malloc pubkey failed"); 365094c80e0Sdjm goto out; 366094c80e0Sdjm } 367094c80e0Sdjm memcpy(response->public_key, ptr, len); 368094c80e0Sdjm ret = 0; 369094c80e0Sdjm out: 370094c80e0Sdjm if (ret != 0) 371094c80e0Sdjm free(response->public_key); 372094c80e0Sdjm return ret; 373094c80e0Sdjm } 374094c80e0Sdjm 375094c80e0Sdjm static int 376094c80e0Sdjm pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response) 377094c80e0Sdjm { 378094c80e0Sdjm switch(alg) { 379094c80e0Sdjm case SK_ECDSA: 380094c80e0Sdjm return pack_public_key_ecdsa(cred, response); 381094c80e0Sdjm case SK_ED25519: 382094c80e0Sdjm return pack_public_key_ed25519(cred, response); 383094c80e0Sdjm default: 384094c80e0Sdjm return -1; 385094c80e0Sdjm } 386094c80e0Sdjm } 387094c80e0Sdjm 388094c80e0Sdjm int 389094c80e0Sdjm sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, 390094c80e0Sdjm const char *application, uint8_t flags, 391094c80e0Sdjm struct sk_enroll_response **enroll_reponse) 392094c80e0Sdjm { 393094c80e0Sdjm fido_cred_t *cred = NULL; 394094c80e0Sdjm fido_dev_t *dev = NULL; 395094c80e0Sdjm const uint8_t *ptr; 396094c80e0Sdjm uint8_t user_id[32]; 397094c80e0Sdjm struct sk_enroll_response *response = NULL; 398094c80e0Sdjm size_t len; 399094c80e0Sdjm int cose_alg; 400094c80e0Sdjm int ret = -1; 401094c80e0Sdjm int r; 402094c80e0Sdjm char *device = NULL; 403094c80e0Sdjm 404094c80e0Sdjm (void)flags; /* XXX; unused */ 405094c80e0Sdjm #ifdef SK_DEBUG 406094c80e0Sdjm fido_init(FIDO_DEBUG); 407094c80e0Sdjm #endif 408094c80e0Sdjm if (enroll_reponse == NULL) { 409094c80e0Sdjm skdebug(__func__, "enroll_reponse == NULL"); 410094c80e0Sdjm goto out; 411094c80e0Sdjm } 412094c80e0Sdjm *enroll_reponse = NULL; 413094c80e0Sdjm switch(alg) { 414094c80e0Sdjm case SK_ECDSA: 415094c80e0Sdjm cose_alg = COSE_ES256; 416094c80e0Sdjm break; 417094c80e0Sdjm case SK_ED25519: 418094c80e0Sdjm cose_alg = COSE_EDDSA; 419094c80e0Sdjm break; 420094c80e0Sdjm default: 421094c80e0Sdjm skdebug(__func__, "unsupported key type %d", alg); 422094c80e0Sdjm goto out; 423094c80e0Sdjm } 424094c80e0Sdjm if ((device = pick_first_device()) == NULL) { 425094c80e0Sdjm skdebug(__func__, "pick_first_device failed"); 426094c80e0Sdjm goto out; 427094c80e0Sdjm } 428094c80e0Sdjm skdebug(__func__, "using device %s", device); 429094c80e0Sdjm if ((cred = fido_cred_new()) == NULL) { 430094c80e0Sdjm skdebug(__func__, "fido_cred_new failed"); 431094c80e0Sdjm goto out; 432094c80e0Sdjm } 433094c80e0Sdjm memset(user_id, 0, sizeof(user_id)); 434094c80e0Sdjm if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { 435094c80e0Sdjm skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); 436094c80e0Sdjm goto out; 437094c80e0Sdjm } 438094c80e0Sdjm if ((r = fido_cred_set_clientdata_hash(cred, challenge, 439094c80e0Sdjm challenge_len)) != FIDO_OK) { 440094c80e0Sdjm skdebug(__func__, "fido_cred_set_clientdata_hash: %s", 441094c80e0Sdjm fido_strerr(r)); 442094c80e0Sdjm goto out; 443094c80e0Sdjm } 444094c80e0Sdjm if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), 445094c80e0Sdjm "openssh", "openssh", NULL)) != FIDO_OK) { 446094c80e0Sdjm skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); 447094c80e0Sdjm goto out; 448094c80e0Sdjm } 449094c80e0Sdjm if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { 450094c80e0Sdjm skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); 451094c80e0Sdjm goto out; 452094c80e0Sdjm } 453094c80e0Sdjm if ((dev = fido_dev_new()) == NULL) { 454094c80e0Sdjm skdebug(__func__, "fido_dev_new failed"); 455094c80e0Sdjm goto out; 456094c80e0Sdjm } 457094c80e0Sdjm if ((r = fido_dev_open(dev, device)) != FIDO_OK) { 458094c80e0Sdjm skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); 459094c80e0Sdjm goto out; 460094c80e0Sdjm } 461094c80e0Sdjm if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) { 462094c80e0Sdjm skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); 463094c80e0Sdjm goto out; 464094c80e0Sdjm } 465094c80e0Sdjm if (fido_cred_x5c_ptr(cred) != NULL) { 466094c80e0Sdjm if ((r = fido_cred_verify(cred)) != FIDO_OK) { 467094c80e0Sdjm skdebug(__func__, "fido_cred_verify: %s", 468094c80e0Sdjm fido_strerr(r)); 469094c80e0Sdjm goto out; 470094c80e0Sdjm } 471094c80e0Sdjm } else { 472094c80e0Sdjm skdebug(__func__, "self-attested credential"); 473094c80e0Sdjm if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { 474094c80e0Sdjm skdebug(__func__, "fido_cred_verify_self: %s", 475094c80e0Sdjm fido_strerr(r)); 476094c80e0Sdjm goto out; 477094c80e0Sdjm } 478094c80e0Sdjm } 479094c80e0Sdjm if ((response = calloc(1, sizeof(*response))) == NULL) { 480094c80e0Sdjm skdebug(__func__, "calloc response failed"); 481094c80e0Sdjm goto out; 482094c80e0Sdjm } 483094c80e0Sdjm if (pack_public_key(alg, cred, response) != 0) { 484094c80e0Sdjm skdebug(__func__, "pack_public_key failed"); 485094c80e0Sdjm goto out; 486094c80e0Sdjm } 487094c80e0Sdjm if ((ptr = fido_cred_id_ptr(cred)) != NULL) { 488094c80e0Sdjm len = fido_cred_id_len(cred); 489094c80e0Sdjm if ((response->key_handle = calloc(1, len)) == NULL) { 490094c80e0Sdjm skdebug(__func__, "calloc key handle failed"); 491094c80e0Sdjm goto out; 492094c80e0Sdjm } 493094c80e0Sdjm memcpy(response->key_handle, ptr, len); 494094c80e0Sdjm response->key_handle_len = len; 495094c80e0Sdjm } 496094c80e0Sdjm if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { 497094c80e0Sdjm len = fido_cred_sig_len(cred); 498094c80e0Sdjm if ((response->signature = calloc(1, len)) == NULL) { 499094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 500094c80e0Sdjm goto out; 501094c80e0Sdjm } 502094c80e0Sdjm memcpy(response->signature, ptr, len); 503094c80e0Sdjm response->signature_len = len; 504094c80e0Sdjm } 505094c80e0Sdjm if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { 506094c80e0Sdjm len = fido_cred_x5c_len(cred); 507094c80e0Sdjm if ((response->attestation_cert = calloc(1, len)) == NULL) { 508094c80e0Sdjm skdebug(__func__, "calloc attestation cert failed"); 509094c80e0Sdjm goto out; 510094c80e0Sdjm } 511094c80e0Sdjm memcpy(response->attestation_cert, ptr, len); 512094c80e0Sdjm response->attestation_cert_len = len; 513094c80e0Sdjm } 514094c80e0Sdjm *enroll_reponse = response; 515094c80e0Sdjm response = NULL; 516094c80e0Sdjm ret = 0; 517094c80e0Sdjm out: 518094c80e0Sdjm free(device); 519094c80e0Sdjm if (response != NULL) { 520094c80e0Sdjm free(response->public_key); 521094c80e0Sdjm free(response->key_handle); 522094c80e0Sdjm free(response->signature); 523094c80e0Sdjm free(response->attestation_cert); 524094c80e0Sdjm free(response); 525094c80e0Sdjm } 526094c80e0Sdjm if (dev != NULL) { 527094c80e0Sdjm fido_dev_close(dev); 528094c80e0Sdjm fido_dev_free(&dev); 529094c80e0Sdjm } 530094c80e0Sdjm if (cred != NULL) { 531094c80e0Sdjm fido_cred_free(&cred); 532094c80e0Sdjm } 533094c80e0Sdjm return ret; 534094c80e0Sdjm } 535094c80e0Sdjm 536094c80e0Sdjm static int 537094c80e0Sdjm pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) 538094c80e0Sdjm { 539094c80e0Sdjm ECDSA_SIG *sig = NULL; 540094c80e0Sdjm const BIGNUM *sig_r, *sig_s; 541094c80e0Sdjm const unsigned char *cp; 542094c80e0Sdjm size_t sig_len; 543094c80e0Sdjm int ret = -1; 544094c80e0Sdjm 545094c80e0Sdjm cp = fido_assert_sig_ptr(assert, 0); 546094c80e0Sdjm sig_len = fido_assert_sig_len(assert, 0); 547094c80e0Sdjm if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { 548094c80e0Sdjm skdebug(__func__, "d2i_ECDSA_SIG failed"); 549094c80e0Sdjm goto out; 550094c80e0Sdjm } 551094c80e0Sdjm ECDSA_SIG_get0(sig, &sig_r, &sig_s); 552094c80e0Sdjm response->sig_r_len = BN_num_bytes(sig_r); 553094c80e0Sdjm response->sig_s_len = BN_num_bytes(sig_s); 554094c80e0Sdjm if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || 555094c80e0Sdjm (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { 556094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 557094c80e0Sdjm goto out; 558094c80e0Sdjm } 559094c80e0Sdjm BN_bn2bin(sig_r, response->sig_r); 560094c80e0Sdjm BN_bn2bin(sig_s, response->sig_s); 561094c80e0Sdjm ret = 0; 562094c80e0Sdjm out: 563094c80e0Sdjm ECDSA_SIG_free(sig); 564094c80e0Sdjm if (ret != 0) { 565094c80e0Sdjm free(response->sig_r); 566094c80e0Sdjm free(response->sig_s); 567094c80e0Sdjm response->sig_r = NULL; 568094c80e0Sdjm response->sig_s = NULL; 569094c80e0Sdjm } 570094c80e0Sdjm return ret; 571094c80e0Sdjm } 572094c80e0Sdjm 573094c80e0Sdjm static int 574094c80e0Sdjm pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) 575094c80e0Sdjm { 576094c80e0Sdjm const unsigned char *ptr; 577094c80e0Sdjm size_t len; 578094c80e0Sdjm int ret = -1; 579094c80e0Sdjm 580094c80e0Sdjm ptr = fido_assert_sig_ptr(assert, 0); 581094c80e0Sdjm len = fido_assert_sig_len(assert, 0); 582094c80e0Sdjm if (len != 64) { 583094c80e0Sdjm skdebug(__func__, "bad length %zu", len); 584094c80e0Sdjm goto out; 585094c80e0Sdjm } 586094c80e0Sdjm response->sig_r_len = len; 587094c80e0Sdjm if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { 588094c80e0Sdjm skdebug(__func__, "calloc signature failed"); 589094c80e0Sdjm goto out; 590094c80e0Sdjm } 591094c80e0Sdjm memcpy(response->sig_r, ptr, len); 592094c80e0Sdjm ret = 0; 593094c80e0Sdjm out: 594094c80e0Sdjm if (ret != 0) { 595094c80e0Sdjm free(response->sig_r); 596094c80e0Sdjm response->sig_r = NULL; 597094c80e0Sdjm } 598094c80e0Sdjm return ret; 599094c80e0Sdjm } 600094c80e0Sdjm 601094c80e0Sdjm static int 602094c80e0Sdjm pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) 603094c80e0Sdjm { 604094c80e0Sdjm switch(alg) { 605094c80e0Sdjm case SK_ECDSA: 606094c80e0Sdjm return pack_sig_ecdsa(assert, response); 607094c80e0Sdjm case SK_ED25519: 608094c80e0Sdjm return pack_sig_ed25519(assert, response); 609094c80e0Sdjm default: 610094c80e0Sdjm return -1; 611094c80e0Sdjm } 612094c80e0Sdjm } 613094c80e0Sdjm 614094c80e0Sdjm int 615094c80e0Sdjm sk_sign(int alg, const uint8_t *message, size_t message_len, 616094c80e0Sdjm const char *application, 617094c80e0Sdjm const uint8_t *key_handle, size_t key_handle_len, 618094c80e0Sdjm uint8_t flags, struct sk_sign_response **sign_response) 619094c80e0Sdjm { 620094c80e0Sdjm fido_assert_t *assert = NULL; 621094c80e0Sdjm fido_dev_t *dev = NULL; 622094c80e0Sdjm struct sk_sign_response *response = NULL; 623094c80e0Sdjm int ret = -1; 624094c80e0Sdjm int r; 625094c80e0Sdjm 626094c80e0Sdjm #ifdef SK_DEBUG 627094c80e0Sdjm fido_init(FIDO_DEBUG); 628094c80e0Sdjm #endif 629094c80e0Sdjm 630094c80e0Sdjm if (sign_response == NULL) { 631094c80e0Sdjm skdebug(__func__, "sign_response == NULL"); 632094c80e0Sdjm goto out; 633094c80e0Sdjm } 634094c80e0Sdjm *sign_response = NULL; 635094c80e0Sdjm if ((dev = find_device(message, message_len, application, key_handle, 636094c80e0Sdjm key_handle_len)) == NULL) { 637094c80e0Sdjm skdebug(__func__, "couldn't find device for key handle"); 638094c80e0Sdjm goto out; 639094c80e0Sdjm } 640094c80e0Sdjm if ((assert = fido_assert_new()) == NULL) { 641094c80e0Sdjm skdebug(__func__, "fido_assert_new failed"); 642094c80e0Sdjm goto out; 643094c80e0Sdjm } 644094c80e0Sdjm if ((r = fido_assert_set_clientdata_hash(assert, message, 645094c80e0Sdjm message_len)) != FIDO_OK) { 646094c80e0Sdjm skdebug(__func__, "fido_assert_set_clientdata_hash: %s", 647094c80e0Sdjm fido_strerr(r)); 648094c80e0Sdjm goto out; 649094c80e0Sdjm } 650094c80e0Sdjm if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { 651094c80e0Sdjm skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); 652094c80e0Sdjm goto out; 653094c80e0Sdjm } 654094c80e0Sdjm if ((r = fido_assert_allow_cred(assert, key_handle, 655094c80e0Sdjm key_handle_len)) != FIDO_OK) { 656094c80e0Sdjm skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); 657094c80e0Sdjm goto out; 658094c80e0Sdjm } 659094c80e0Sdjm if ((r = fido_assert_set_up(assert, 660094c80e0Sdjm (flags & SK_USER_PRESENCE_REQD) ? 661094c80e0Sdjm FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { 662094c80e0Sdjm skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); 663094c80e0Sdjm goto out; 664094c80e0Sdjm } 665094c80e0Sdjm if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) { 666094c80e0Sdjm skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); 667094c80e0Sdjm goto out; 668094c80e0Sdjm } 669094c80e0Sdjm if ((response = calloc(1, sizeof(*response))) == NULL) { 670094c80e0Sdjm skdebug(__func__, "calloc response failed"); 671094c80e0Sdjm goto out; 672094c80e0Sdjm } 673094c80e0Sdjm response->flags = fido_assert_flags(assert, 0); 674094c80e0Sdjm response->counter = fido_assert_sigcount(assert, 0); 675094c80e0Sdjm if (pack_sig(alg, assert, response) != 0) { 676094c80e0Sdjm skdebug(__func__, "pack_sig failed"); 677094c80e0Sdjm goto out; 678094c80e0Sdjm } 679094c80e0Sdjm *sign_response = response; 680094c80e0Sdjm response = NULL; 681094c80e0Sdjm ret = 0; 682094c80e0Sdjm out: 683094c80e0Sdjm if (response != NULL) { 684094c80e0Sdjm free(response->sig_r); 685094c80e0Sdjm free(response->sig_s); 686094c80e0Sdjm free(response); 687094c80e0Sdjm } 688094c80e0Sdjm if (dev != NULL) { 689094c80e0Sdjm fido_dev_close(dev); 690094c80e0Sdjm fido_dev_free(&dev); 691094c80e0Sdjm } 692094c80e0Sdjm if (assert != NULL) { 693094c80e0Sdjm fido_assert_free(&assert); 694094c80e0Sdjm } 695094c80e0Sdjm return ret; 696094c80e0Sdjm } 697