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, int *ms) 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, ms)) != 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], ms)) != 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, ms) < 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, 107 ms)) != FIDO_OK) 108 return r; 109 110 return fido_rx_cbor_status(dev, ms); 111 } 112 113 int 114 fido_dev_enable_entattest(fido_dev_t *dev, const char *pin) 115 { 116 int ms = dev->timeout_ms; 117 118 return (config_enable_entattest_wait(dev, pin, &ms)); 119 } 120 121 static int 122 config_toggle_always_uv_wait(fido_dev_t *dev, const char *pin, int *ms) 123 { 124 int r; 125 126 if ((r = config_tx(dev, CMD_TOGGLE_ALWAYS_UV, NULL, 0, pin, 127 ms)) != FIDO_OK) 128 return r; 129 130 return (fido_rx_cbor_status(dev, ms)); 131 } 132 133 int 134 fido_dev_toggle_always_uv(fido_dev_t *dev, const char *pin) 135 { 136 int ms = dev->timeout_ms; 137 138 return config_toggle_always_uv_wait(dev, pin, &ms); 139 } 140 141 static int 142 config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force, 143 const fido_str_array_t *rpid, const char *pin, int *ms) 144 { 145 cbor_item_t *argv[3]; 146 int r; 147 148 memset(argv, 0, sizeof(argv)); 149 150 if ((rpid == NULL && len == 0 && !force) || len > UINT8_MAX) { 151 r = FIDO_ERR_INVALID_ARGUMENT; 152 goto fail; 153 } 154 if (len && (argv[0] = cbor_build_uint8((uint8_t)len)) == NULL) { 155 fido_log_debug("%s: cbor_encode_uint8", __func__); 156 r = FIDO_ERR_INTERNAL; 157 goto fail; 158 } 159 if (rpid != NULL && (argv[1] = cbor_encode_str_array(rpid)) == NULL) { 160 fido_log_debug("%s: cbor_encode_str_array", __func__); 161 r = FIDO_ERR_INTERNAL; 162 goto fail; 163 } 164 if (force && (argv[2] = cbor_build_bool(true)) == NULL) { 165 fido_log_debug("%s: cbor_build_bool", __func__); 166 r = FIDO_ERR_INTERNAL; 167 goto fail; 168 } 169 if ((r = config_tx(dev, CMD_SET_PIN_MINLEN, argv, nitems(argv), 170 pin, ms)) != FIDO_OK) { 171 fido_log_debug("%s: config_tx", __func__); 172 goto fail; 173 } 174 175 fail: 176 cbor_vector_free(argv, nitems(argv)); 177 178 return r; 179 } 180 181 static int 182 config_pin_minlen(fido_dev_t *dev, size_t len, bool force, 183 const fido_str_array_t *rpid, const char *pin, int *ms) 184 { 185 int r; 186 187 if ((r = config_pin_minlen_tx(dev, len, force, rpid, pin, 188 ms)) != FIDO_OK) 189 return r; 190 191 return fido_rx_cbor_status(dev, ms); 192 } 193 194 int 195 fido_dev_set_pin_minlen(fido_dev_t *dev, size_t len, const char *pin) 196 { 197 int ms = dev->timeout_ms; 198 199 return config_pin_minlen(dev, len, false, NULL, pin, &ms); 200 } 201 202 int 203 fido_dev_force_pin_change(fido_dev_t *dev, const char *pin) 204 { 205 int ms = dev->timeout_ms; 206 207 return config_pin_minlen(dev, 0, true, NULL, pin, &ms); 208 } 209 210 int 211 fido_dev_set_pin_minlen_rpid(fido_dev_t *dev, const char * const *rpid, 212 size_t n, const char *pin) 213 { 214 fido_str_array_t sa; 215 int ms = dev->timeout_ms; 216 int r; 217 218 memset(&sa, 0, sizeof(sa)); 219 if (fido_str_array_pack(&sa, rpid, n) < 0) { 220 fido_log_debug("%s: fido_str_array_pack", __func__); 221 r = FIDO_ERR_INTERNAL; 222 goto fail; 223 } 224 r = config_pin_minlen(dev, 0, false, &sa, pin, &ms); 225 fail: 226 fido_str_array_free(&sa); 227 228 return r; 229 } 230