195dbdf32Schristos /*
2*2d40c451Schristos * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
395dbdf32Schristos * Use of this source code is governed by a BSD-style
495dbdf32Schristos * license that can be found in the LICENSE file.
5*2d40c451Schristos * SPDX-License-Identifier: BSD-2-Clause
695dbdf32Schristos */
795dbdf32Schristos
895dbdf32Schristos #include "fido.h"
995dbdf32Schristos #include "fido/config.h"
1095dbdf32Schristos #include "fido/es256.h"
1195dbdf32Schristos
1295dbdf32Schristos #define CMD_ENABLE_ENTATTEST 0x01
1395dbdf32Schristos #define CMD_TOGGLE_ALWAYS_UV 0x02
1495dbdf32Schristos #define CMD_SET_PIN_MINLEN 0x03
1595dbdf32Schristos
1695dbdf32Schristos static int
config_prepare_hmac(uint8_t subcmd,const cbor_item_t * item,fido_blob_t * hmac)1795dbdf32Schristos config_prepare_hmac(uint8_t subcmd, const cbor_item_t *item, fido_blob_t *hmac)
1895dbdf32Schristos {
1995dbdf32Schristos uint8_t prefix[32 + 2 * sizeof(uint8_t)], cbor[128];
20*2d40c451Schristos size_t cbor_len = 0;
2195dbdf32Schristos
2295dbdf32Schristos memset(prefix, 0xff, sizeof(prefix));
2395dbdf32Schristos prefix[sizeof(prefix) - 2] = CTAP_CBOR_CONFIG;
2495dbdf32Schristos prefix[sizeof(prefix) - 1] = subcmd;
2595dbdf32Schristos
26*2d40c451Schristos if (item != NULL) {
2795dbdf32Schristos if ((cbor_len = cbor_serialize(item, cbor, sizeof(cbor))) == 0) {
2895dbdf32Schristos fido_log_debug("%s: cbor_serialize", __func__);
2995dbdf32Schristos return -1;
3095dbdf32Schristos }
31*2d40c451Schristos }
3295dbdf32Schristos if ((hmac->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
3395dbdf32Schristos fido_log_debug("%s: malloc", __func__);
3495dbdf32Schristos return -1;
3595dbdf32Schristos }
3695dbdf32Schristos memcpy(hmac->ptr, prefix, sizeof(prefix));
3795dbdf32Schristos memcpy(hmac->ptr + sizeof(prefix), cbor, cbor_len);
3895dbdf32Schristos hmac->len = cbor_len + sizeof(prefix);
3995dbdf32Schristos
4095dbdf32Schristos return 0;
4195dbdf32Schristos }
4295dbdf32Schristos
4395dbdf32Schristos static int
config_tx(fido_dev_t * dev,uint8_t subcmd,cbor_item_t ** paramv,size_t paramc,const char * pin,int * ms)4495dbdf32Schristos config_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **paramv, size_t paramc,
45*2d40c451Schristos const char *pin, int *ms)
4695dbdf32Schristos {
4795dbdf32Schristos cbor_item_t *argv[4];
4895dbdf32Schristos es256_pk_t *pk = NULL;
4995dbdf32Schristos fido_blob_t *ecdh = NULL, f, hmac;
5095dbdf32Schristos const uint8_t cmd = CTAP_CBOR_CONFIG;
5195dbdf32Schristos int r = FIDO_ERR_INTERNAL;
5295dbdf32Schristos
5395dbdf32Schristos memset(&f, 0, sizeof(f));
5495dbdf32Schristos memset(&hmac, 0, sizeof(hmac));
5595dbdf32Schristos memset(&argv, 0, sizeof(argv));
5695dbdf32Schristos
5795dbdf32Schristos /* subCommand */
5895dbdf32Schristos if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
5995dbdf32Schristos fido_log_debug("%s: cbor encode", __func__);
6095dbdf32Schristos goto fail;
6195dbdf32Schristos }
6295dbdf32Schristos
63*2d40c451Schristos /* subCommandParams */
64*2d40c451Schristos if (paramc != 0 &&
65*2d40c451Schristos (argv[1] = cbor_flatten_vector(paramv, paramc)) == NULL) {
6695dbdf32Schristos fido_log_debug("%s: cbor_flatten_vector", __func__);
6795dbdf32Schristos goto fail;
6895dbdf32Schristos }
69*2d40c451Schristos
70*2d40c451Schristos /* pinProtocol, pinAuth */
71*2d40c451Schristos if (pin != NULL ||
72*2d40c451Schristos (fido_dev_supports_permissions(dev) && fido_dev_has_uv(dev))) {
7395dbdf32Schristos if (config_prepare_hmac(subcmd, argv[1], &hmac) < 0) {
7495dbdf32Schristos fido_log_debug("%s: config_prepare_hmac", __func__);
7595dbdf32Schristos goto fail;
7695dbdf32Schristos }
77*2d40c451Schristos if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
7895dbdf32Schristos fido_log_debug("%s: fido_do_ecdh", __func__);
7995dbdf32Schristos goto fail;
8095dbdf32Schristos }
8195dbdf32Schristos if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
82*2d40c451Schristos NULL, &argv[3], &argv[2], ms)) != FIDO_OK) {
8395dbdf32Schristos fido_log_debug("%s: cbor_add_uv_params", __func__);
8495dbdf32Schristos goto fail;
8595dbdf32Schristos }
8695dbdf32Schristos }
8795dbdf32Schristos
8895dbdf32Schristos /* framing and transmission */
8995dbdf32Schristos if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
90*2d40c451Schristos fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
9195dbdf32Schristos fido_log_debug("%s: fido_tx", __func__);
9295dbdf32Schristos r = FIDO_ERR_TX;
9395dbdf32Schristos goto fail;
9495dbdf32Schristos }
9595dbdf32Schristos
9695dbdf32Schristos r = FIDO_OK;
9795dbdf32Schristos fail:
9895dbdf32Schristos cbor_vector_free(argv, nitems(argv));
9995dbdf32Schristos es256_pk_free(&pk);
10095dbdf32Schristos fido_blob_free(&ecdh);
10195dbdf32Schristos free(f.ptr);
10295dbdf32Schristos free(hmac.ptr);
10395dbdf32Schristos
10495dbdf32Schristos return r;
10595dbdf32Schristos }
10695dbdf32Schristos
10795dbdf32Schristos static int
config_enable_entattest_wait(fido_dev_t * dev,const char * pin,int * ms)108*2d40c451Schristos config_enable_entattest_wait(fido_dev_t *dev, const char *pin, int *ms)
10995dbdf32Schristos {
11095dbdf32Schristos int r;
11195dbdf32Schristos
112*2d40c451Schristos if ((r = config_tx(dev, CMD_ENABLE_ENTATTEST, NULL, 0, pin,
113*2d40c451Schristos ms)) != FIDO_OK)
11495dbdf32Schristos return r;
11595dbdf32Schristos
11695dbdf32Schristos return fido_rx_cbor_status(dev, ms);
11795dbdf32Schristos }
11895dbdf32Schristos
11995dbdf32Schristos int
fido_dev_enable_entattest(fido_dev_t * dev,const char * pin)12095dbdf32Schristos fido_dev_enable_entattest(fido_dev_t *dev, const char *pin)
12195dbdf32Schristos {
122*2d40c451Schristos int ms = dev->timeout_ms;
123*2d40c451Schristos
124*2d40c451Schristos return (config_enable_entattest_wait(dev, pin, &ms));
12595dbdf32Schristos }
12695dbdf32Schristos
12795dbdf32Schristos static int
config_toggle_always_uv_wait(fido_dev_t * dev,const char * pin,int * ms)128*2d40c451Schristos config_toggle_always_uv_wait(fido_dev_t *dev, const char *pin, int *ms)
12995dbdf32Schristos {
13095dbdf32Schristos int r;
13195dbdf32Schristos
132*2d40c451Schristos if ((r = config_tx(dev, CMD_TOGGLE_ALWAYS_UV, NULL, 0, pin,
133*2d40c451Schristos ms)) != FIDO_OK)
13495dbdf32Schristos return r;
13595dbdf32Schristos
13695dbdf32Schristos return (fido_rx_cbor_status(dev, ms));
13795dbdf32Schristos }
13895dbdf32Schristos
13995dbdf32Schristos int
fido_dev_toggle_always_uv(fido_dev_t * dev,const char * pin)14095dbdf32Schristos fido_dev_toggle_always_uv(fido_dev_t *dev, const char *pin)
14195dbdf32Schristos {
142*2d40c451Schristos int ms = dev->timeout_ms;
143*2d40c451Schristos
144*2d40c451Schristos return config_toggle_always_uv_wait(dev, pin, &ms);
14595dbdf32Schristos }
14695dbdf32Schristos
14795dbdf32Schristos static int
config_pin_minlen_tx(fido_dev_t * dev,size_t len,bool force,const fido_str_array_t * rpid,const char * pin,int * ms)148*2d40c451Schristos config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force,
149*2d40c451Schristos const fido_str_array_t *rpid, const char *pin, int *ms)
15095dbdf32Schristos {
15195dbdf32Schristos cbor_item_t *argv[3];
15295dbdf32Schristos int r;
15395dbdf32Schristos
15495dbdf32Schristos memset(argv, 0, sizeof(argv));
15595dbdf32Schristos
156*2d40c451Schristos if ((rpid == NULL && len == 0 && !force) || len > UINT8_MAX) {
15795dbdf32Schristos r = FIDO_ERR_INVALID_ARGUMENT;
15895dbdf32Schristos goto fail;
15995dbdf32Schristos }
16095dbdf32Schristos if (len && (argv[0] = cbor_build_uint8((uint8_t)len)) == NULL) {
16195dbdf32Schristos fido_log_debug("%s: cbor_encode_uint8", __func__);
16295dbdf32Schristos r = FIDO_ERR_INTERNAL;
16395dbdf32Schristos goto fail;
16495dbdf32Schristos }
165*2d40c451Schristos if (rpid != NULL && (argv[1] = cbor_encode_str_array(rpid)) == NULL) {
166*2d40c451Schristos fido_log_debug("%s: cbor_encode_str_array", __func__);
167*2d40c451Schristos r = FIDO_ERR_INTERNAL;
168*2d40c451Schristos goto fail;
169*2d40c451Schristos }
17095dbdf32Schristos if (force && (argv[2] = cbor_build_bool(true)) == NULL) {
17195dbdf32Schristos fido_log_debug("%s: cbor_build_bool", __func__);
17295dbdf32Schristos r = FIDO_ERR_INTERNAL;
17395dbdf32Schristos goto fail;
17495dbdf32Schristos }
17595dbdf32Schristos if ((r = config_tx(dev, CMD_SET_PIN_MINLEN, argv, nitems(argv),
176*2d40c451Schristos pin, ms)) != FIDO_OK) {
17795dbdf32Schristos fido_log_debug("%s: config_tx", __func__);
17895dbdf32Schristos goto fail;
17995dbdf32Schristos }
18095dbdf32Schristos
18195dbdf32Schristos fail:
18295dbdf32Schristos cbor_vector_free(argv, nitems(argv));
18395dbdf32Schristos
18495dbdf32Schristos return r;
18595dbdf32Schristos }
18695dbdf32Schristos
18795dbdf32Schristos static int
config_pin_minlen(fido_dev_t * dev,size_t len,bool force,const fido_str_array_t * rpid,const char * pin,int * ms)188*2d40c451Schristos config_pin_minlen(fido_dev_t *dev, size_t len, bool force,
189*2d40c451Schristos const fido_str_array_t *rpid, const char *pin, int *ms)
19095dbdf32Schristos {
19195dbdf32Schristos int r;
19295dbdf32Schristos
193*2d40c451Schristos if ((r = config_pin_minlen_tx(dev, len, force, rpid, pin,
194*2d40c451Schristos ms)) != FIDO_OK)
19595dbdf32Schristos return r;
19695dbdf32Schristos
19795dbdf32Schristos return fido_rx_cbor_status(dev, ms);
19895dbdf32Schristos }
19995dbdf32Schristos
20095dbdf32Schristos int
fido_dev_set_pin_minlen(fido_dev_t * dev,size_t len,const char * pin)20195dbdf32Schristos fido_dev_set_pin_minlen(fido_dev_t *dev, size_t len, const char *pin)
20295dbdf32Schristos {
203*2d40c451Schristos int ms = dev->timeout_ms;
204*2d40c451Schristos
205*2d40c451Schristos return config_pin_minlen(dev, len, false, NULL, pin, &ms);
20695dbdf32Schristos }
20795dbdf32Schristos
20895dbdf32Schristos int
fido_dev_force_pin_change(fido_dev_t * dev,const char * pin)20995dbdf32Schristos fido_dev_force_pin_change(fido_dev_t *dev, const char *pin)
21095dbdf32Schristos {
211*2d40c451Schristos int ms = dev->timeout_ms;
212*2d40c451Schristos
213*2d40c451Schristos return config_pin_minlen(dev, 0, true, NULL, pin, &ms);
214*2d40c451Schristos }
215*2d40c451Schristos
216*2d40c451Schristos int
fido_dev_set_pin_minlen_rpid(fido_dev_t * dev,const char * const * rpid,size_t n,const char * pin)217*2d40c451Schristos fido_dev_set_pin_minlen_rpid(fido_dev_t *dev, const char * const *rpid,
218*2d40c451Schristos size_t n, const char *pin)
219*2d40c451Schristos {
220*2d40c451Schristos fido_str_array_t sa;
221*2d40c451Schristos int ms = dev->timeout_ms;
222*2d40c451Schristos int r;
223*2d40c451Schristos
224*2d40c451Schristos memset(&sa, 0, sizeof(sa));
225*2d40c451Schristos if (fido_str_array_pack(&sa, rpid, n) < 0) {
226*2d40c451Schristos fido_log_debug("%s: fido_str_array_pack", __func__);
227*2d40c451Schristos r = FIDO_ERR_INTERNAL;
228*2d40c451Schristos goto fail;
229*2d40c451Schristos }
230*2d40c451Schristos r = config_pin_minlen(dev, 0, false, &sa, pin, &ms);
231*2d40c451Schristos fail:
232*2d40c451Schristos fido_str_array_free(&sa);
233*2d40c451Schristos
234*2d40c451Schristos return r;
23595dbdf32Schristos }
236