Coverage Report

Created: 2023-09-23 17:42

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