Yubico / libfido2

Provides library functionality for FIDO2, including communication with a device over USB or NFC.
Other
590 stars 152 forks source link

Attestation process does not require client PIN with uv=true #642

Closed Treeston closed 1 year ago

Treeston commented 1 year ago

What version of libfido2 are you using? libfido2.so.1.12.0

What operating system are you running? Ubuntu 20.04 LTS

Does the problem happen with different authenticators? Unknown

Please include the output of fido2-token -L.

fido2-token -L
$ fido2-token -L
/dev/hidraw1: vendor=0x32a3, product=0x3201 (GoTrust Idem Key)

Please include the output of fido2-token -I.

fido2-token -I
$ fido2-token -I /dev/hidraw1
proto: 0x02
major: 0x01
minor: 0x15
build: 0x31
caps: 0x05 (wink, cbor, msg)
version strings: U2F_V2, FIDO_2_0
extension strings: hmac-secret
aaguid: 
options: rk, up, noplat, clientPin
fwversion: 0x0
maxmsgsiz: 1024
maxcredcntlst: 0
maxcredlen: 0
maxlargeblob: 0
pin protocols: 1
pin retries: 8
pin change required: false
uv retries: undefined

I am creating an assertion for a relying party which requires user verification. To this end, I call fido_assert_set_uv with FIDO_OPT_TRUE before issuing fido_dev_get_assert.

If the device does not support native UV, and requires a client PIN, I am expecting this operation to fail with FIDO_ERR_PIN_REQUIRED, allowing me to query the user for a PIN and retry.

Instead, the operation completes with FIDO_OK after a user presence test is passed. Unsurprisingly, the returned credential does not have UV (0x40) set, and will be rejected by the relying party.

Minimal example, using a previously-created, discoverable test credential for brevity.
(The issue also arises when specifying the accepted credential ID explicitly.)

#include "fido.h"
#include <cstdio>
#include <cstdint>

#define CHECKED(v) { int r = (v); if (r != FIDO_OK) { printf("%s: 0x%02x (%s)\n", #v, r, fido_strerr(r)); goto end; } }

int main(int, char const**) {
    fido_init(FIDO_DEBUG);
    fido_dev_t* dev = fido_dev_new();
    fido_assert_t* assn = fido_assert_new();
    {
        fido_dev_info_t* list = fido_dev_info_new(1);
        size_t n;
        CHECKED(fido_dev_info_manifest(list, 1, &n));

        printf("Found %zu devices.\n", n);
        if (n == 0) goto end;

        fido_dev_info_t const* devinfo = fido_dev_info_ptr(list, 0);
        printf("Using %s %s at %s\n", fido_dev_info_manufacturer_string(devinfo), fido_dev_info_product_string(devinfo), fido_dev_info_path(devinfo));
        CHECKED(fido_dev_open(dev, fido_dev_info_path(devinfo)));
        CHECKED(fido_dev_cancel(dev));
        fido_dev_info_free(&list, 1);

        uint8_t const ccdHash[32] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
                                      0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 };
        fido_assert_set_clientdata_hash(assn, ccdHash, 32);
        fido_assert_set_rp(assn, "example.com");
        fido_assert_set_up(assn, FIDO_OPT_TRUE);
        fido_assert_set_uv(assn, FIDO_OPT_TRUE);

        int result = fido_dev_get_assert(dev, assn, nullptr);
        printf("fido_dev_get_assert: %s (0x%02x)\n", fido_strerr(result), result);

        if (result == FIDO_OK) {
            size_t c = fido_assert_count(assn);
            if (c == 0) goto end;

            printf("Flags:\n");
            uint8_t flags = fido_assert_flags(assn, 0);
            char const* names[8] = { "UP", "RFU1", "UV", "BE", "BS", "RFU2", "AT", "ED" };
            for (uint8_t i=0; i<8; ++i)
                printf("[0x%02x] %-4s %s\n", (1 << i), names[i], (flags & (1 << i)) ? "set" : "clear");
        }
    }

end:
    fido_assert_free(&assn);
    fido_dev_close(dev);
    fido_dev_free(&dev);
    return 0;
}
stdout output
Found 1 devices.
Using GoTrust Idem Key at /dev/hidraw1
fido_dev_get_assert: FIDO_ERR_SUCCESS (0x00)
Flags:
[0x01] UP   set
[0x02] RFU1 clear
[0x04] UV   clear
[0x08] BE   clear
[0x10] BS   clear
[0x20] RFU2 clear
[0x40] AT   clear
[0x80] ED   clear
FIDO_DEBUG stderr output
fido_hid_unix_open: open /dev/hidraw0: Permission denied
run_manifest: found 1 hid device
run_manifest: skipping nfc
fido_tx: dev=0x5621afbff2a0, cmd=0x06
fido_tx: buf=0x5621afbff2a0, len=8
0000: fb 80 60 05 a8 11 d6 54
fido_rx: dev=0x5621afbff2a0, cmd=0x06, ms=-1
rx_preamble: buf=0x7ffc24674cd0, len=64
0000: ff ff ff ff 86 00 11 fb 80 60 05 a8 11 d6 54 03
0016: 00 00 00 02 01 15 31 05 00 00 00 00 00 00 00 00
0032: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0048: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
rx: payload_len=17
fido_rx: buf=0x5621afbff2a8, len=17
0000: fb 80 60 05 a8 11 d6 54 03 00 00 00 02 01 15 31
0016: 05
fido_dev_get_cbor_info_tx: dev=0x5621afbff2a0
fido_tx: dev=0x5621afbff2a0, cmd=0x10
fido_tx: buf=0x7ffc24674d27, len=1
0000: 04
fido_dev_get_cbor_info_rx: dev=0x5621afbff2a0, ci=0x5621afc100d0, ms=-1
fido_rx: dev=0x5621afbff2a0, cmd=0x10, ms=-1
rx_preamble: buf=0x7ffc24674c90, len=64
0000: 03 00 00 00 90 00 56 00 a6 01 82 66 55 32 46 5f
0016: 56 32 68 46 49 44 4f 5f 32 5f 30 02 81 6b 68 6d
0032: 61 63 2d 73 65 63 72 65 74 03 50 3b 1a db 99 0d
0048: fe 46 fd 90 b8 7f 76 14 a4 de 2a 04 a4 62 72 6b
rx: payload_len=86
rx: buf=0x7ffc24674c90, len=64
0000: 03 00 00 00 00 f5 62 75 70 f5 64 70 6c 61 74 f4
0016: 69 63 6c 69 65 6e 74 50 69 6e f5 05 19 04 00 06
0032: 81 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0048: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fido_rx: buf=0x5621afc00e90, len=86
0000: 00 a6 01 82 66 55 32 46 5f 56 32 68 46 49 44 4f
0016: 5f 32 5f 30 02 81 6b 68 6d 61 63 2d 73 65 63 72
0032: 65 74 03 50 3b 1a db 99 0d fe 46 fd 90 b8 7f 76
0048: 14 a4 de 2a 04 a4 62 72 6b f5 62 75 70 f5 64 70
0064: 6c 61 74 f4 69 63 6c 69 65 6e 74 50 69 6e f5 05
0080: 19 04 00 06 81 01
fido_dev_open_rx: FIDO_MAXMSG=2048, maxmsgsiz=1024
fido_tx: dev=0x5621afbff2a0, cmd=0x11
fido_tx: buf=(nil), len=0
fido_tx: dev=0x5621afbff2a0, cmd=0x10
fido_tx: buf=0x5621afc10da0, len=60
0000: 02 a3 01 6b 65 78 61 6d 70 6c 65 2e 63 6f 6d 02
0016: 58 20 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e
0032: 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e
0048: 1f 20 05 a2 62 75 70 f5 62 75 76 f5
fido_rx: dev=0x5621afbff2a0, cmd=0x10, ms=-1
rx_preamble: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 90 00 b2 00 a5 01 a2 62 69 64 50 ac
0016: d4 77 94 fb d7 c5 63 78 ce 62 07 72 96 9c c5 64
0032: 74 79 70 65 6a 70 75 62 6c 69 63 2d 6b 65 79 02
0048: 58 25 a3 79 a6 f6 ee af b9 a5 5e 37 8c 11 80 34
rx: payload_len=178
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 00 e2 75 1e 68 2f ab 9f 2d 30 ab 13
0016: d2 12 55 86 ce 19 47 01 00 00 01 d2 03 58 47 30
0032: 45 02 20 4a c7 8e 68 86 77 b1 92 ea ee 80 e2 b9
0048: c8 1b 4c 8a 21 da 45 0a 25 3b c9 d9 ba 27 b7 47
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 01 d9 6e 9c 02 21 00 d9 57 1e 08 50
0016: 3a 09 aa d5 55 b1 fa 3e ca 80 41 26 03 ea 1b 42
0032: bd af fd f2 74 51 d7 53 85 c4 e0 04 a1 62 69 64
0048: 50 7e 6c ed 69 a0 27 56 34 b0 e3 1c ae e6 3a c0
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 02 b8 05 04 00 00 00 00 00 00 00 00
0016: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0032: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0048: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fido_rx: buf=0x5621afc00e90, len=178
0000: 00 a5 01 a2 62 69 64 50 ac d4 77 94 fb d7 c5 63
0016: 78 ce 62 07 72 96 9c c5 64 74 79 70 65 6a 70 75
0032: 62 6c 69 63 2d 6b 65 79 02 58 25 a3 79 a6 f6 ee
0048: af b9 a5 5e 37 8c 11 80 34 e2 75 1e 68 2f ab 9f
0064: 2d 30 ab 13 d2 12 55 86 ce 19 47 01 00 00 01 d2
0080: 03 58 47 30 45 02 20 4a c7 8e 68 86 77 b1 92 ea
0096: ee 80 e2 b9 c8 1b 4c 8a 21 da 45 0a 25 3b c9 d9
0112: ba 27 b7 47 d9 6e 9c 02 21 00 d9 57 1e 08 50 3a
0128: 09 aa d5 55 b1 fa 3e ca 80 41 26 03 ea 1b 42 bd
0144: af fd f2 74 51 d7 53 85 c4 e0 04 a1 62 69 64 50
0160: 7e 6c ed 69 a0 27 56 34 b0 e3 1c ae e6 3a c0 b8
0176: 05 04
adjust_assert_count: cbor_type
adjust_assert_count: cbor_type
adjust_assert_count: cbor_type
adjust_assert_count: cbor_type
cbor_decode_assert_authdata: buf=0x5621afc0f750, len=37
parse_assert_reply: cbor type
fido_tx: dev=0x5621afbff2a0, cmd=0x10
fido_tx: buf=0x7ffc24674d87, len=1
0000: 08
fido_rx: dev=0x5621afbff2a0, cmd=0x10, ms=-1
rx_preamble: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 90 00 b0 00 a4 01 a2 62 69 64 50 a7
0016: 97 23 7f 33 a3 aa b5 a3 61 af fc 0d a0 4f f8 64
0032: 74 79 70 65 6a 70 75 62 6c 69 63 2d 6b 65 79 02
0048: 58 25 a3 79 a6 f6 ee af b9 a5 5e 37 8c 11 80 34
rx: payload_len=176
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 00 e2 75 1e 68 2f ab 9f 2d 30 ab 13
0016: d2 12 55 86 ce 19 47 01 00 00 01 d3 03 58 47 30
0032: 45 02 20 2c 82 e2 af 46 da 2c 61 a4 e2 a5 fd 6c
0048: 21 1b 7e e9 0e ed 7d eb f3 a6 2e 42 ae 63 37 30
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 01 ea 06 c8 02 21 00 b7 16 e8 8f a7
0016: 1e 67 00 f9 bd 56 d2 de 40 1a 91 05 87 23 00 bf
0032: 95 89 a4 be db 6d d4 42 95 e1 c9 04 a1 62 69 64
0048: 50 9f 10 63 4b 48 53 92 26 dc e1 b9 0d 7c 9f d8
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 02 b8 00 00 00 00 00 00 00 00 00 00
0016: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0032: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0048: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fido_rx: buf=0x5621afc00e90, len=176
0000: 00 a4 01 a2 62 69 64 50 a7 97 23 7f 33 a3 aa b5
0016: a3 61 af fc 0d a0 4f f8 64 74 79 70 65 6a 70 75
0032: 62 6c 69 63 2d 6b 65 79 02 58 25 a3 79 a6 f6 ee
0048: af b9 a5 5e 37 8c 11 80 34 e2 75 1e 68 2f ab 9f
0064: 2d 30 ab 13 d2 12 55 86 ce 19 47 01 00 00 01 d3
0080: 03 58 47 30 45 02 20 2c 82 e2 af 46 da 2c 61 a4
0096: e2 a5 fd 6c 21 1b 7e e9 0e ed 7d eb f3 a6 2e 42
0112: ae 63 37 30 ea 06 c8 02 21 00 b7 16 e8 8f a7 1e
0128: 67 00 f9 bd 56 d2 de 40 1a 91 05 87 23 00 bf 95
0144: 89 a4 be db 6d d4 42 95 e1 c9 04 a1 62 69 64 50
0160: 9f 10 63 4b 48 53 92 26 dc e1 b9 0d 7c 9f d8 b8
cbor_decode_assert_authdata: buf=0x5621afc10d70, len=37
fido_tx: dev=0x5621afbff2a0, cmd=0x10
fido_tx: buf=0x7ffc24674d87, len=1
0000: 08
fido_rx: dev=0x5621afbff2a0, cmd=0x10, ms=-1
rx_preamble: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 90 00 b1 00 a4 01 a2 62 69 64 50 05
0016: 3b 0e 01 94 40 a6 01 22 be d1 dc d9 6f 2f 8a 64
0032: 74 79 70 65 6a 70 75 62 6c 69 63 2d 6b 65 79 02
0048: 58 25 a3 79 a6 f6 ee af b9 a5 5e 37 8c 11 80 34
rx: payload_len=177
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 00 e2 75 1e 68 2f ab 9f 2d 30 ab 13
0016: d2 12 55 86 ce 19 47 01 00 00 01 d4 03 58 48 30
0032: 46 02 21 00 88 95 13 c6 61 1f cf a0 6e a5 21 83
0048: aa 24 be ed 58 49 19 ca 3d b7 98 fc 3e 8b 48 c0
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 01 56 a8 51 0f 02 21 00 a0 bb 24 0d
0016: ba db a3 31 9e c7 1b 5b 8a 19 f4 78 e9 c0 02 3d
0032: c3 41 b7 1b 7a 27 80 e7 00 96 d5 61 04 a1 62 69
0048: 64 50 8e 9f ce 0c 55 df db 0f 2c 74 02 95 f1 5a
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 02 df 3d 00 00 00 00 00 00 00 00 00
0016: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0032: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0048: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fido_rx: buf=0x5621afc00e90, len=177
0000: 00 a4 01 a2 62 69 64 50 05 3b 0e 01 94 40 a6 01
0016: 22 be d1 dc d9 6f 2f 8a 64 74 79 70 65 6a 70 75
0032: 62 6c 69 63 2d 6b 65 79 02 58 25 a3 79 a6 f6 ee
0048: af b9 a5 5e 37 8c 11 80 34 e2 75 1e 68 2f ab 9f
0064: 2d 30 ab 13 d2 12 55 86 ce 19 47 01 00 00 01 d4
0080: 03 58 48 30 46 02 21 00 88 95 13 c6 61 1f cf a0
0096: 6e a5 21 83 aa 24 be ed 58 49 19 ca 3d b7 98 fc
0112: 3e 8b 48 c0 56 a8 51 0f 02 21 00 a0 bb 24 0d ba
0128: db a3 31 9e c7 1b 5b 8a 19 f4 78 e9 c0 02 3d c3
0144: 41 b7 1b 7a 27 80 e7 00 96 d5 61 04 a1 62 69 64
0160: 50 8e 9f ce 0c 55 df db 0f 2c 74 02 95 f1 5a df
0176: 3d
cbor_decode_assert_authdata: buf=0x5621afc0f750, len=37
fido_tx: dev=0x5621afbff2a0, cmd=0x10
fido_tx: buf=0x7ffc24674d87, len=1
0000: 08
fido_rx: dev=0x5621afbff2a0, cmd=0x10, ms=-1
rx_preamble: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 90 00 b0 00 a4 01 a2 62 69 64 50 10
0016: 33 49 91 14 04 f4 80 4f d0 b4 27 34 e1 43 5a 64
0032: 74 79 70 65 6a 70 75 62 6c 69 63 2d 6b 65 79 02
0048: 58 25 a3 79 a6 f6 ee af b9 a5 5e 37 8c 11 80 34
rx: payload_len=176
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 00 e2 75 1e 68 2f ab 9f 2d 30 ab 13
0016: d2 12 55 86 ce 19 47 01 00 00 01 d5 03 58 47 30
0032: 45 02 21 00 c7 67 4c 38 2f 44 7d 6c 30 ec 3f df
0048: a1 2f e7 4b af f4 5a f8 ca 62 6f c4 47 bd 2f 77
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 01 1d a1 1d a3 02 20 43 ee 65 3d aa
0016: 96 38 85 f5 e6 cc a4 bb f2 d3 2e ee 9d df ad 57
0032: ea 7e f4 c1 c1 a7 cf e6 95 75 62 04 a1 62 69 64
0048: 50 28 4a 5d b1 f2 6f 0e 81 84 ee e7 a4 18 e0 92
rx: buf=0x7ffc24674c60, len=64
0000: 03 00 00 00 02 15 00 00 00 00 00 00 00 00 00 00
0016: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0032: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0048: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fido_rx: buf=0x5621afc00e90, len=176
0000: 00 a4 01 a2 62 69 64 50 10 33 49 91 14 04 f4 80
0016: 4f d0 b4 27 34 e1 43 5a 64 74 79 70 65 6a 70 75
0032: 62 6c 69 63 2d 6b 65 79 02 58 25 a3 79 a6 f6 ee
0048: af b9 a5 5e 37 8c 11 80 34 e2 75 1e 68 2f ab 9f
0064: 2d 30 ab 13 d2 12 55 86 ce 19 47 01 00 00 01 d5
0080: 03 58 47 30 45 02 21 00 c7 67 4c 38 2f 44 7d 6c
0096: 30 ec 3f df a1 2f e7 4b af f4 5a f8 ca 62 6f c4
0112: 47 bd 2f 77 1d a1 1d a3 02 20 43 ee 65 3d aa 96
0128: 38 85 f5 e6 cc a4 bb f2 d3 2e ee 9d df ad 57 ea
0144: 7e f4 c1 c1 a7 cf e6 95 75 62 04 a1 62 69 64 50
0160: 28 4a 5d b1 f2 6f 0e 81 84 ee e7 a4 18 e0 92 15
cbor_decode_assert_authdata: buf=0x5621afc0f750, len=37

LDVG commented 1 year ago

Hi,

If the device does not support native UV, and requires a client PIN, I am expecting this operation to fail with FIDO_ERR_PIN_REQUIRED, allowing me to query the user for a PIN and retry.

Per the CTAP2.0 specification that your device should adhere to: if the "uv" option was specified and set to true and the device doesn’t support built-in UV, it should return FIDO_ERR_UNSUPPORTED_OPTION (CTAP2.1 authenticators return some other error code). It would seem that, unfortunately, your authenticator does not comply with the specification, and the bug report should be filed with the manufacturer.

For reference, this is the request sent to the authenticator (per the debug output above):

{1: "example.com", 2: h'0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20', 5: {"up": true, "uv": true}}

You could work around this using fido_dev_{supports,has}_{pin,uv}() if you'd like to determine what user verification method to use before firing off the assertion request.

Treeston commented 1 year ago

Thanks for the swift response!

Am I understanding you correctly that the uv option (controlled by fido_assert_set_uv) only controls device-native UV such as biometrics or on-authenticator PIN pad, but not a PIN entered on the client device? I.e., if I am looking to authenticate to a device without native UV, using a client PIN, I should not include the uv option?

martelletto commented 1 year ago

Yes, that's correct.