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