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