1 /* 2 * Copyright (c) 2020 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 "fido.h" 8 #include "fido/config.h" 9 #include "fido/es256.h" 10 11 #define CMD_ENABLE_ENTATTEST 0x01 12 #define CMD_TOGGLE_ALWAYS_UV 0x02 13 #define CMD_SET_PIN_MINLEN 0x03 14 15 static int 16 config_prepare_hmac(uint8_t subcmd, const cbor_item_t *item, fido_blob_t *hmac) 17 { 18 uint8_t prefix[32 + 2 * sizeof(uint8_t)], cbor[128]; 19 size_t cbor_len; 20 21 memset(prefix, 0xff, sizeof(prefix)); 22 prefix[sizeof(prefix) - 2] = CTAP_CBOR_CONFIG; 23 prefix[sizeof(prefix) - 1] = subcmd; 24 25 if ((cbor_len = cbor_serialize(item, cbor, sizeof(cbor))) == 0) { 26 fido_log_debug("%s: cbor_serialize", __func__); 27 return -1; 28 } 29 if ((hmac->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) { 30 fido_log_debug("%s: malloc", __func__); 31 return -1; 32 } 33 memcpy(hmac->ptr, prefix, sizeof(prefix)); 34 memcpy(hmac->ptr + sizeof(prefix), cbor, cbor_len); 35 hmac->len = cbor_len + sizeof(prefix); 36 37 return 0; 38 } 39 40 static int 41 config_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **paramv, size_t paramc, 42 const char *pin) 43 { 44 cbor_item_t *argv[4]; 45 es256_pk_t *pk = NULL; 46 fido_blob_t *ecdh = NULL, f, hmac; 47 const uint8_t cmd = CTAP_CBOR_CONFIG; 48 int r = FIDO_ERR_INTERNAL; 49 50 memset(&f, 0, sizeof(f)); 51 memset(&hmac, 0, sizeof(hmac)); 52 memset(&argv, 0, sizeof(argv)); 53 54 /* subCommand */ 55 if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) { 56 fido_log_debug("%s: cbor encode", __func__); 57 goto fail; 58 } 59 60 /* pinProtocol, pinAuth */ 61 if (pin != NULL || (fido_dev_supports_permissions(dev) && 62 fido_dev_has_uv(dev))) { 63 if ((argv[1] = cbor_flatten_vector(paramv, paramc)) == NULL) { 64 fido_log_debug("%s: cbor_flatten_vector", __func__); 65 goto fail; 66 } 67 if (config_prepare_hmac(subcmd, argv[1], &hmac) < 0) { 68 fido_log_debug("%s: config_prepare_hmac", __func__); 69 goto fail; 70 } 71 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { 72 fido_log_debug("%s: fido_do_ecdh", __func__); 73 goto fail; 74 } 75 if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin, 76 NULL, &argv[3], &argv[2])) != FIDO_OK) { 77 fido_log_debug("%s: cbor_add_uv_params", __func__); 78 goto fail; 79 } 80 } 81 82 /* framing and transmission */ 83 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 || 84 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { 85 fido_log_debug("%s: fido_tx", __func__); 86 r = FIDO_ERR_TX; 87 goto fail; 88 } 89 90 r = FIDO_OK; 91 fail: 92 cbor_vector_free(argv, nitems(argv)); 93 es256_pk_free(&pk); 94 fido_blob_free(&ecdh); 95 free(f.ptr); 96 free(hmac.ptr); 97 98 return r; 99 } 100 101 static int 102 config_enable_entattest_wait(fido_dev_t *dev, const char *pin, int ms) 103 { 104 int r; 105 106 if ((r = config_tx(dev, CMD_ENABLE_ENTATTEST, NULL, 0, pin)) != FIDO_OK) 107 return r; 108 109 return fido_rx_cbor_status(dev, ms); 110 } 111 112 int 113 fido_dev_enable_entattest(fido_dev_t *dev, const char *pin) 114 { 115 return (config_enable_entattest_wait(dev, pin, -1)); 116 } 117 118 static int 119 config_toggle_always_uv_wait(fido_dev_t *dev, const char *pin, int ms) 120 { 121 int r; 122 123 if ((r = config_tx(dev, CMD_TOGGLE_ALWAYS_UV, NULL, 0, pin)) != FIDO_OK) 124 return r; 125 126 return (fido_rx_cbor_status(dev, ms)); 127 } 128 129 int 130 fido_dev_toggle_always_uv(fido_dev_t *dev, const char *pin) 131 { 132 return config_toggle_always_uv_wait(dev, pin, -1); 133 } 134 135 static int 136 config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force, const char *pin) 137 { 138 cbor_item_t *argv[3]; 139 int r; 140 141 memset(argv, 0, sizeof(argv)); 142 143 if ((!len && !force) || len > UINT8_MAX) { 144 r = FIDO_ERR_INVALID_ARGUMENT; 145 goto fail; 146 } 147 if (len && (argv[0] = cbor_build_uint8((uint8_t)len)) == NULL) { 148 fido_log_debug("%s: cbor_encode_uint8", __func__); 149 r = FIDO_ERR_INTERNAL; 150 goto fail; 151 } 152 if (force && (argv[2] = cbor_build_bool(true)) == NULL) { 153 fido_log_debug("%s: cbor_build_bool", __func__); 154 r = FIDO_ERR_INTERNAL; 155 goto fail; 156 } 157 if ((r = config_tx(dev, CMD_SET_PIN_MINLEN, argv, nitems(argv), 158 pin)) != FIDO_OK) { 159 fido_log_debug("%s: config_tx", __func__); 160 goto fail; 161 } 162 163 fail: 164 cbor_vector_free(argv, nitems(argv)); 165 166 return r; 167 } 168 169 static int 170 config_pin_minlen(fido_dev_t *dev, size_t len, bool force, const char *pin, 171 int ms) 172 { 173 int r; 174 175 if ((r = config_pin_minlen_tx(dev, len, force, pin)) != FIDO_OK) 176 return r; 177 178 return fido_rx_cbor_status(dev, ms); 179 } 180 181 int 182 fido_dev_set_pin_minlen(fido_dev_t *dev, size_t len, const char *pin) 183 { 184 return config_pin_minlen(dev, len, false, pin, -1); 185 } 186 187 int 188 fido_dev_force_pin_change(fido_dev_t *dev, const char *pin) 189 { 190 return config_pin_minlen(dev, 0, true, pin, -1); 191 } 192