| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | /* | 
| 2 |  |  * Copyright (c) 2018-2022 Yubico AB. All rights reserved. | 
| 3 |  |  * Use of this source code is governed by a BSD-style | 
| 4 |  |  * license that can be found in the LICENSE file. | 
| 5 |  |  * SPDX-License-Identifier: BSD-2-Clause | 
| 6 |  |  */ | 
| 7 |  |  | 
| 8 |  | #include <openssl/sha.h> | 
| 9 |  | #include <openssl/x509.h> | 
| 10 |  |  | 
| 11 |  | #ifdef HAVE_UNISTD_H | 
| 12 |  | #include <unistd.h> | 
| 13 |  | #endif | 
| 14 |  | #include <errno.h> | 
| 15 |  |  | 
| 16 |  | #include "fido.h" | 
| 17 |  | #include "fido/es256.h" | 
| 18 |  | #include "fallthrough.h" | 
| 19 |  |  | 
| 20 | 2.50k | #define U2F_PACE_MS (100) | 
| 21 |  |  | 
| 22 |  | #if defined(_MSC_VER) | 
| 23 |  | static int | 
| 24 |  | usleep(unsigned int usec) | 
| 25 |  | { | 
| 26 |  |         Sleep(usec / 1000); | 
| 27 |  |  | 
| 28 |  |         return (0); | 
| 29 |  | } | 
| 30 |  | #endif | 
| 31 |  |  | 
| 32 |  | static int | 
| 33 |  | delay_ms(unsigned int ms, int *ms_remain) | 
| 34 | 2.50k | { | 
| 35 | 2.50k |         if (*ms_remain > -1 && (unsigned int)*ms_remain < ms) | 
| 36 | 1.61k |                 ms = (unsigned int)*ms_remain; | 
| 37 |  |  | 
| 38 | 2.50k |         if (ms > UINT_MAX / 1000) { | 
| 39 | 0 |                 fido_log_debug("%s: ms=%u", __func__, ms); | 
| 40 | 0 |                 return (-1); | 
| 41 | 0 |         } | 
| 42 |  |  | 
| 43 | 2.50k |         if (usleep(ms * 1000) < 0) { | 
| 44 | 3 |                 fido_log_error(errno, "%s: usleep", __func__); | 
| 45 | 3 |                 return (-1); | 
| 46 | 3 |         } | 
| 47 |  |  | 
| 48 | 2.49k |         if (*ms_remain > -1) | 
| 49 | 2.49k |                 *ms_remain -= (int)ms; | 
| 50 |  |  | 
| 51 | 2.49k |         return (0); | 
| 52 | 2.50k | } | 
| 53 |  |  | 
| 54 |  | static int | 
| 55 |  | sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len) | 
| 56 | 877 | { | 
| 57 | 877 |         sig->len = *len; /* consume the whole buffer */ | 
| 58 | 877 |         if ((sig->ptr = calloc(1, sig->len)) == NULL || | 
| 59 | 877 |             fido_buf_read(buf, len, sig->ptr, sig->len) < 0) { | 
| 60 | 2 |                 fido_log_debug("%s: fido_buf_read", __func__); | 
| 61 | 2 |                 fido_blob_reset(sig); | 
| 62 | 2 |                 return (-1); | 
| 63 | 2 |         } | 
| 64 |  |  | 
| 65 | 875 |         return (0); | 
| 66 | 877 | } | 
| 67 |  |  | 
| 68 |  | static int | 
| 69 |  | x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len) | 
| 70 | 600 | { | 
| 71 | 600 |         X509    *cert = NULL; | 
| 72 | 600 |         int      ok = -1; | 
| 73 |  |  | 
| 74 | 600 |         if (*len > LONG_MAX) { | 
| 75 | 0 |                 fido_log_debug("%s: invalid len %zu", __func__, *len); | 
| 76 | 0 |                 goto fail; | 
| 77 | 0 |         } | 
| 78 |  |  | 
| 79 |  |         /* find out the certificate's length */ | 
| 80 | 600 |         const unsigned char *end = *buf; | 
| 81 | 600 |         if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf || | 
| 82 | 600 |             (x5c->len = (size_t)(end - *buf)) >= *len) { | 
| 83 | 350 |                 fido_log_debug("%s: d2i_X509", __func__); | 
| 84 | 350 |                 goto fail; | 
| 85 | 350 |         } | 
| 86 |  |  | 
| 87 |  |         /* read accordingly */ | 
| 88 | 250 |         if ((x5c->ptr = calloc(1, x5c->len)) == NULL || | 
| 89 | 250 |             fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) { | 
| 90 | 1 |                 fido_log_debug("%s: fido_buf_read", __func__); | 
| 91 | 1 |                 goto fail; | 
| 92 | 1 |         } | 
| 93 |  |  | 
| 94 | 249 |         ok = 0; | 
| 95 | 600 | fail: | 
| 96 | 600 |         if (cert != NULL) | 
| 97 | 250 |                 X509_free(cert); | 
| 98 |  |  | 
| 99 | 600 |         if (ok < 0) | 
| 100 | 351 |                 fido_blob_reset(x5c); | 
| 101 |  |  | 
| 102 | 600 |         return (ok); | 
| 103 | 249 | } | 
| 104 |  |  | 
| 105 |  | static int | 
| 106 |  | authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount, | 
| 107 |  |     fido_blob_t *fake_cbor_ad) | 
| 108 | 627 | { | 
| 109 | 627 |         fido_authdata_t  ad; | 
| 110 | 627 |         cbor_item_t     *item = NULL; | 
| 111 | 627 |         size_t           alloc_len; | 
| 112 |  |  | 
| 113 | 627 |         memset(&ad, 0, sizeof(ad)); | 
| 114 |  |  | 
| 115 | 627 |         if (SHA256((const void *)rp_id, strlen(rp_id), | 
| 116 | 627 |             ad.rp_id_hash) != ad.rp_id_hash) { | 
| 117 | 2 |                 fido_log_debug("%s: sha256", __func__); | 
| 118 | 2 |                 return (-1); | 
| 119 | 2 |         } | 
| 120 |  |  | 
| 121 | 625 |         ad.flags = flags; /* XXX translate? */ | 
| 122 | 625 |         ad.sigcount = sigcount; | 
| 123 |  |  | 
| 124 | 625 |         if ((item = cbor_build_bytestring((const unsigned char *)&ad, | 
| 125 | 625 |             sizeof(ad))) == NULL) { | 
| 126 | 1 |                 fido_log_debug("%s: cbor_build_bytestring", __func__); | 
| 127 | 1 |                 return (-1); | 
| 128 | 1 |         } | 
| 129 |  |  | 
| 130 | 624 |         if (fake_cbor_ad->ptr != NULL || | 
| 131 | 624 |             (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr, | 
| 132 | 624 |             &alloc_len)) == 0) { | 
| 133 | 1 |                 fido_log_debug("%s: cbor_serialize_alloc", __func__); | 
| 134 | 1 |                 cbor_decref(&item); | 
| 135 | 1 |                 return (-1); | 
| 136 | 1 |         } | 
| 137 |  |  | 
| 138 | 623 |         cbor_decref(&item); | 
| 139 |  |  | 
| 140 | 623 |         return (0); | 
| 141 | 624 | } | 
| 142 |  |  | 
| 143 |  | /* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */ | 
| 144 |  | static int | 
| 145 |  | send_dummy_register(fido_dev_t *dev, int *ms) | 
| 146 | 56 | { | 
| 147 | 56 |         iso7816_apdu_t  *apdu = NULL; | 
| 148 | 56 |         unsigned char   *reply = NULL; | 
| 149 | 56 |         unsigned char    challenge[SHA256_DIGEST_LENGTH]; | 
| 150 | 56 |         unsigned char    application[SHA256_DIGEST_LENGTH]; | 
| 151 | 56 |         int              r; | 
| 152 |  |  | 
| 153 |  |         /* dummy challenge & application */ | 
| 154 | 56 |         memset(&challenge, 0xff, sizeof(challenge)); | 
| 155 | 56 |         memset(&application, 0xff, sizeof(application)); | 
| 156 |  |  | 
| 157 | 56 |         if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * | 
| 158 | 56 |             SHA256_DIGEST_LENGTH)) == NULL || | 
| 159 | 56 |             iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || | 
| 160 | 56 |             iso7816_add(apdu, &application, sizeof(application)) < 0) { | 
| 161 | 1 |                 fido_log_debug("%s: iso7816", __func__); | 
| 162 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 163 | 1 |                 goto fail; | 
| 164 | 1 |         } | 
| 165 |  |  | 
| 166 | 55 |         if ((reply = malloc(FIDO_MAXMSG)) == NULL) { | 
| 167 | 1 |                 fido_log_debug("%s: malloc", __func__); | 
| 168 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 169 | 1 |                 goto fail; | 
| 170 | 1 |         } | 
| 171 |  |  | 
| 172 | 171 |         do { | 
| 173 | 171 |                 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), | 
| 174 | 171 |                     iso7816_len(apdu), ms) < 0) { | 
| 175 | 4 |                         fido_log_debug("%s: fido_tx", __func__); | 
| 176 | 4 |                         r = FIDO_ERR_TX; | 
| 177 | 4 |                         goto fail; | 
| 178 | 4 |                 } | 
| 179 | 167 |                 if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) < 2) { | 
| 180 | 26 |                         fido_log_debug("%s: fido_rx", __func__); | 
| 181 | 26 |                         r = FIDO_ERR_RX; | 
| 182 | 26 |                         goto fail; | 
| 183 | 26 |                 } | 
| 184 | 141 |                 if (delay_ms(U2F_PACE_MS, ms) != 0) { | 
| 185 | 1 |                         fido_log_debug("%s: delay_ms", __func__); | 
| 186 | 1 |                         r = FIDO_ERR_RX; | 
| 187 | 1 |                         goto fail; | 
| 188 | 1 |                 } | 
| 189 | 141 |         } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); | 
| 190 |  |  | 
| 191 | 23 |         r = FIDO_OK; | 
| 192 | 56 | fail: | 
| 193 | 56 |         iso7816_free(&apdu); | 
| 194 | 56 |         freezero(reply, FIDO_MAXMSG); | 
| 195 |  |  | 
| 196 | 56 |         return (r); | 
| 197 | 23 | } | 
| 198 |  |  | 
| 199 |  | static int | 
| 200 |  | key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id, | 
| 201 |  |     int *found, int *ms) | 
| 202 | 1.81k | { | 
| 203 | 1.81k |         iso7816_apdu_t  *apdu = NULL; | 
| 204 | 1.81k |         unsigned char   *reply = NULL; | 
| 205 | 1.81k |         unsigned char    challenge[SHA256_DIGEST_LENGTH]; | 
| 206 | 1.81k |         unsigned char    rp_id_hash[SHA256_DIGEST_LENGTH]; | 
| 207 | 1.81k |         uint8_t          key_id_len; | 
| 208 | 1.81k |         int              r; | 
| 209 |  |  | 
| 210 | 1.81k |         if (key_id->len > UINT8_MAX || rp_id == NULL) { | 
| 211 | 8 |                 fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__, | 
| 212 | 8 |                     key_id->len, (const void *)rp_id); | 
| 213 | 8 |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 214 | 8 |                 goto fail; | 
| 215 | 8 |         } | 
| 216 |  |  | 
| 217 | 1.80k |         memset(&challenge, 0xff, sizeof(challenge)); | 
| 218 | 1.80k |         memset(&rp_id_hash, 0, sizeof(rp_id_hash)); | 
| 219 |  |  | 
| 220 | 1.80k |         if (SHA256((const void *)rp_id, strlen(rp_id), | 
| 221 | 1.80k |             rp_id_hash) != rp_id_hash) { | 
| 222 | 3 |                 fido_log_debug("%s: sha256", __func__); | 
| 223 | 3 |                 r = FIDO_ERR_INTERNAL; | 
| 224 | 3 |                 goto fail; | 
| 225 | 3 |         } | 
| 226 |  |  | 
| 227 | 1.80k |         key_id_len = (uint8_t)key_id->len; | 
| 228 |  |  | 
| 229 | 1.80k |         if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 * | 
| 230 | 1.80k |             SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL || | 
| 231 | 1.80k |             iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || | 
| 232 | 1.80k |             iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || | 
| 233 | 1.80k |             iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 || | 
| 234 | 1.80k |             iso7816_add(apdu, key_id->ptr, key_id_len) < 0) { | 
| 235 | 5 |                 fido_log_debug("%s: iso7816", __func__); | 
| 236 | 5 |                 r = FIDO_ERR_INTERNAL; | 
| 237 | 5 |                 goto fail; | 
| 238 | 5 |         } | 
| 239 |  |  | 
| 240 | 1.79k |         if ((reply = malloc(FIDO_MAXMSG)) == NULL) { | 
| 241 | 6 |                 fido_log_debug("%s: malloc", __func__); | 
| 242 | 6 |                 r = FIDO_ERR_INTERNAL; | 
| 243 | 6 |                 goto fail; | 
| 244 | 6 |         } | 
| 245 |  |  | 
| 246 | 1.78k |         if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), | 
| 247 | 1.78k |             iso7816_len(apdu), ms) < 0) { | 
| 248 | 92 |                 fido_log_debug("%s: fido_tx", __func__); | 
| 249 | 92 |                 r = FIDO_ERR_TX; | 
| 250 | 92 |                 goto fail; | 
| 251 | 92 |         } | 
| 252 | 1.69k |         if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) != 2) { | 
| 253 | 681 |                 fido_log_debug("%s: fido_rx", __func__); | 
| 254 | 681 |                 r = FIDO_ERR_RX; | 
| 255 | 681 |                 goto fail; | 
| 256 | 681 |         } | 
| 257 |  |  | 
| 258 | 1.01k |         switch ((reply[0] << 8) | reply[1]) { | 
| 259 | 864 |         case SW_CONDITIONS_NOT_SATISFIED: | 
| 260 | 864 |                 *found = 1; /* key exists */ | 
| 261 | 864 |                 break; | 
| 262 | 20 |         case SW_WRONG_DATA: | 
| 263 | 20 |                 *found = 0; /* key does not exist */ | 
| 264 | 20 |                 break; | 
| 265 | 132 |         default: | 
| 266 |  |                 /* unexpected sw */ | 
| 267 | 132 |                 r = FIDO_ERR_INTERNAL; | 
| 268 | 132 |                 goto fail; | 
| 269 | 1.01k |         } | 
| 270 |  |  | 
| 271 | 884 |         r = FIDO_OK; | 
| 272 | 1.81k | fail: | 
| 273 | 1.81k |         iso7816_free(&apdu); | 
| 274 | 1.81k |         freezero(reply, FIDO_MAXMSG); | 
| 275 |  |  | 
| 276 | 1.81k |         return (r); | 
| 277 | 884 | } | 
| 278 |  |  | 
| 279 |  | static int | 
| 280 |  | parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id, | 
| 281 |  |     const unsigned char *reply, size_t len) | 
| 282 | 663 | { | 
| 283 | 663 |         uint8_t         flags; | 
| 284 | 663 |         uint32_t        sigcount; | 
| 285 |  |  | 
| 286 | 663 |         if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) { | 
| 287 | 33 |                 fido_log_debug("%s: unexpected sw", __func__); | 
| 288 | 33 |                 return (FIDO_ERR_RX); | 
| 289 | 33 |         } | 
| 290 |  |  | 
| 291 | 630 |         len -= 2; | 
| 292 |  |  | 
| 293 | 630 |         if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 || | 
| 294 | 630 |             fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) { | 
| 295 | 2 |                 fido_log_debug("%s: fido_buf_read", __func__); | 
| 296 | 2 |                 return (FIDO_ERR_RX); | 
| 297 | 2 |         } | 
| 298 |  |  | 
| 299 | 628 |         if (sig_get(sig, &reply, &len) < 0) { | 
| 300 | 1 |                 fido_log_debug("%s: sig_get", __func__); | 
| 301 | 1 |                 return (FIDO_ERR_RX); | 
| 302 | 1 |         } | 
| 303 |  |  | 
| 304 | 627 |         if (authdata_fake(rp_id, flags, sigcount, ad) < 0) { | 
| 305 | 4 |                 fido_log_debug("%s; authdata_fake", __func__); | 
| 306 | 4 |                 return (FIDO_ERR_RX); | 
| 307 | 4 |         } | 
| 308 |  |  | 
| 309 | 623 |         return (FIDO_OK); | 
| 310 | 627 | } | 
| 311 |  |  | 
| 312 |  | static int | 
| 313 |  | do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id, | 
| 314 |  |     const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int *ms) | 
| 315 | 703 | { | 
| 316 | 703 |         iso7816_apdu_t  *apdu = NULL; | 
| 317 | 703 |         unsigned char   *reply = NULL; | 
| 318 | 703 |         unsigned char    rp_id_hash[SHA256_DIGEST_LENGTH]; | 
| 319 | 703 |         int              reply_len; | 
| 320 | 703 |         uint8_t          key_id_len; | 
| 321 | 703 |         int              r; | 
| 322 |  |  | 
| 323 | 703 | #ifdef FIDO_FUZZ | 
| 324 | 703 |         *ms = 0; /* XXX */ | 
| 325 | 703 | #endif | 
| 326 |  |  | 
| 327 | 703 |         if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX || | 
| 328 | 703 |             rp_id == NULL) { | 
| 329 | 6 |                 r = FIDO_ERR_INVALID_ARGUMENT; | 
| 330 | 6 |                 goto fail; | 
| 331 | 6 |         } | 
| 332 |  |  | 
| 333 | 697 |         memset(&rp_id_hash, 0, sizeof(rp_id_hash)); | 
| 334 |  |  | 
| 335 | 697 |         if (SHA256((const void *)rp_id, strlen(rp_id), | 
| 336 | 697 |             rp_id_hash) != rp_id_hash) { | 
| 337 | 1 |                 fido_log_debug("%s: sha256", __func__); | 
| 338 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 339 | 1 |                 goto fail; | 
| 340 | 1 |         } | 
| 341 |  |  | 
| 342 | 696 |         key_id_len = (uint8_t)key_id->len; | 
| 343 |  |  | 
| 344 | 696 |         if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 * | 
| 345 | 696 |             SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL || | 
| 346 | 696 |             iso7816_add(apdu, cdh->ptr, cdh->len) < 0 || | 
| 347 | 696 |             iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || | 
| 348 | 696 |             iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 || | 
| 349 | 696 |             iso7816_add(apdu, key_id->ptr, key_id_len) < 0) { | 
| 350 | 1 |                 fido_log_debug("%s: iso7816", __func__); | 
| 351 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 352 | 1 |                 goto fail; | 
| 353 | 1 |         } | 
| 354 |  |  | 
| 355 | 695 |         if ((reply = malloc(FIDO_MAXMSG)) == NULL) { | 
| 356 | 1 |                 fido_log_debug("%s: malloc", __func__); | 
| 357 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 358 | 1 |                 goto fail; | 
| 359 | 1 |         } | 
| 360 |  |  | 
| 361 | 1.05k |         do { | 
| 362 | 1.05k |                 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), | 
| 363 | 1.05k |                     iso7816_len(apdu), ms) < 0) { | 
| 364 | 5 |                         fido_log_debug("%s: fido_tx", __func__); | 
| 365 | 5 |                         r = FIDO_ERR_TX; | 
| 366 | 5 |                         goto fail; | 
| 367 | 5 |                 } | 
| 368 | 1.05k |                 if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, | 
| 369 | 1.05k |                     FIDO_MAXMSG, ms)) < 2) { | 
| 370 | 25 |                         fido_log_debug("%s: fido_rx", __func__); | 
| 371 | 25 |                         r = FIDO_ERR_RX; | 
| 372 | 25 |                         goto fail; | 
| 373 | 25 |                 } | 
| 374 | 1.02k |                 if (delay_ms(U2F_PACE_MS, ms) != 0) { | 
| 375 | 1 |                         fido_log_debug("%s: delay_ms", __func__); | 
| 376 | 1 |                         r = FIDO_ERR_RX; | 
| 377 | 1 |                         goto fail; | 
| 378 | 1 |                 } | 
| 379 | 1.02k |         } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); | 
| 380 |  |  | 
| 381 | 663 |         if ((r = parse_auth_reply(sig, ad, rp_id, reply, | 
| 382 | 663 |             (size_t)reply_len)) != FIDO_OK) { | 
| 383 | 40 |                 fido_log_debug("%s: parse_auth_reply", __func__); | 
| 384 | 40 |                 goto fail; | 
| 385 | 40 |         } | 
| 386 |  |  | 
| 387 | 703 | fail: | 
| 388 | 703 |         iso7816_free(&apdu); | 
| 389 | 703 |         freezero(reply, FIDO_MAXMSG); | 
| 390 |  |  | 
| 391 | 703 |         return (r); | 
| 392 | 663 | } | 
| 393 |  |  | 
| 394 |  | static int | 
| 395 |  | cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len, | 
| 396 |  |     fido_blob_t *cbor_blob) | 
| 397 | 233 | { | 
| 398 | 233 |         es256_pk_t      *pk = NULL; | 
| 399 | 233 |         cbor_item_t     *pk_cbor = NULL; | 
| 400 | 233 |         size_t           alloc_len; | 
| 401 | 233 |         int              ok = -1; | 
| 402 |  |  | 
| 403 |  |         /* only handle uncompressed points */ | 
| 404 | 233 |         if (ec_point_len != 65 || ec_point[0] != 0x04) { | 
| 405 | 7 |                 fido_log_debug("%s: unexpected format", __func__); | 
| 406 | 7 |                 goto fail; | 
| 407 | 7 |         } | 
| 408 |  |  | 
| 409 | 226 |         if ((pk = es256_pk_new()) == NULL || | 
| 410 | 226 |             es256_pk_set_x(pk, &ec_point[1]) < 0 || | 
| 411 | 226 |             es256_pk_set_y(pk, &ec_point[33]) < 0) { | 
| 412 | 1 |                 fido_log_debug("%s: es256_pk_set", __func__); | 
| 413 | 1 |                 goto fail; | 
| 414 | 1 |         } | 
| 415 |  |  | 
| 416 | 225 |         if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) { | 
| 417 | 2 |                 fido_log_debug("%s: es256_pk_encode", __func__); | 
| 418 | 2 |                 goto fail; | 
| 419 | 2 |         } | 
| 420 |  |  | 
| 421 | 223 |         if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr, | 
| 422 | 223 |             &alloc_len)) != 77) { | 
| 423 | 1 |                 fido_log_debug("%s: cbor_serialize_alloc", __func__); | 
| 424 | 1 |                 goto fail; | 
| 425 | 1 |         } | 
| 426 |  |  | 
| 427 | 222 |         ok = 0; | 
| 428 | 233 | fail: | 
| 429 | 233 |         es256_pk_free(&pk); | 
| 430 |  |  | 
| 431 | 233 |         if (pk_cbor) | 
| 432 | 223 |                 cbor_decref(&pk_cbor); | 
| 433 |  |  | 
| 434 | 233 |         return (ok); | 
| 435 | 222 | } | 
| 436 |  |  | 
| 437 |  | static int | 
| 438 |  | encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c, | 
| 439 |  |     const fido_blob_t *sig, fido_blob_t *out) | 
| 440 | 248 | { | 
| 441 | 248 |         cbor_item_t             *item = NULL; | 
| 442 | 248 |         cbor_item_t             *x5c_cbor = NULL; | 
| 443 | 248 |         const uint8_t            alg_cbor = (uint8_t)(-cose_alg - 1); | 
| 444 | 248 |         struct cbor_pair         kv[3]; | 
| 445 | 248 |         size_t                   alloc_len; | 
| 446 | 248 |         int                      ok = -1; | 
| 447 |  |  | 
| 448 | 248 |         memset(&kv, 0, sizeof(kv)); | 
| 449 | 248 |         memset(out, 0, sizeof(*out)); | 
| 450 |  |  | 
| 451 | 248 |         if ((item = cbor_new_definite_map(3)) == NULL) { | 
| 452 | 1 |                 fido_log_debug("%s: cbor_new_definite_map", __func__); | 
| 453 | 1 |                 goto fail; | 
| 454 | 1 |         } | 
| 455 |  |  | 
| 456 | 247 |         if ((kv[0].key = cbor_build_string("alg")) == NULL || | 
| 457 | 247 |             (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL || | 
| 458 | 247 |             !cbor_map_add(item, kv[0])) { | 
| 459 | 4 |                 fido_log_debug("%s: alg", __func__); | 
| 460 | 4 |                 goto fail; | 
| 461 | 4 |         } | 
| 462 |  |  | 
| 463 | 243 |         if ((kv[1].key = cbor_build_string("sig")) == NULL || | 
| 464 | 243 |             (kv[1].value = fido_blob_encode(sig)) == NULL || | 
| 465 | 243 |             !cbor_map_add(item, kv[1])) { | 
| 466 | 3 |                 fido_log_debug("%s: sig", __func__); | 
| 467 | 3 |                 goto fail; | 
| 468 | 3 |         } | 
| 469 |  |  | 
| 470 | 240 |         if ((kv[2].key = cbor_build_string("x5c")) == NULL || | 
| 471 | 240 |             (kv[2].value = cbor_new_definite_array(1)) == NULL || | 
| 472 | 240 |             (x5c_cbor = fido_blob_encode(x5c)) == NULL || | 
| 473 | 240 |             !cbor_array_push(kv[2].value, x5c_cbor) || | 
| 474 | 240 |             !cbor_map_add(item, kv[2])) { | 
| 475 | 6 |                 fido_log_debug("%s: x5c", __func__); | 
| 476 | 6 |                 goto fail; | 
| 477 | 6 |         } | 
| 478 |  |  | 
| 479 | 234 |         if ((out->len = cbor_serialize_alloc(item, &out->ptr, | 
| 480 | 234 |             &alloc_len)) == 0) { | 
| 481 | 1 |                 fido_log_debug("%s: cbor_serialize_alloc", __func__); | 
| 482 | 1 |                 goto fail; | 
| 483 | 1 |         } | 
| 484 |  |  | 
| 485 | 233 |         ok = 0; | 
| 486 | 248 | fail: | 
| 487 | 248 |         if (item != NULL) | 
| 488 | 247 |                 cbor_decref(&item); | 
| 489 | 248 |         if (x5c_cbor != NULL) | 
| 490 | 236 |                 cbor_decref(&x5c_cbor); | 
| 491 |  |  | 
| 492 | 992 |         for (size_t i = 0; i < nitems(kv); i++) { | 
| 493 | 744 |                 if (kv[i].key) | 
| 494 | 727 |                         cbor_decref(&kv[i].key); | 
| 495 | 744 |                 if (kv[i].value) | 
| 496 | 722 |                         cbor_decref(&kv[i].value); | 
| 497 | 744 |         } | 
| 498 |  |  | 
| 499 | 248 |         return (ok); | 
| 500 | 233 | } | 
| 501 |  |  | 
| 502 |  | static int | 
| 503 |  | encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len, | 
| 504 |  |     const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out) | 
| 505 | 233 | { | 
| 506 | 233 |         fido_authdata_t          authdata; | 
| 507 | 233 |         fido_attcred_raw_t       attcred_raw; | 
| 508 | 233 |         fido_blob_t              pk_blob; | 
| 509 | 233 |         fido_blob_t              authdata_blob; | 
| 510 | 233 |         cbor_item_t             *authdata_cbor = NULL; | 
| 511 | 233 |         unsigned char           *ptr; | 
| 512 | 233 |         size_t                   len; | 
| 513 | 233 |         size_t                   alloc_len; | 
| 514 | 233 |         int                      ok = -1; | 
| 515 |  |  | 
| 516 | 233 |         memset(&pk_blob, 0, sizeof(pk_blob)); | 
| 517 | 233 |         memset(&authdata, 0, sizeof(authdata)); | 
| 518 | 233 |         memset(&authdata_blob, 0, sizeof(authdata_blob)); | 
| 519 | 233 |         memset(out, 0, sizeof(*out)); | 
| 520 |  |  | 
| 521 | 233 |         if (rp_id == NULL) { | 
| 522 | 0 |                 fido_log_debug("%s: NULL rp_id", __func__); | 
| 523 | 0 |                 goto fail; | 
| 524 | 0 |         } | 
| 525 |  |  | 
| 526 | 233 |         if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) { | 
| 527 | 11 |                 fido_log_debug("%s: cbor_blob_from_ec_point", __func__); | 
| 528 | 11 |                 goto fail; | 
| 529 | 11 |         } | 
| 530 |  |  | 
| 531 | 222 |         if (SHA256((const void *)rp_id, strlen(rp_id), | 
| 532 | 222 |             authdata.rp_id_hash) != authdata.rp_id_hash) { | 
| 533 | 1 |                 fido_log_debug("%s: sha256", __func__); | 
| 534 | 1 |                 goto fail; | 
| 535 | 1 |         } | 
| 536 |  |  | 
| 537 | 221 |         authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT); | 
| 538 | 221 |         authdata.sigcount = 0; | 
| 539 |  |  | 
| 540 | 221 |         memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid)); | 
| 541 | 221 |         attcred_raw.id_len = htobe16(kh_len); | 
| 542 |  |  | 
| 543 | 221 |         len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) + | 
| 544 | 221 |             kh_len + pk_blob.len; | 
| 545 | 221 |         ptr = authdata_blob.ptr = calloc(1, authdata_blob.len); | 
| 546 |  |  | 
| 547 | 221 |         fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len); | 
| 548 |  |  | 
| 549 | 221 |         if (authdata_blob.ptr == NULL) | 
| 550 | 1 |                 goto fail; | 
| 551 |  |  | 
| 552 | 220 |         if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 || | 
| 553 | 220 |             fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 || | 
| 554 | 220 |             fido_buf_write(&ptr, &len, kh, kh_len) < 0 || | 
| 555 | 220 |             fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) { | 
| 556 | 0 |                 fido_log_debug("%s: fido_buf_write", __func__); | 
| 557 | 0 |                 goto fail; | 
| 558 | 0 |         } | 
| 559 |  |  | 
| 560 | 220 |         if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) { | 
| 561 | 1 |                 fido_log_debug("%s: fido_blob_encode", __func__); | 
| 562 | 1 |                 goto fail; | 
| 563 | 1 |         } | 
| 564 |  |  | 
| 565 | 219 |         if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr, | 
| 566 | 219 |             &alloc_len)) == 0) { | 
| 567 | 1 |                 fido_log_debug("%s: cbor_serialize_alloc", __func__); | 
| 568 | 1 |                 goto fail; | 
| 569 | 1 |         } | 
| 570 |  |  | 
| 571 | 218 |         ok = 0; | 
| 572 | 233 | fail: | 
| 573 | 233 |         if (authdata_cbor) | 
| 574 | 219 |                 cbor_decref(&authdata_cbor); | 
| 575 |  |  | 
| 576 | 233 |         fido_blob_reset(&pk_blob); | 
| 577 | 233 |         fido_blob_reset(&authdata_blob); | 
| 578 |  |  | 
| 579 | 233 |         return (ok); | 
| 580 | 218 | } | 
| 581 |  |  | 
| 582 |  | static int | 
| 583 |  | parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len) | 
| 584 | 661 | { | 
| 585 | 661 |         fido_blob_t      x5c; | 
| 586 | 661 |         fido_blob_t      sig; | 
| 587 | 661 |         fido_blob_t      ad; | 
| 588 | 661 |         fido_blob_t      stmt; | 
| 589 | 661 |         uint8_t          dummy; | 
| 590 | 661 |         uint8_t          pubkey[65]; | 
| 591 | 661 |         uint8_t          kh_len = 0; | 
| 592 | 661 |         uint8_t         *kh = NULL; | 
| 593 | 661 |         int              r; | 
| 594 |  |  | 
| 595 | 661 |         memset(&x5c, 0, sizeof(x5c)); | 
| 596 | 661 |         memset(&sig, 0, sizeof(sig)); | 
| 597 | 661 |         memset(&ad, 0, sizeof(ad)); | 
| 598 | 661 |         memset(&stmt, 0, sizeof(stmt)); | 
| 599 | 661 |         r = FIDO_ERR_RX; | 
| 600 |  |  | 
| 601 |  |         /* status word */ | 
| 602 | 661 |         if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) { | 
| 603 | 54 |                 fido_log_debug("%s: unexpected sw", __func__); | 
| 604 | 54 |                 goto fail; | 
| 605 | 54 |         } | 
| 606 |  |  | 
| 607 | 607 |         len -= 2; | 
| 608 |  |  | 
| 609 |  |         /* reserved byte */ | 
| 610 | 607 |         if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 || | 
| 611 | 607 |             dummy != 0x05) { | 
| 612 | 4 |                 fido_log_debug("%s: reserved byte", __func__); | 
| 613 | 4 |                 goto fail; | 
| 614 | 4 |         } | 
| 615 |  |  | 
| 616 |  |         /* pubkey + key handle */ | 
| 617 | 603 |         if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 || | 
| 618 | 603 |             fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 || | 
| 619 | 603 |             (kh = calloc(1, kh_len)) == NULL || | 
| 620 | 603 |             fido_buf_read(&reply, &len, kh, kh_len) < 0) { | 
| 621 | 3 |                 fido_log_debug("%s: fido_buf_read", __func__); | 
| 622 | 3 |                 goto fail; | 
| 623 | 3 |         } | 
| 624 |  |  | 
| 625 |  |         /* x5c + sig */ | 
| 626 | 600 |         if (x5c_get(&x5c, &reply, &len) < 0 || | 
| 627 | 600 |             sig_get(&sig, &reply, &len) < 0) { | 
| 628 | 352 |                 fido_log_debug("%s: x5c || sig", __func__); | 
| 629 | 352 |                 goto fail; | 
| 630 | 352 |         } | 
| 631 |  |  | 
| 632 |  |         /* attstmt */ | 
| 633 | 248 |         if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) { | 
| 634 | 15 |                 fido_log_debug("%s: encode_cred_attstmt", __func__); | 
| 635 | 15 |                 goto fail; | 
| 636 | 15 |         } | 
| 637 |  |  | 
| 638 |  |         /* authdata */ | 
| 639 | 233 |         if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey, | 
| 640 | 233 |             sizeof(pubkey), &ad) < 0) { | 
| 641 | 15 |                 fido_log_debug("%s: encode_cred_authdata", __func__); | 
| 642 | 15 |                 goto fail; | 
| 643 | 15 |         } | 
| 644 |  |  | 
| 645 | 218 |         if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK || | 
| 646 | 218 |             fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK || | 
| 647 | 218 |             fido_cred_set_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) { | 
| 648 | 10 |                 fido_log_debug("%s: fido_cred_set", __func__); | 
| 649 | 10 |                 r = FIDO_ERR_INTERNAL; | 
| 650 | 10 |                 goto fail; | 
| 651 | 10 |         } | 
| 652 |  |  | 
| 653 | 208 |         r = FIDO_OK; | 
| 654 | 661 | fail: | 
| 655 | 661 |         freezero(kh, kh_len); | 
| 656 | 661 |         fido_blob_reset(&x5c); | 
| 657 | 661 |         fido_blob_reset(&sig); | 
| 658 | 661 |         fido_blob_reset(&ad); | 
| 659 | 661 |         fido_blob_reset(&stmt); | 
| 660 |  |  | 
| 661 | 661 |         return (r); | 
| 662 | 208 | } | 
| 663 |  |  | 
| 664 |  | int | 
| 665 |  | u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms) | 
| 666 | 997 | { | 
| 667 | 997 |         iso7816_apdu_t  *apdu = NULL; | 
| 668 | 997 |         unsigned char    rp_id_hash[SHA256_DIGEST_LENGTH]; | 
| 669 | 997 |         unsigned char   *reply = NULL; | 
| 670 | 997 |         int              reply_len; | 
| 671 | 997 |         int              found; | 
| 672 | 997 |         int              r; | 
| 673 |  |  | 
| 674 | 997 |         if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) { | 
| 675 | 9 |                 fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk, | 
| 676 | 9 |                     cred->uv); | 
| 677 | 9 |                 return (FIDO_ERR_UNSUPPORTED_OPTION); | 
| 678 | 9 |         } | 
| 679 |  |  | 
| 680 | 988 |         if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL || | 
| 681 | 988 |             cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) { | 
| 682 | 50 |                 fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__, | 
| 683 | 50 |                     cred->type, (void *)cred->cdh.ptr, cred->cdh.len); | 
| 684 | 50 |                 return (FIDO_ERR_INVALID_ARGUMENT); | 
| 685 | 50 |         } | 
| 686 |  |  | 
| 687 | 948 |         for (size_t i = 0; i < cred->excl.len; i++) { | 
| 688 | 248 |                 if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i], | 
| 689 | 248 |                     &found, ms)) != FIDO_OK) { | 
| 690 | 182 |                         fido_log_debug("%s: key_lookup", __func__); | 
| 691 | 182 |                         return (r); | 
| 692 | 182 |                 } | 
| 693 | 66 |                 if (found) { | 
| 694 | 56 |                         if ((r = send_dummy_register(dev, ms)) != FIDO_OK) { | 
| 695 | 33 |                                 fido_log_debug("%s: send_dummy_register", | 
| 696 | 33 |                                     __func__); | 
| 697 | 33 |                                 return (r); | 
| 698 | 33 |                         } | 
| 699 | 23 |                         return (FIDO_ERR_CREDENTIAL_EXCLUDED); | 
| 700 | 56 |                 } | 
| 701 | 66 |         } | 
| 702 |  |  | 
| 703 | 700 |         memset(&rp_id_hash, 0, sizeof(rp_id_hash)); | 
| 704 |  |  | 
| 705 | 700 |         if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id), | 
| 706 | 700 |             rp_id_hash) != rp_id_hash) { | 
| 707 | 1 |                 fido_log_debug("%s: sha256", __func__); | 
| 708 | 1 |                 return (FIDO_ERR_INTERNAL); | 
| 709 | 1 |         } | 
| 710 |  |  | 
| 711 | 699 |         if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * | 
| 712 | 699 |             SHA256_DIGEST_LENGTH)) == NULL || | 
| 713 | 699 |             iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 || | 
| 714 | 699 |             iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) { | 
| 715 | 2 |                 fido_log_debug("%s: iso7816", __func__); | 
| 716 | 2 |                 r = FIDO_ERR_INTERNAL; | 
| 717 | 2 |                 goto fail; | 
| 718 | 2 |         } | 
| 719 |  |  | 
| 720 | 697 |         if ((reply = malloc(FIDO_MAXMSG)) == NULL) { | 
| 721 | 1 |                 fido_log_debug("%s: malloc", __func__); | 
| 722 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 723 | 1 |                 goto fail; | 
| 724 | 1 |         } | 
| 725 |  |  | 
| 726 | 1.36k |         do { | 
| 727 | 1.36k |                 if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), | 
| 728 | 1.36k |                     iso7816_len(apdu), ms) < 0) { | 
| 729 | 4 |                         fido_log_debug("%s: fido_tx", __func__); | 
| 730 | 4 |                         r = FIDO_ERR_TX; | 
| 731 | 4 |                         goto fail; | 
| 732 | 4 |                 } | 
| 733 | 1.36k |                 if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, | 
| 734 | 1.36k |                     FIDO_MAXMSG, ms)) < 2) { | 
| 735 | 30 |                         fido_log_debug("%s: fido_rx", __func__); | 
| 736 | 30 |                         r = FIDO_ERR_RX; | 
| 737 | 30 |                         goto fail; | 
| 738 | 30 |                 } | 
| 739 | 1.33k |                 if (delay_ms(U2F_PACE_MS, ms) != 0) { | 
| 740 | 1 |                         fido_log_debug("%s: delay_ms", __func__); | 
| 741 | 1 |                         r = FIDO_ERR_RX; | 
| 742 | 1 |                         goto fail; | 
| 743 | 1 |                 } | 
| 744 | 1.33k |         } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED); | 
| 745 |  |  | 
| 746 | 661 |         if ((r = parse_register_reply(cred, reply, | 
| 747 | 661 |             (size_t)reply_len)) != FIDO_OK) { | 
| 748 | 453 |                 fido_log_debug("%s: parse_register_reply", __func__); | 
| 749 | 453 |                 goto fail; | 
| 750 | 453 |         } | 
| 751 | 699 | fail: | 
| 752 | 699 |         iso7816_free(&apdu); | 
| 753 | 699 |         freezero(reply, FIDO_MAXMSG); | 
| 754 |  |  | 
| 755 | 699 |         return (r); | 
| 756 | 661 | } | 
| 757 |  |  | 
| 758 |  | static int | 
| 759 |  | u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id, | 
| 760 |  |     fido_assert_t *fa, size_t idx, int *ms) | 
| 761 | 1.56k | { | 
| 762 | 1.56k |         fido_blob_t     sig; | 
| 763 | 1.56k |         fido_blob_t     ad; | 
| 764 | 1.56k |         int             found; | 
| 765 | 1.56k |         int             r; | 
| 766 |  |  | 
| 767 | 1.56k |         memset(&sig, 0, sizeof(sig)); | 
| 768 | 1.56k |         memset(&ad, 0, sizeof(ad)); | 
| 769 |  |  | 
| 770 | 1.56k |         if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) { | 
| 771 | 745 |                 fido_log_debug("%s: key_lookup", __func__); | 
| 772 | 745 |                 goto fail; | 
| 773 | 745 |         } | 
| 774 |  |  | 
| 775 | 818 |         if (!found) { | 
| 776 | 10 |                 fido_log_debug("%s: not found", __func__); | 
| 777 | 10 |                 r = FIDO_ERR_CREDENTIAL_EXCLUDED; | 
| 778 | 10 |                 goto fail; | 
| 779 | 10 |         } | 
| 780 |  |  | 
| 781 | 808 |         if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) { | 
| 782 | 1 |                 fido_log_debug("%s: fido_blob_set", __func__); | 
| 783 | 1 |                 r = FIDO_ERR_INTERNAL; | 
| 784 | 1 |                 goto fail; | 
| 785 | 1 |         } | 
| 786 |  |  | 
| 787 | 807 |         if (fa->up == FIDO_OPT_FALSE) { | 
| 788 | 104 |                 fido_log_debug("%s: checking for key existence only", __func__); | 
| 789 | 104 |                 r = FIDO_ERR_USER_PRESENCE_REQUIRED; | 
| 790 | 104 |                 goto fail; | 
| 791 | 104 |         } | 
| 792 |  |  | 
| 793 | 703 |         if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad, | 
| 794 | 703 |             ms)) != FIDO_OK) { | 
| 795 | 80 |                 fido_log_debug("%s: do_auth", __func__); | 
| 796 | 80 |                 goto fail; | 
| 797 | 80 |         } | 
| 798 |  |  | 
| 799 | 623 |         if (fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK || | 
| 800 | 623 |             fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) { | 
| 801 | 8 |                 fido_log_debug("%s: fido_assert_set", __func__); | 
| 802 | 8 |                 r = FIDO_ERR_INTERNAL; | 
| 803 | 8 |                 goto fail; | 
| 804 | 8 |         } | 
| 805 |  |  | 
| 806 | 615 |         r = FIDO_OK; | 
| 807 | 1.56k | fail: | 
| 808 | 1.56k |         fido_blob_reset(&sig); | 
| 809 | 1.56k |         fido_blob_reset(&ad); | 
| 810 |  |  | 
| 811 | 1.56k |         return (r); | 
| 812 | 615 | } | 
| 813 |  |  | 
| 814 |  | int | 
| 815 |  | u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms) | 
| 816 | 918 | { | 
| 817 | 918 |         size_t  nfound = 0; | 
| 818 | 918 |         size_t  nauth_ok = 0; | 
| 819 | 918 |         int     r; | 
| 820 |  |  | 
| 821 | 918 |         if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) { | 
| 822 | 72 |                 fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv, | 
| 823 | 72 |                     (void *)fa->allow_list.ptr); | 
| 824 | 72 |                 return (FIDO_ERR_UNSUPPORTED_OPTION); | 
| 825 | 72 |         } | 
| 826 |  |  | 
| 827 | 846 |         if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) { | 
| 828 | 1 |                 fido_log_debug("%s: fido_assert_set_count", __func__); | 
| 829 | 1 |                 return (r); | 
| 830 | 1 |         } | 
| 831 |  |  | 
| 832 | 1.57k |         for (size_t i = 0; i < fa->allow_list.len; i++) { | 
| 833 | 1.56k |                 switch ((r = u2f_authenticate_single(dev, | 
| 834 | 1.56k |                     &fa->allow_list.ptr[i], fa, nfound, ms))) { | 
| 835 | 615 |                 case FIDO_OK: | 
| 836 | 615 |                         nauth_ok++; | 
| 837 | 615 |                         FALLTHROUGH | 
| 838 | 719 |                 case FIDO_ERR_USER_PRESENCE_REQUIRED: | 
| 839 | 719 |                         nfound++; | 
| 840 | 719 |                         break; | 
| 841 | 844 |                 default: | 
| 842 | 844 |                         if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) { | 
| 843 | 834 |                                 fido_log_debug("%s: u2f_authenticate_single", | 
| 844 | 834 |                                     __func__); | 
| 845 | 834 |                                 return (r); | 
| 846 | 834 |                         } | 
| 847 |  |                         /* ignore credentials that don't exist */ | 
| 848 | 1.56k |                 } | 
| 849 | 1.56k |         } | 
| 850 |  |  | 
| 851 | 11 |         fa->stmt_len = nfound; | 
| 852 |  |  | 
| 853 | 11 |         if (nfound == 0) | 
| 854 | 2 |                 return (FIDO_ERR_NO_CREDENTIALS); | 
| 855 | 9 |         if (nauth_ok == 0) | 
| 856 | 1 |                 return (FIDO_ERR_USER_PRESENCE_REQUIRED); | 
| 857 |  |  | 
| 858 | 8 |         return (FIDO_OK); | 
| 859 | 9 | } | 
| 860 |  |  | 
| 861 |  | int | 
| 862 |  | u2f_get_touch_begin(fido_dev_t *dev, int *ms) | 
| 863 | 3.29k | { | 
| 864 | 3.29k |         iso7816_apdu_t  *apdu = NULL; | 
| 865 | 3.29k |         const char      *clientdata = FIDO_DUMMY_CLIENTDATA; | 
| 866 | 3.29k |         const char      *rp_id = FIDO_DUMMY_RP_ID; | 
| 867 | 3.29k |         unsigned char   *reply = NULL; | 
| 868 | 3.29k |         unsigned char    clientdata_hash[SHA256_DIGEST_LENGTH]; | 
| 869 | 3.29k |         unsigned char    rp_id_hash[SHA256_DIGEST_LENGTH]; | 
| 870 | 3.29k |         int              r; | 
| 871 |  |  | 
| 872 | 3.29k |         memset(&clientdata_hash, 0, sizeof(clientdata_hash)); | 
| 873 | 3.29k |         memset(&rp_id_hash, 0, sizeof(rp_id_hash)); | 
| 874 |  |  | 
| 875 | 3.29k |         if (SHA256((const void *)clientdata, strlen(clientdata), | 
| 876 | 3.29k |             clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id, | 
| 877 | 3.28k |             strlen(rp_id), rp_id_hash) != rp_id_hash) { | 
| 878 | 15 |                 fido_log_debug("%s: sha256", __func__); | 
| 879 | 15 |                 return (FIDO_ERR_INTERNAL); | 
| 880 | 15 |         } | 
| 881 |  |  | 
| 882 | 3.27k |         if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * | 
| 883 | 3.27k |             SHA256_DIGEST_LENGTH)) == NULL || | 
| 884 | 3.27k |             iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 || | 
| 885 | 3.27k |             iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) { | 
| 886 | 12 |                 fido_log_debug("%s: iso7816", __func__); | 
| 887 | 12 |                 r = FIDO_ERR_INTERNAL; | 
| 888 | 12 |                 goto fail; | 
| 889 | 12 |         } | 
| 890 |  |  | 
| 891 | 3.26k |         if ((reply = malloc(FIDO_MAXMSG)) == NULL) { | 
| 892 | 11 |                 fido_log_debug("%s: malloc", __func__); | 
| 893 | 11 |                 r =  FIDO_ERR_INTERNAL; | 
| 894 | 11 |                 goto fail; | 
| 895 | 11 |         } | 
| 896 |  |  | 
| 897 | 3.25k |         if (dev->attr.flags & FIDO_CAP_WINK) { | 
| 898 | 1.84k |                 fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms); | 
| 899 | 1.84k |                 fido_rx(dev, CTAP_CMD_WINK, reply, FIDO_MAXMSG, ms); | 
| 900 | 1.84k |         } | 
| 901 |  |  | 
| 902 | 3.25k |         if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), | 
| 903 | 3.25k |             iso7816_len(apdu), ms) < 0) { | 
| 904 | 268 |                 fido_log_debug("%s: fido_tx", __func__); | 
| 905 | 268 |                 r = FIDO_ERR_TX; | 
| 906 | 268 |                 goto fail; | 
| 907 | 268 |         } | 
| 908 |  |  | 
| 909 | 2.98k |         r = FIDO_OK; | 
| 910 | 3.27k | fail: | 
| 911 | 3.27k |         iso7816_free(&apdu); | 
| 912 | 3.27k |         freezero(reply, FIDO_MAXMSG); | 
| 913 |  |  | 
| 914 | 3.27k |         return (r); | 
| 915 | 2.98k | } | 
| 916 |  |  | 
| 917 |  | int | 
| 918 |  | u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms) | 
| 919 | 3.26k | { | 
| 920 | 3.26k |         unsigned char   *reply; | 
| 921 | 3.26k |         int              reply_len; | 
| 922 | 3.26k |         int              r; | 
| 923 |  |  | 
| 924 | 3.26k |         if ((reply = malloc(FIDO_MAXMSG)) == NULL) { | 
| 925 | 9 |                 fido_log_debug("%s: malloc", __func__); | 
| 926 | 9 |                 r =  FIDO_ERR_INTERNAL; | 
| 927 | 9 |                 goto out; | 
| 928 | 9 |         } | 
| 929 |  |  | 
| 930 | 3.25k |         if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, | 
| 931 | 3.25k |             ms)) < 2) { | 
| 932 | 2.95k |                 fido_log_debug("%s: fido_rx", __func__); | 
| 933 | 2.95k |                 r = FIDO_OK; /* ignore */ | 
| 934 | 2.95k |                 goto out; | 
| 935 | 2.95k |         } | 
| 936 |  |  | 
| 937 | 304 |         switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) { | 
| 938 | 28 |         case SW_CONDITIONS_NOT_SATISFIED: | 
| 939 | 28 |                 if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) { | 
| 940 | 7 |                         fido_log_debug("%s: u2f_get_touch_begin", __func__); | 
| 941 | 7 |                         goto out; | 
| 942 | 7 |                 } | 
| 943 | 21 |                 *touched = 0; | 
| 944 | 21 |                 break; | 
| 945 | 13 |         case SW_NO_ERROR: | 
| 946 | 13 |                 *touched = 1; | 
| 947 | 13 |                 break; | 
| 948 | 263 |         default: | 
| 949 | 263 |                 fido_log_debug("%s: unexpected sw", __func__); | 
| 950 | 263 |                 r = FIDO_ERR_RX; | 
| 951 | 263 |                 goto out; | 
| 952 | 304 |         } | 
| 953 |  |  | 
| 954 | 34 |         r = FIDO_OK; | 
| 955 | 3.26k | out: | 
| 956 | 3.26k |         freezero(reply, FIDO_MAXMSG); | 
| 957 |  |  | 
| 958 | 3.26k |         return (r); | 
| 959 | 34 | } |