## Coverage Report

#### Created: 2025-03-01 02:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /libfido2/src/io.c

    Line
    Count
    Source
    1
    /*
    2
     * Copyright (c) 2018-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 "packed.h"
    10
    11
    PACKED_TYPE(frame_t,
    12
    struct frame {
    13
            uint32_t cid; /* channel id */
    14
            union {
    15
                    uint8_t type;
    16
                    struct {
    17
                            uint8_t cmd;
    18
                            uint8_t bcnth;
    19
                            uint8_t bcntl;
    20
                            uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
    21
                    } init;
    22
                    struct {
    23
                            uint8_t seq;
    24
                            uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
    25
                    } cont;
    26
            } body;
    27
    })
    28
    29
    #ifndef MIN
    30
    1.81M
    #define MIN(x, y) ((x) > (y) ? (y) : (x))
    31
    #endif
    32
    33
    static int
    34
    tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms)
    35
    1.83M
    {
    36
    1.83M
            struct timespec ts;
    37
    1.83M
            int n;
    38
    39
    1.83M
            if (fido_time_now(&ts) != 0)
    40
    4.03k
                    return (-1);
    41
    42
    1.82M
            n = d->io.write(d->io_handle, pkt, len);
    43
    44
    1.82M
            if (fido_time_delta(&ts, ms) != 0)
    45
    12.0k
                    return (-1);
    46
    47
    1.81M
            return (n);
    48
    1.82M
    }
    49
    50
    static int
    51
    tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
    52
    17.8k
    {
    53
    17.8k
            struct frame    *fp;
    54
    17.8k
            unsigned char    pkt[sizeof(*fp) + 1];
    55
    17.8k
            const size_t     len = d->tx_len + 1;
    56
    17.8k
            int              n;
    57
    58
    17.8k
            memset(&pkt, 0, sizeof(pkt));
    59
    17.8k
            fp = (struct frame *)(pkt + 1);
    60
    17.8k
            fp->cid = d->cid;
    61
    17.8k
            fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
    62
    63
    17.8k
            if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
    64
    17.8k
                (size_t)n != len)
    65
    348
                    return (-1);
    66
    67
    17.5k
            return (0);
    68
    17.8k
    }
    69
    70
    static size_t
    71
    tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
    72
    1.26M
    {
    73
    1.26M
            struct frame    *fp;
    74
    1.26M
            unsigned char    pkt[sizeof(*fp) + 1];
    75
    1.26M
            const size_t     len = d->tx_len + 1;
    76
    1.26M
            int              n;
    77
    78
    1.26M
            if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
    79
    0
                    return (0);
    80
    81
    1.26M
            memset(&pkt, 0, sizeof(pkt));
    82
    1.26M
            fp = (struct frame *)(pkt + 1);
    83
    1.26M
            fp->cid = d->cid;
    84
    1.26M
            fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
    85
    1.26M
            fp->body.init.bcnth = (count >> 8) & 0xff;
    86
    1.26M
            fp->body.init.bcntl = count & 0xff;
    87
    1.26M
            count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
    88
    1.26M
            memcpy(&fp->body.init.data, buf, count);
    89
    90
    1.26M
            if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
    91
    1.26M
                (size_t)n != len)
    92
    15.7k
                    return (0);
    93
    94
    1.24M
            return (count);
    95
    1.26M
    }
    96
    97
    static size_t
    98
    tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
    99
    549k
    {
    100
    549k
            struct frame    *fp;
    101
    549k
            unsigned char    pkt[sizeof(*fp) + 1];
    102
    549k
            const size_t     len = d->tx_len + 1;
    103
    549k
            int              n;
    104
    105
    549k
            if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
    106
    0
                    return (0);
    107
    108
    549k
            memset(&pkt, 0, sizeof(pkt));
    109
    549k
            fp = (struct frame *)(pkt + 1);
    110
    549k
            fp->cid = d->cid;
    111
    549k
            fp->body.cont.seq = seq;
    112
    549k
            count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
    113
    549k
            memcpy(&fp->body.cont.data, buf, count);
    114
    115
    549k
            if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
    116
    549k
                (size_t)n != len)
    117
    4.36k
                    return (0);
    118
    119
    545k
            return (count);
    120
    549k
    }
    121
    122
    static int
    123
    tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
    124
    1.26M
    {
    125
    1.26M
            size_t n, sent;
    126
    127
    1.26M
            if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
    128
    15.7k
                    fido_log_debug("%s: tx_preamble", __func__);
    129
    15.7k
                    return (-1);
    130
    15.7k
            }
    131
    132
    1.79M
            for (uint8_t seq = 0; sent < count; sent += n) {
    133
    550k
                    if (seq & 0x80) {
    134
    283
                            fido_log_debug("%s: seq & 0x80", __func__);
    135
    283
                            return (-1);
    136
    283
                    }
    137
    549k
                    if ((n = tx_frame(d, seq++, buf + sent, count - sent,
    138
    549k
                        ms)) == 0) {
    139
    4.36k
                            fido_log_debug("%s: tx_frame", __func__);
    140
    4.36k
                            return (-1);
    141
    4.36k
                    }
    142
    549k
            }
    143
    144
    1.24M
            return (0);
    145
    1.24M
    }
    146
    147
    static int
    148
    transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
    149
    21.7k
    {
    150
    21.7k
            struct timespec ts;
    151
    21.7k
            int n;
    152
    153
    21.7k
            if (fido_time_now(&ts) != 0)
    154
    118
                    return (-1);
    155
    156
    21.6k
            n = d->transport.tx(d, cmd, buf, count);
    157
    158
    21.6k
            if (fido_time_delta(&ts, ms) != 0)
    159
    113
                    return (-1);
    160
    161
    21.5k
            return (n);
    162
    21.6k
    }
    163
    164
    int
    165
    fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
    166
    1.30M
    {
    167
    1.30M
            fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
    168
    1.30M
            fido_log_xxd(buf, count, "%s", __func__);
    169
    170
    1.30M
            if (d->transport.tx != NULL)
    171
    21.7k
                    return (transport_tx(d, cmd, buf, count, ms));
    172
    1.28M
            if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
    173
    140
                    fido_log_debug("%s: invalid argument", __func__);
    174
    140
                    return (-1);
    175
    140
            }
    176
    177
    1.28M
            return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
    178
    1.28M
    }
    179
    180
    static int
    181
    rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
    182
    2.24M
    {
    183
    2.24M
            struct timespec ts;
    184
    2.24M
            int n;
    185
    186
    2.24M
            memset(fp, 0, sizeof(*fp));
    187
    188
    2.24M
            if (fido_time_now(&ts) != 0)
    189
    3.96k
                    return (-1);
    190
    191
    2.23M
            if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
    192
    2.23M
                (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
    193
    538k
                    return (-1);
    194
    195
    1.69M
            return (fido_time_delta(&ts, ms));
    196
    2.23M
    }
    197
    198
    static int
    199
    rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
    200
    1.24M
    {
    201
    1.27M
            do {
    202
    1.27M
                    if (rx_frame(d, fp, ms) < 0)
    203
    526k
                            return (-1);
    204
    743k
    #ifdef FIDO_FUZZ
    205
    743k
                    fp->cid = d->cid;
    206
    743k
    #endif
    207
    743k
            } while (fp->cid != d->cid || (fp->cid == d->cid &&
    208
    743k
                fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
    209
    210
    722k
            if (d->rx_len > sizeof(*fp))
    211
    0
                    return (-1);
    212
    213
    722k
            fido_log_xxd(fp, d->rx_len, "%s", __func__);
    214
    722k
    #ifdef FIDO_FUZZ
    215
    722k
            fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
    216
    722k
    #endif
    217
    218
    722k
            if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
    219
    0
                    fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
    220
    0
                        __func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
    221
    0
                    return (-1);
    222
    0
            }
    223
    224
    722k
            return (0);
    225
    722k
    }
    226
    227
    static int
    228
    rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
    229
    1.24M
    {
    230
    1.24M
            struct frame f;
    231
    1.24M
            size_t r, payload_len, init_data_len, cont_data_len;
    232
    233
    1.24M
            if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
    234
    1.24M
                d->rx_len <= CTAP_CONT_HEADER_LEN)
    235
    0
                    return (-1);
    236
    237
    1.24M
            init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
    238
    1.24M
            cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
    239
    240
    1.24M
            if (init_data_len > sizeof(f.body.init.data) ||
    241
    1.24M
                cont_data_len > sizeof(f.body.cont.data))
    242
    0
                    return (-1);
    243
    244
    1.24M
            if (rx_preamble(d, cmd, &f, ms) < 0) {
    245
    526k
                    fido_log_debug("%s: rx_preamble", __func__);
    246
    526k
                    return (-1);
    247
    526k
            }
    248
    249
    722k
            payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
    250
    722k
            fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
    251
    252
    722k
            if (count < payload_len) {
    253
    93.1k
                    fido_log_debug("%s: count < payload_len", __func__);
    254
    93.1k
                    return (-1);
    255
    93.1k
            }
    256
    257
    629k
            if (payload_len < init_data_len) {
    258
    361k
                    memcpy(buf, f.body.init.data, payload_len);
    259
    361k
                    return ((int)payload_len);
    260
    361k
            }
    261
    262
    268k
            memcpy(buf, f.body.init.data, init_data_len);
    263
    268k
            r = init_data_len;
    264
    265
    1.21M
            for (int seq = 0; r < payload_len; seq++) {
    266
    971k
                    if (rx_frame(d, &f, ms) < 0) {
    267
    19.6k
                            fido_log_debug("%s: rx_frame", __func__);
    268
    19.6k
                            return (-1);
    269
    19.6k
                    }
    270
    271
    951k
                    fido_log_xxd(&f, d->rx_len, "%s", __func__);
    272
    951k
    #ifdef FIDO_FUZZ
    273
    951k
                    f.cid = d->cid;
    274
    951k
                    f.body.cont.seq = (uint8_t)seq;
    275
    951k
    #endif
    276
    277
    951k
                    if (f.cid != d->cid || f.body.cont.seq != seq) {
    278
    73
                            fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
    279
    73
                                __func__, f.cid, d->cid, f.body.cont.seq, seq);
    280
    73
                            return (-1);
    281
    73
                    }
    282
    283
    951k
                    if (payload_len - r > cont_data_len) {
    284
    718k
                            memcpy(buf + r, f.body.cont.data, cont_data_len);
    285
    718k
                            r += cont_data_len;
    286
    718k
                    } else {
    287
    233k
                            memcpy(buf + r, f.body.cont.data, payload_len - r);
    288
    233k
                            r += payload_len - r; /* break */
    289
    233k
                    }
    290
    951k
            }
    291
    292
    248k
            return ((int)r);
    293
    268k
    }
    294
    295
    static int
    296
    transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
    297
    20.3k
    {
    298
    20.3k
            struct timespec ts;
    299
    20.3k
            int n;
    300
    301
    20.3k
            if (fido_time_now(&ts) != 0)
    302
    278
                    return (-1);
    303
    304
    20.0k
            n = d->transport.rx(d, cmd, buf, count, *ms);
    305
    306
    20.0k
            if (fido_time_delta(&ts, ms) != 0)
    307
    118
                    return (-1);
    308
    309
    19.9k
            return (n);
    310
    20.0k
    }
    311
    312
    int
    313
    fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
    314
    1.26M
    {
    315
    1.26M
            int n;
    316
    317
    1.26M
            fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
    318
    1.26M
                cmd, *ms);
    319
    320
    1.26M
            if (d->transport.rx != NULL)
    321
    20.3k
                    return (transport_rx(d, cmd, buf, count, ms));
    322
    1.24M
            if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
    323
    0
                    fido_log_debug("%s: invalid argument", __func__);
    324
    0
                    return (-1);
    325
    0
            }
    326
    1.24M
            if ((n = rx(d, cmd, buf, count, ms)) >= 0)
    327
    609k
                    fido_log_xxd(buf, (size_t)n, "%s", __func__);
    328
    329
    1.24M
            return (n);
    330
    1.24M
    }
    331
    332
    int
    333
    fido_rx_cbor_status(fido_dev_t *d, int *ms)
    334
    27.8k
    {
    335
    27.8k
            unsigned char   *msg;
    336
    27.8k
            int              msglen;
    337
    27.8k
            int              r;
    338
    339
    27.8k
            if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
    340
    50
                    r = FIDO_ERR_INTERNAL;
    341
    50
                    goto out;
    342
    50
            }
    343
    344
    27.8k
            if ((msglen = fido_rx(d, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0 ||
    345
    27.8k
                (size_t)msglen < 1) {
    346
    21.5k
                    fido_log_debug("%s: fido_rx", __func__);
    347
    21.5k
                    r = FIDO_ERR_RX;
    348
    21.5k
                    goto out;
    349
    21.5k
            }
    350
    351
    6.26k
            r = msg[0];
    352
    27.8k
    out:
    353
    27.8k
            freezero(msg, FIDO_MAXMSG);
    354
    355
    27.8k
            return (r);
    356
    6.26k
    }