Coverage Report

Created: 2022-04-27 14:33

/libfido2/src/dev.c
Line
Count
Source (jump to first uncovered line)
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
 */
6
7
#include "fido.h"
8
9
#ifndef TLS
10
#define TLS
11
#endif
12
13
static TLS bool disable_u2f_fallback;
14
15
#ifdef FIDO_FUZZ
16
static void
17
set_random_report_len(fido_dev_t *dev)
18
69.4k
{
19
69.4k
        dev->rx_len = CTAP_MIN_REPORT_LEN +
20
69.4k
            uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1);
21
69.4k
        dev->tx_len = CTAP_MIN_REPORT_LEN +
22
69.4k
            uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1);
23
69.4k
}
24
#endif
25
26
static void
27
fido_dev_set_extension_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
28
7.91k
{
29
7.91k
        char * const    *ptr = fido_cbor_info_extensions_ptr(info);
30
7.91k
        size_t           len = fido_cbor_info_extensions_len(info);
31
32
23.1k
        for (size_t i = 0; i < len; i++)
33
15.2k
                if (strcmp(ptr[i], "credProtect") == 0)
34
3.34k
                        dev->flags |= FIDO_DEV_CRED_PROT;
35
7.91k
}
36
37
static void
38
fido_dev_set_option_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
39
7.91k
{
40
7.91k
        char * const    *ptr = fido_cbor_info_options_name_ptr(info);
41
7.91k
        const bool      *val = fido_cbor_info_options_value_ptr(info);
42
7.91k
        size_t           len = fido_cbor_info_options_len(info);
43
44
44.5k
        for (size_t i = 0; i < len; i++)
45
36.6k
                if (strcmp(ptr[i], "clientPin") == 0) {
46
3.31k
                        dev->flags |= val[i] ?
47
3.00k
                            FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET;
48
33.3k
                } else if (strcmp(ptr[i], "credMgmt") == 0 ||
49
33.3k
                           strcmp(ptr[i], "credentialMgmtPreview") == 0) {
50
2.78k
                        if (val[i])
51
2.76k
                                dev->flags |= FIDO_DEV_CREDMAN;
52
30.5k
                } else if (strcmp(ptr[i], "uv") == 0) {
53
417
                        dev->flags |= val[i] ?
54
414
                            FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET;
55
30.1k
                } else if (strcmp(ptr[i], "pinUvAuthToken") == 0) {
56
277
                        if (val[i])
57
263
                                dev->flags |= FIDO_DEV_TOKEN_PERMS;
58
277
                }
59
7.91k
}
60
61
static void
62
fido_dev_set_protocol_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
63
7.91k
{
64
7.91k
        const uint8_t   *ptr = fido_cbor_info_protocols_ptr(info);
65
7.91k
        size_t           len = fido_cbor_info_protocols_len(info);
66
67
17.1k
        for (size_t i = 0; i < len; i++)
68
9.21k
                switch (ptr[i]) {
69
6.90k
                case CTAP_PIN_PROTOCOL1:
70
6.90k
                        dev->flags |= FIDO_DEV_PIN_PROTOCOL1;
71
6.90k
                        break;
72
1.00k
                case CTAP_PIN_PROTOCOL2:
73
1.00k
                        dev->flags |= FIDO_DEV_PIN_PROTOCOL2;
74
1.00k
                        break;
75
1.30k
                default:
76
1.30k
                        fido_log_debug("%s: unknown protocol %u", __func__,
77
1.30k
                            ptr[i]);
78
1.30k
                        break;
79
9.21k
                }
80
7.91k
}
81
82
static void
83
fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
84
7.91k
{
85
7.91k
        fido_dev_set_extension_flags(dev, info);
86
7.91k
        fido_dev_set_option_flags(dev, info);
87
7.91k
        fido_dev_set_protocol_flags(dev, info);
88
7.91k
}
89
90
static int
91
fido_dev_open_tx(fido_dev_t *dev, const char *path, int *ms)
92
69.4k
{
93
69.4k
        int r;
94
95
69.4k
        if (dev->io_handle != NULL) {
96
0
                fido_log_debug("%s: handle=%p", __func__, dev->io_handle);
97
0
                return (FIDO_ERR_INVALID_ARGUMENT);
98
0
        }
99
100
69.4k
        if (dev->io.open == NULL || dev->io.close == NULL) {
101
0
                fido_log_debug("%s: NULL open/close", __func__);
102
0
                return (FIDO_ERR_INVALID_ARGUMENT);
103
0
        }
104
105
69.4k
        if (dev->cid != CTAP_CID_BROADCAST) {
106
0
                fido_log_debug("%s: cid=0x%x", __func__, dev->cid);
107
0
                return (FIDO_ERR_INVALID_ARGUMENT);
108
0
        }
109
110
69.4k
        if (fido_get_random(&dev->nonce, sizeof(dev->nonce)) < 0) {
111
0
                fido_log_debug("%s: fido_get_random", __func__);
112
0
                return (FIDO_ERR_INTERNAL);
113
0
        }
114
115
69.4k
        if ((dev->io_handle = dev->io.open(path)) == NULL) {
116
0
                fido_log_debug("%s: dev->io.open", __func__);
117
0
                return (FIDO_ERR_INTERNAL);
118
0
        }
119
120
69.4k
        if (dev->io_own) {
121
69.4k
                dev->rx_len = CTAP_MAX_REPORT_LEN;
122
69.4k
                dev->tx_len = CTAP_MAX_REPORT_LEN;
123
69.4k
        } else {
124
0
                dev->rx_len = fido_hid_report_in_len(dev->io_handle);
125
0
                dev->tx_len = fido_hid_report_out_len(dev->io_handle);
126
0
        }
127
128
69.4k
#ifdef FIDO_FUZZ
129
69.4k
        set_random_report_len(dev);
130
69.4k
#endif
131
132
69.4k
        if (dev->rx_len < CTAP_MIN_REPORT_LEN ||
133
69.4k
            dev->rx_len > CTAP_MAX_REPORT_LEN) {
134
0
                fido_log_debug("%s: invalid rx_len %zu", __func__, dev->rx_len);
135
0
                r = FIDO_ERR_RX;
136
0
                goto fail;
137
0
        }
138
139
69.4k
        if (dev->tx_len < CTAP_MIN_REPORT_LEN ||
140
69.4k
            dev->tx_len > CTAP_MAX_REPORT_LEN) {
141
0
                fido_log_debug("%s: invalid tx_len %zu", __func__, dev->tx_len);
142
0
                r = FIDO_ERR_TX;
143
0
                goto fail;
144
0
        }
145
146
69.4k
        if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce),
147
69.4k
            ms) < 0) {
148
1.54k
                fido_log_debug("%s: fido_tx", __func__);
149
1.54k
                r = FIDO_ERR_TX;
150
1.54k
                goto fail;
151
1.54k
        }
152
153
67.9k
        return (FIDO_OK);
154
1.54k
fail:
155
1.54k
        dev->io.close(dev->io_handle);
156
1.54k
        dev->io_handle = NULL;
157
158
1.54k
        return (r);
159
69.4k
}
160
161
static int
162
fido_dev_open_rx(fido_dev_t *dev, int *ms)
163
67.9k
{
164
67.9k
        fido_cbor_info_t        *info = NULL;
165
67.9k
        int                      reply_len;
166
67.9k
        int                      r;
167
168
67.9k
        if ((reply_len = fido_rx(dev, CTAP_CMD_INIT, &dev->attr,
169
67.9k
            sizeof(dev->attr), ms)) < 0) {
170
42.2k
                fido_log_debug("%s: fido_rx", __func__);
171
42.2k
                r = FIDO_ERR_RX;
172
42.2k
                goto fail;
173
42.2k
        }
174
175
25.6k
#ifdef FIDO_FUZZ
176
25.6k
        dev->attr.nonce = dev->nonce;
177
25.6k
#endif
178
179
25.6k
        if ((size_t)reply_len != sizeof(dev->attr) ||
180
25.6k
            dev->attr.nonce != dev->nonce) {
181
540
                fido_log_debug("%s: invalid nonce", __func__);
182
540
                r = FIDO_ERR_RX;
183
540
                goto fail;
184
540
        }
185
186
25.1k
        dev->flags = 0;
187
25.1k
        dev->cid = dev->attr.cid;
188
189
25.1k
        if (fido_dev_is_fido2(dev)) {
190
20.3k
                if ((info = fido_cbor_info_new()) == NULL) {
191
60
                        fido_log_debug("%s: fido_cbor_info_new", __func__);
192
60
                        r = FIDO_ERR_INTERNAL;
193
60
                        goto fail;
194
60
                }
195
20.2k
                if ((r = fido_dev_get_cbor_info_wait(dev, info,
196
20.2k
                    ms)) != FIDO_OK) {
197
12.3k
                        fido_log_debug("%s: fido_dev_cbor_info_wait: %d",
198
12.3k
                            __func__, r);
199
12.3k
                        if (disable_u2f_fallback)
200
0
                                goto fail;
201
12.3k
                        fido_log_debug("%s: falling back to u2f", __func__);
202
12.3k
                        fido_dev_force_u2f(dev);
203
12.3k
                } else {
204
7.91k
                        fido_dev_set_flags(dev, info);
205
7.91k
                }
206
20.2k
        }
207
208
25.0k
        if (fido_dev_is_fido2(dev) && info != NULL) {
209
7.91k
                dev->maxmsgsize = fido_cbor_info_maxmsgsiz(info);
210
7.91k
                fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__,
211
7.91k
                    FIDO_MAXMSG, (unsigned long)dev->maxmsgsize);
212
7.91k
        }
213
214
25.0k
        r = FIDO_OK;
215
67.9k
fail:
216
67.9k
        fido_cbor_info_free(&info);
217
218
67.9k
        if (r != FIDO_OK) {
219
42.8k
                dev->io.close(dev->io_handle);
220
42.8k
                dev->io_handle = NULL;
221
42.8k
        }
222
223
67.9k
        return (r);
224
25.0k
}
225
226
static int
227
fido_dev_open_wait(fido_dev_t *dev, const char *path, int *ms)
228
69.4k
{
229
69.4k
        int r;
230
231
#ifdef USE_WINHELLO
232
        if (strcmp(path, FIDO_WINHELLO_PATH) == 0)
233
                return (fido_winhello_open(dev));
234
#endif
235
69.4k
        if ((r = fido_dev_open_tx(dev, path, ms)) != FIDO_OK ||
236
69.4k
            (r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
237
44.4k
                return (r);
238
239
25.0k
        return (FIDO_OK);
240
69.4k
}
241
242
static void
243
run_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen,
244
    const char *type, int (*manifest)(fido_dev_info_t *, size_t, size_t *))
245
3.68k
{
246
3.68k
        size_t ndevs = 0;
247
3.68k
        int r;
248
249
3.68k
        if (*olen >= ilen) {
250
85
                fido_log_debug("%s: skipping %s", __func__, type);
251
85
                return;
252
85
        }
253
3.59k
        if ((r = manifest(devlist + *olen, ilen - *olen, &ndevs)) != FIDO_OK)
254
38
                fido_log_debug("%s: %s: 0x%x", __func__, type, r);
255
3.59k
        fido_log_debug("%s: found %zu %s device%s", __func__, ndevs, type,
256
3.59k
            ndevs == 1 ? "" : "s");
257
3.59k
        *olen += ndevs;
258
3.59k
}
259
260
int
261
fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
262
1.22k
{
263
1.22k
        *olen = 0;
264
265
1.22k
        run_manifest(devlist, ilen, olen, "hid", fido_hid_manifest);
266
1.22k
#ifdef USE_NFC
267
1.22k
        run_manifest(devlist, ilen, olen, "nfc", fido_nfc_manifest);
268
1.22k
#endif
269
1.22k
#ifdef USE_PCSC
270
1.22k
        run_manifest(devlist, ilen, olen, "pcsc", fido_pcsc_manifest);
271
1.22k
#endif
272
#ifdef USE_WINHELLO
273
        run_manifest(devlist, ilen, olen, "winhello", fido_winhello_manifest);
274
#endif
275
276
1.22k
        return (FIDO_OK);
277
1.22k
}
278
279
int
280
fido_dev_open_with_info(fido_dev_t *dev)
281
0
{
282
0
        int ms = dev->timeout_ms;
283
284
0
        if (dev->path == NULL)
285
0
                return (FIDO_ERR_INVALID_ARGUMENT);
286
287
0
        return (fido_dev_open_wait(dev, dev->path, &ms));
288
0
}
289
290
int
291
fido_dev_open(fido_dev_t *dev, const char *path)
292
69.4k
{
293
69.4k
        int ms = dev->timeout_ms;
294
295
69.4k
#ifdef USE_NFC
296
69.4k
        if (fido_is_nfc(path) && fido_dev_set_nfc(dev) < 0) {
297
0
                fido_log_debug("%s: fido_dev_set_nfc", __func__);
298
0
                return FIDO_ERR_INTERNAL;
299
0
        }
300
69.4k
#endif
301
69.4k
#ifdef USE_PCSC
302
69.4k
        if (fido_is_pcsc(path) && fido_dev_set_pcsc(dev) < 0) {
303
0
                fido_log_debug("%s: fido_dev_set_pcsc", __func__);
304
0
                return FIDO_ERR_INTERNAL;
305
0
        }
306
69.4k
#endif
307
308
69.4k
        return (fido_dev_open_wait(dev, path, &ms));
309
69.4k
}
310
311
int
312
fido_dev_close(fido_dev_t *dev)
313
25.0k
{
314
#ifdef USE_WINHELLO
315
        if (dev->flags & FIDO_DEV_WINHELLO)
316
                return (fido_winhello_close(dev));
317
#endif
318
25.0k
        if (dev->io_handle == NULL || dev->io.close == NULL)
319
0
                return (FIDO_ERR_INVALID_ARGUMENT);
320
321
25.0k
        dev->io.close(dev->io_handle);
322
25.0k
        dev->io_handle = NULL;
323
25.0k
        dev->cid = CTAP_CID_BROADCAST;
324
325
25.0k
        return (FIDO_OK);
326
25.0k
}
327
328
int
329
fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask)
330
0
{
331
0
        if (dev->io_handle == NULL || sigmask == NULL)
332
0
                return (FIDO_ERR_INVALID_ARGUMENT);
333
334
0
#ifdef USE_NFC
335
0
        if (dev->transport.rx == fido_nfc_rx && dev->io.read == fido_nfc_read)
336
0
                return (fido_nfc_set_sigmask(dev->io_handle, sigmask));
337
0
#endif
338
0
        if (dev->transport.rx == NULL && dev->io.read == fido_hid_read)
339
0
                return (fido_hid_set_sigmask(dev->io_handle, sigmask));
340
341
0
        return (FIDO_ERR_INVALID_ARGUMENT);
342
0
}
343
344
int
345
fido_dev_cancel(fido_dev_t *dev)
346
6.56k
{
347
6.56k
        int ms = dev->timeout_ms;
348
349
#ifdef USE_WINHELLO
350
        if (dev->flags & FIDO_DEV_WINHELLO)
351
                return (fido_winhello_cancel(dev));
352
#endif
353
6.56k
        if (fido_dev_is_fido2(dev) == false)
354
4.21k
                return (FIDO_ERR_INVALID_ARGUMENT);
355
2.35k
        if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0, &ms) < 0)
356
101
                return (FIDO_ERR_TX);
357
358
2.25k
        return (FIDO_OK);
359
2.35k
}
360
361
int
362
fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
363
69.4k
{
364
69.4k
        if (dev->io_handle != NULL) {
365
0
                fido_log_debug("%s: non-NULL handle", __func__);
366
0
                return (FIDO_ERR_INVALID_ARGUMENT);
367
0
        }
368
369
69.4k
        if (io == NULL || io->open == NULL || io->close == NULL ||
370
69.4k
            io->read == NULL || io->write == NULL) {
371
0
                fido_log_debug("%s: NULL function", __func__);
372
0
                return (FIDO_ERR_INVALID_ARGUMENT);
373
0
        }
374
375
69.4k
        dev->io = *io;
376
69.4k
        dev->io_own = true;
377
378
69.4k
        return (FIDO_OK);
379
69.4k
}
380
381
int
382
fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t)
383
813
{
384
813
        if (dev->io_handle != NULL) {
385
0
                fido_log_debug("%s: non-NULL handle", __func__);
386
0
                return (FIDO_ERR_INVALID_ARGUMENT);
387
0
        }
388
389
813
        dev->transport = *t;
390
813
        dev->io_own = true;
391
392
813
        return (FIDO_OK);
393
813
}
394
395
void *
396
fido_dev_io_handle(const fido_dev_t *dev)
397
0
{
398
399
0
        return (dev->io_handle);
400
0
}
401
402
void
403
fido_init(int flags)
404
17.9k
{
405
17.9k
        if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
406
17.9k
                fido_log_init();
407
408
17.9k
        disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK);
409
17.9k
}
410
411
fido_dev_t *
412
fido_dev_new(void)
413
69.7k
{
414
69.7k
        fido_dev_t *dev;
415
416
69.7k
        if ((dev = calloc(1, sizeof(*dev))) == NULL)
417
251
                return (NULL);
418
419
69.4k
        dev->cid = CTAP_CID_BROADCAST;
420
69.4k
        dev->timeout_ms = -1;
421
69.4k
        dev->io = (fido_dev_io_t) {
422
69.4k
                &fido_hid_open,
423
69.4k
                &fido_hid_close,
424
69.4k
                &fido_hid_read,
425
69.4k
                &fido_hid_write,
426
69.4k
        };
427
428
69.4k
        return (dev);
429
69.7k
}
430
431
fido_dev_t *
432
fido_dev_new_with_info(const fido_dev_info_t *di)
433
0
{
434
0
        fido_dev_t *dev;
435
436
0
        if ((dev = calloc(1, sizeof(*dev))) == NULL)
437
0
                return (NULL);
438
439
#if 0
440
        if (di->io.open == NULL || di->io.close == NULL ||
441
            di->io.read == NULL || di->io.write == NULL) {
442
                fido_log_debug("%s: NULL function", __func__);
443
                fido_dev_free(&dev);
444
                return (NULL);
445
        }
446
#endif
447
448
0
        dev->io = di->io;
449
0
        dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL;
450
0
        dev->transport = di->transport;
451
0
        dev->cid = CTAP_CID_BROADCAST;
452
0
        dev->timeout_ms = -1;
453
454
0
        if ((dev->path = strdup(di->path)) == NULL) {
455
0
                fido_log_debug("%s: strdup", __func__);
456
0
                fido_dev_free(&dev);
457
0
                return (NULL);
458
0
        }
459
460
0
        return (dev);
461
0
}
462
463
void
464
fido_dev_free(fido_dev_t **dev_p)
465
78.5k
{
466
78.5k
        fido_dev_t *dev;
467
468
78.5k
        if (dev_p == NULL || (dev = *dev_p) == NULL)
469
9.12k
                return;
470
471
69.4k
        free(dev->path);
472
69.4k
        free(dev);
473
474
69.4k
        *dev_p = NULL;
475
69.4k
}
476
477
uint8_t
478
fido_dev_protocol(const fido_dev_t *dev)
479
292
{
480
292
        return (dev->attr.protocol);
481
292
}
482
483
uint8_t
484
fido_dev_major(const fido_dev_t *dev)
485
292
{
486
292
        return (dev->attr.major);
487
292
}
488
489
uint8_t
490
fido_dev_minor(const fido_dev_t *dev)
491
292
{
492
292
        return (dev->attr.minor);
493
292
}
494
495
uint8_t
496
fido_dev_build(const fido_dev_t *dev)
497
292
{
498
292
        return (dev->attr.build);
499
292
}
500
501
uint8_t
502
fido_dev_flags(const fido_dev_t *dev)
503
292
{
504
292
        return (dev->attr.flags);
505
292
}
506
507
bool
508
fido_dev_is_fido2(const fido_dev_t *dev)
509
77.1k
{
510
77.1k
        return (dev->attr.flags & FIDO_CAP_CBOR);
511
77.1k
}
512
513
bool
514
fido_dev_is_winhello(const fido_dev_t *dev)
515
0
{
516
0
        return (dev->flags & FIDO_DEV_WINHELLO);
517
0
}
518
519
bool
520
fido_dev_supports_pin(const fido_dev_t *dev)
521
3.01k
{
522
3.01k
        return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET));
523
3.01k
}
524
525
bool
526
fido_dev_has_pin(const fido_dev_t *dev)
527
2.86k
{
528
2.86k
        return (dev->flags & FIDO_DEV_PIN_SET);
529
2.86k
}
530
531
bool
532
fido_dev_supports_cred_prot(const fido_dev_t *dev)
533
3.86k
{
534
3.86k
        return (dev->flags & FIDO_DEV_CRED_PROT);
535
3.86k
}
536
537
bool
538
fido_dev_supports_credman(const fido_dev_t *dev)
539
3.86k
{
540
3.86k
        return (dev->flags & FIDO_DEV_CREDMAN);
541
3.86k
}
542
543
bool
544
fido_dev_supports_uv(const fido_dev_t *dev)
545
2.86k
{
546
2.86k
        return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET));
547
2.86k
}
548
549
bool
550
fido_dev_has_uv(const fido_dev_t *dev)
551
2.94k
{
552
2.94k
        return (dev->flags & FIDO_DEV_UV_SET);
553
2.94k
}
554
555
bool
556
fido_dev_supports_permissions(const fido_dev_t *dev)
557
5.48k
{
558
5.48k
        return (dev->flags & FIDO_DEV_TOKEN_PERMS);
559
5.48k
}
560
561
void
562
fido_dev_force_u2f(fido_dev_t *dev)
563
14.0k
{
564
14.0k
        dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR;
565
14.0k
        dev->flags = 0;
566
14.0k
}
567
568
void
569
fido_dev_force_fido2(fido_dev_t *dev)
570
0
{
571
0
        dev->attr.flags |= FIDO_CAP_CBOR;
572
0
}
573
574
uint8_t
575
fido_dev_get_pin_protocol(const fido_dev_t *dev)
576
23.7k
{
577
23.7k
        if (dev->flags & FIDO_DEV_PIN_PROTOCOL2)
578
1.83k
                return (CTAP_PIN_PROTOCOL2);
579
21.9k
        else if (dev->flags & FIDO_DEV_PIN_PROTOCOL1)
580
20.0k
                return (CTAP_PIN_PROTOCOL1);
581
582
1.91k
        return (0);
583
23.7k
}
584
585
uint64_t
586
fido_dev_maxmsgsize(const fido_dev_t *dev)
587
2.09k
{
588
2.09k
        return (dev->maxmsgsize);
589
2.09k
}
590
591
int
592
fido_dev_set_timeout(fido_dev_t *dev, int ms)
593
69.4k
{
594
69.4k
        if (ms < -1)
595
0
                return (FIDO_ERR_INVALID_ARGUMENT);
596
597
69.4k
        dev->timeout_ms = ms;
598
599
69.4k
        return (FIDO_OK);
600
69.4k
}