1 /* 2 * Copyright (c) 2018 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 */ 6 7 #include <string.h> 8 9 #include "fido.h" 10 #include "fido/es256.h" 11 12 static int 13 parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg) 14 { 15 fido_blob_t *token = arg; 16 17 if (cbor_isa_uint(key) == false || 18 cbor_int_get_width(key) != CBOR_INT_8 || 19 cbor_get_uint8(key) != 2) { 20 fido_log_debug("%s: cbor type", __func__); 21 return (0); /* ignore */ 22 } 23 24 return (fido_blob_decode(val, token)); 25 } 26 27 #ifdef FIDO_UVTOKEN 28 static int 29 parse_uvtoken(const cbor_item_t *key, const cbor_item_t *val, void *arg) 30 { 31 return (parse_pintoken(key, val, arg)); 32 } 33 #endif /* FIDO_UVTOKEN */ 34 35 static int 36 fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin, 37 const fido_blob_t *ecdh, const es256_pk_t *pk) 38 { 39 fido_blob_t f; 40 fido_blob_t *p = NULL; 41 cbor_item_t *argv[6]; 42 int r; 43 44 memset(&f, 0, sizeof(f)); 45 memset(argv, 0, sizeof(argv)); 46 47 if ((p = fido_blob_new()) == NULL || fido_blob_set(p, 48 (const unsigned char *)pin, strlen(pin)) < 0) { 49 fido_log_debug("%s: fido_blob_set", __func__); 50 r = FIDO_ERR_INVALID_ARGUMENT; 51 goto fail; 52 } 53 54 if ((argv[0] = cbor_build_uint8(1)) == NULL || 55 (argv[1] = cbor_build_uint8(5)) == NULL || 56 (argv[2] = es256_pk_encode(pk, 1)) == NULL || 57 (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) { 58 fido_log_debug("%s: cbor encode", __func__); 59 r = FIDO_ERR_INTERNAL; 60 goto fail; 61 } 62 63 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), 64 &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { 65 fido_log_debug("%s: fido_tx", __func__); 66 r = FIDO_ERR_TX; 67 goto fail; 68 } 69 70 r = FIDO_OK; 71 fail: 72 cbor_vector_free(argv, nitems(argv)); 73 fido_blob_free(&p); 74 free(f.ptr); 75 76 return (r); 77 } 78 79 #ifdef FIDO_UVTOKEN 80 static int 81 fido_dev_get_uv_token_tx(fido_dev_t *dev, const es256_pk_t *pk) 82 { 83 fido_blob_t f; 84 cbor_item_t *argv[3]; 85 int r; 86 87 memset(&f, 0, sizeof(f)); 88 memset(argv, 0, sizeof(argv)); 89 90 if ((argv[0] = cbor_build_uint8(1)) == NULL || 91 (argv[1] = cbor_build_uint8(6)) == NULL || 92 (argv[2] = es256_pk_encode(pk, 1)) == NULL) { 93 fido_log_debug("%s: cbor encode", __func__); 94 r = FIDO_ERR_INTERNAL; 95 goto fail; 96 } 97 98 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), 99 &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { 100 fido_log_debug("%s: fido_tx", __func__); 101 r = FIDO_ERR_TX; 102 goto fail; 103 } 104 105 r = FIDO_OK; 106 fail: 107 cbor_vector_free(argv, nitems(argv)); 108 free(f.ptr); 109 110 return (r); 111 } 112 #endif /* FIDO_UVTOKEN */ 113 114 static int 115 fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, 116 fido_blob_t *token, int ms) 117 { 118 fido_blob_t *aes_token = NULL; 119 unsigned char reply[FIDO_MAXMSG]; 120 int reply_len; 121 int r; 122 123 if ((aes_token = fido_blob_new()) == NULL) { 124 r = FIDO_ERR_INTERNAL; 125 goto fail; 126 } 127 128 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 129 ms)) < 0) { 130 fido_log_debug("%s: fido_rx", __func__); 131 r = FIDO_ERR_RX; 132 goto fail; 133 } 134 135 if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token, 136 parse_pintoken)) != FIDO_OK) { 137 fido_log_debug("%s: parse_pintoken", __func__); 138 goto fail; 139 } 140 141 if (aes256_cbc_dec(ecdh, aes_token, token) < 0) { 142 fido_log_debug("%s: aes256_cbc_dec", __func__); 143 r = FIDO_ERR_RX; 144 goto fail; 145 } 146 147 r = FIDO_OK; 148 fail: 149 fido_blob_free(&aes_token); 150 151 return (r); 152 } 153 154 #ifdef FIDO_UVTOKEN 155 static int 156 fido_dev_get_uv_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, 157 fido_blob_t *token, int ms) 158 { 159 fido_blob_t *aes_token = NULL; 160 unsigned char reply[FIDO_MAXMSG]; 161 int reply_len; 162 int r; 163 164 if ((aes_token = fido_blob_new()) == NULL) { 165 r = FIDO_ERR_INTERNAL; 166 goto fail; 167 } 168 169 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 170 ms)) < 0) { 171 fido_log_debug("%s: fido_rx", __func__); 172 r = FIDO_ERR_RX; 173 goto fail; 174 } 175 176 if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token, 177 parse_uvtoken)) != FIDO_OK) { 178 fido_log_debug("%s: parse_uvtoken", __func__); 179 goto fail; 180 } 181 182 if (aes256_cbc_dec(ecdh, aes_token, token) < 0) { 183 fido_log_debug("%s: aes256_cbc_dec", __func__); 184 r = FIDO_ERR_RX; 185 goto fail; 186 } 187 188 r = FIDO_OK; 189 fail: 190 fido_blob_free(&aes_token); 191 192 return (r); 193 } 194 #endif /* FIDO_UVTOKEN */ 195 196 static int 197 fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin, 198 const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms) 199 { 200 int r; 201 202 #ifdef FIDO_UVTOKEN 203 if (getenv("FIDO_UVTOKEN") != NULL) { 204 if ((r = fido_dev_get_uv_token_tx(dev, pk)) != FIDO_OK || 205 (r = fido_dev_get_uv_token_rx(dev, ecdh, token, ms)) != FIDO_OK) 206 return (r); 207 } else { 208 if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK || 209 (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK) 210 return (r); 211 } 212 #else 213 if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK || 214 (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK) 215 return (r); 216 #endif 217 218 return (FIDO_OK); 219 } 220 221 int 222 fido_dev_get_pin_token(fido_dev_t *dev, const char *pin, 223 const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token) 224 { 225 return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1)); 226 } 227 228 static int 229 pad64(const char *pin, fido_blob_t **ppin) 230 { 231 size_t pin_len; 232 size_t ppin_len; 233 234 pin_len = strlen(pin); 235 if (pin_len < 4 || pin_len > 255) { 236 fido_log_debug("%s: invalid pin length", __func__); 237 return (FIDO_ERR_PIN_POLICY_VIOLATION); 238 } 239 240 if ((*ppin = fido_blob_new()) == NULL) 241 return (FIDO_ERR_INTERNAL); 242 243 ppin_len = (pin_len + 63U) & ~63U; 244 if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) { 245 fido_blob_free(ppin); 246 return (FIDO_ERR_INTERNAL); 247 } 248 249 memcpy((*ppin)->ptr, pin, pin_len); 250 (*ppin)->len = ppin_len; 251 252 return (FIDO_OK); 253 } 254 255 static int 256 fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin) 257 { 258 fido_blob_t f; 259 fido_blob_t *ppin = NULL; 260 fido_blob_t *ecdh = NULL; 261 fido_blob_t *opin = NULL; 262 cbor_item_t *argv[6]; 263 es256_pk_t *pk = NULL; 264 int r; 265 266 memset(&f, 0, sizeof(f)); 267 memset(argv, 0, sizeof(argv)); 268 269 if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin, 270 (const unsigned char *)oldpin, strlen(oldpin)) < 0) { 271 fido_log_debug("%s: fido_blob_set", __func__); 272 r = FIDO_ERR_INVALID_ARGUMENT; 273 goto fail; 274 } 275 276 if ((r = pad64(pin, &ppin)) != FIDO_OK) { 277 fido_log_debug("%s: pad64", __func__); 278 goto fail; 279 } 280 281 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { 282 fido_log_debug("%s: fido_do_ecdh", __func__); 283 goto fail; 284 } 285 286 if ((argv[0] = cbor_build_uint8(1)) == NULL || 287 (argv[1] = cbor_build_uint8(4)) == NULL || 288 (argv[2] = es256_pk_encode(pk, 1)) == NULL || 289 (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL || 290 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL || 291 (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) { 292 fido_log_debug("%s: cbor encode", __func__); 293 r = FIDO_ERR_INTERNAL; 294 goto fail; 295 } 296 297 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), 298 &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { 299 fido_log_debug("%s: fido_tx", __func__); 300 r = FIDO_ERR_TX; 301 goto fail; 302 } 303 304 r = FIDO_OK; 305 fail: 306 cbor_vector_free(argv, nitems(argv)); 307 es256_pk_free(&pk); 308 fido_blob_free(&ppin); 309 fido_blob_free(&ecdh); 310 fido_blob_free(&opin); 311 free(f.ptr); 312 313 return (r); 314 315 } 316 317 static int 318 fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin) 319 { 320 fido_blob_t f; 321 fido_blob_t *ppin = NULL; 322 fido_blob_t *ecdh = NULL; 323 cbor_item_t *argv[5]; 324 es256_pk_t *pk = NULL; 325 int r; 326 327 memset(&f, 0, sizeof(f)); 328 memset(argv, 0, sizeof(argv)); 329 330 if ((r = pad64(pin, &ppin)) != FIDO_OK) { 331 fido_log_debug("%s: pad64", __func__); 332 goto fail; 333 } 334 335 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { 336 fido_log_debug("%s: fido_do_ecdh", __func__); 337 goto fail; 338 } 339 340 if ((argv[0] = cbor_build_uint8(1)) == NULL || 341 (argv[1] = cbor_build_uint8(3)) == NULL || 342 (argv[2] = es256_pk_encode(pk, 1)) == NULL || 343 (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL || 344 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) { 345 fido_log_debug("%s: cbor encode", __func__); 346 r = FIDO_ERR_INTERNAL; 347 goto fail; 348 } 349 350 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), 351 &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { 352 fido_log_debug("%s: fido_tx", __func__); 353 r = FIDO_ERR_TX; 354 goto fail; 355 } 356 357 r = FIDO_OK; 358 fail: 359 cbor_vector_free(argv, nitems(argv)); 360 es256_pk_free(&pk); 361 fido_blob_free(&ppin); 362 fido_blob_free(&ecdh); 363 free(f.ptr); 364 365 return (r); 366 } 367 368 static int 369 fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin, 370 int ms) 371 { 372 int r; 373 374 if (oldpin != NULL) { 375 if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) { 376 fido_log_debug("%s: fido_dev_change_pin_tx", __func__); 377 return (r); 378 } 379 } else { 380 if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) { 381 fido_log_debug("%s: fido_dev_set_pin_tx", __func__); 382 return (r); 383 } 384 } 385 386 if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) { 387 fido_log_debug("%s: fido_rx_cbor_status", __func__); 388 return (r); 389 } 390 391 return (FIDO_OK); 392 } 393 394 int 395 fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin) 396 { 397 return (fido_dev_set_pin_wait(dev, pin, oldpin, -1)); 398 } 399 400 static int 401 parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg) 402 { 403 int *retries = arg; 404 uint64_t n; 405 406 if (cbor_isa_uint(key) == false || 407 cbor_int_get_width(key) != CBOR_INT_8 || 408 cbor_get_uint8(key) != 3) { 409 fido_log_debug("%s: cbor type", __func__); 410 return (0); /* ignore */ 411 } 412 413 if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) { 414 fido_log_debug("%s: cbor_decode_uint64", __func__); 415 return (-1); 416 } 417 418 *retries = (int)n; 419 420 return (0); 421 } 422 423 static int 424 fido_dev_get_retry_count_tx(fido_dev_t *dev) 425 { 426 fido_blob_t f; 427 cbor_item_t *argv[2]; 428 int r; 429 430 memset(&f, 0, sizeof(f)); 431 memset(argv, 0, sizeof(argv)); 432 433 if ((argv[0] = cbor_build_uint8(1)) == NULL || 434 (argv[1] = cbor_build_uint8(1)) == NULL) { 435 r = FIDO_ERR_INTERNAL; 436 goto fail; 437 } 438 439 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), 440 &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { 441 fido_log_debug("%s: fido_tx", __func__); 442 r = FIDO_ERR_TX; 443 goto fail; 444 } 445 446 r = FIDO_OK; 447 fail: 448 cbor_vector_free(argv, nitems(argv)); 449 free(f.ptr); 450 451 return (r); 452 } 453 454 static int 455 fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms) 456 { 457 unsigned char reply[FIDO_MAXMSG]; 458 int reply_len; 459 int r; 460 461 *retries = 0; 462 463 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 464 ms)) < 0) { 465 fido_log_debug("%s: fido_rx", __func__); 466 return (FIDO_ERR_RX); 467 } 468 469 if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries, 470 parse_retry_count)) != FIDO_OK) { 471 fido_log_debug("%s: parse_retry_count", __func__); 472 return (r); 473 } 474 475 return (FIDO_OK); 476 } 477 478 static int 479 fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms) 480 { 481 int r; 482 483 if ((r = fido_dev_get_retry_count_tx(dev)) != FIDO_OK || 484 (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK) 485 return (r); 486 487 return (FIDO_OK); 488 } 489 490 int 491 fido_dev_get_retry_count(fido_dev_t *dev, int *retries) 492 { 493 return (fido_dev_get_retry_count_wait(dev, retries, -1)); 494 } 495 496 int 497 cbor_add_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data, 498 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, 499 cbor_item_t **auth, cbor_item_t **opt) 500 { 501 fido_blob_t *token = NULL; 502 int r; 503 504 if ((token = fido_blob_new()) == NULL) { 505 r = FIDO_ERR_INTERNAL; 506 goto fail; 507 } 508 509 if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) { 510 fido_log_debug("%s: fido_dev_get_pin_token", __func__); 511 goto fail; 512 } 513 514 if ((*auth = cbor_encode_pin_auth(token, hmac_data)) == NULL || 515 (*opt = cbor_encode_pin_opt()) == NULL) { 516 fido_log_debug("%s: cbor encode", __func__); 517 r = FIDO_ERR_INTERNAL; 518 goto fail; 519 } 520 521 r = FIDO_OK; 522 fail: 523 fido_blob_free(&token); 524 525 return (r); 526 } 527