CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
cbor_helpers.cpp
Go to the documentation of this file.
1
5
7#include "cdc_log.h"
8#include <string.h>
9
10static const char* TAG = "CBOR";
11
13
20void cbor_writer_init(cbor_writer_t *w, uint8_t *buffer, size_t size) {
21 w->buffer = buffer;
22 w->size = size;
23 w->offset = 0;
24 w->error = false;
25}
26
32size_t cbor_writer_length(const cbor_writer_t *w) {
33 return w->offset;
34}
35
41bool cbor_writer_error(const cbor_writer_t *w) {
42 return w->error;
43}
44
50static void write_byte(cbor_writer_t *w, uint8_t b) {
51 if (w->error) return;
52 if (w->offset >= w->size) {
53 w->error = true;
54 LOG_E(TAG, "Write overflow");
55 return;
56 }
57 w->buffer[w->offset++] = b;
58}
59
66static void write_bytes(cbor_writer_t *w, const uint8_t *data, size_t len) {
67 if (w->error) return;
68 if (w->offset + len > w->size) {
69 w->error = true;
70 LOG_E(TAG, "Write overflow (need %d, have %d)", len, w->size - w->offset);
71 return;
72 }
73 memcpy(w->buffer + w->offset, data, len);
74 w->offset += len;
75}
76
83static void write_type_value(cbor_writer_t *w, uint8_t type, uint64_t value) {
84 uint8_t major = type << 5;
85
86 if (value < 24) {
87 write_byte(w, major | (uint8_t)value);
88 } else if (value <= 0xFF) {
89 write_byte(w, major | 24);
90 write_byte(w, (uint8_t)value);
91 } else if (value <= 0xFFFF) {
92 write_byte(w, major | 25);
93 write_byte(w, (value >> 8) & 0xFF);
94 write_byte(w, value & 0xFF);
95 } else if (value <= 0xFFFFFFFF) {
96 write_byte(w, major | 26);
97 write_byte(w, (value >> 24) & 0xFF);
98 write_byte(w, (value >> 16) & 0xFF);
99 write_byte(w, (value >> 8) & 0xFF);
100 write_byte(w, value & 0xFF);
101 } else {
102 write_byte(w, major | 27);
103 write_byte(w, (value >> 56) & 0xFF);
104 write_byte(w, (value >> 48) & 0xFF);
105 write_byte(w, (value >> 40) & 0xFF);
106 write_byte(w, (value >> 32) & 0xFF);
107 write_byte(w, (value >> 24) & 0xFF);
108 write_byte(w, (value >> 16) & 0xFF);
109 write_byte(w, (value >> 8) & 0xFF);
110 write_byte(w, value & 0xFF);
111 }
112}
113
119void cbor_encode_uint(cbor_writer_t *w, uint64_t value) {
121}
122
128void cbor_encode_int(cbor_writer_t *w, int64_t value) {
129 if (value >= 0) {
130 write_type_value(w, CBOR_UNSIGNED, (uint64_t)value);
131 } else {
132 write_type_value(w, CBOR_NEGATIVE, (uint64_t)(-1 - value));
133 }
134}
135
142void cbor_encode_bytes(cbor_writer_t *w, const uint8_t *data, size_t len) {
144 if (len > 0 && data) {
145 write_bytes(w, data, len);
146 }
147}
148
154void cbor_encode_text(cbor_writer_t *w, const char *str) {
155 size_t len = str ? strlen(str) : 0;
157 if (len > 0) {
158 write_bytes(w, (const uint8_t *)str, len);
159 }
160}
161
168void cbor_encode_text_len(cbor_writer_t *w, const char *str, size_t len) {
170 if (len > 0 && str) {
171 write_bytes(w, (const uint8_t *)str, len);
172 }
173}
174
180void cbor_encode_bool(cbor_writer_t *w, bool value) {
181 write_byte(w, value ? CBOR_TRUE : CBOR_FALSE);
182}
183
188void cbor_encode_null(cbor_writer_t *w) {
190}
191
197void cbor_encode_array(cbor_writer_t *w, size_t count) {
198 write_type_value(w, CBOR_ARRAY, count);
199}
200
206void cbor_encode_map(cbor_writer_t *w, size_t count) {
207 write_type_value(w, CBOR_MAP, count);
208}
209
216void cbor_encode_cose_key_p256(cbor_writer_t *w, const uint8_t *x, const uint8_t *y) {
217 // COSE_Key for P-256:
218 // {
219 // 1: 2, // kty: EC2
220 // 3: -7, // alg: ES256
221 // -1: 1, // crv: P-256
222 // -2: x, // x coordinate (32 bytes)
223 // -3: y // y coordinate (32 bytes)
224 // }
225 cbor_encode_map(w, 5);
226
227 cbor_encode_uint(w, 1); // kty
228 cbor_encode_uint(w, 2); // EC2
229
230 cbor_encode_uint(w, 3); // alg
231 cbor_encode_int(w, -7); // ES256
232
233 cbor_encode_int(w, -1); // crv
234 cbor_encode_uint(w, 1); // P-256
235
236 cbor_encode_int(w, -2); // x
237 cbor_encode_bytes(w, x, 32);
238
239 cbor_encode_int(w, -3); // y
240 cbor_encode_bytes(w, y, 32);
241}
242
248void cbor_encode_cose_key_ed25519(cbor_writer_t *w, const uint8_t *pubkey) {
249 // COSE_Key for Ed25519 (OKP - Octet Key Pair):
250 // {
251 // 1: 1, // kty: OKP
252 // 3: -8, // alg: EdDSA
253 // -1: 6, // crv: Ed25519
254 // -2: x // x: public key (32 bytes)
255 // }
256 cbor_encode_map(w, 4);
257
258 cbor_encode_uint(w, 1); // kty
259 cbor_encode_uint(w, 1); // OKP (Octet Key Pair)
260
261 cbor_encode_uint(w, 3); // alg
262 cbor_encode_int(w, -8); // EdDSA
263
264 cbor_encode_int(w, -1); // crv
265 cbor_encode_uint(w, 6); // Ed25519
266
267 cbor_encode_int(w, -2); // x (public key)
268 cbor_encode_bytes(w, pubkey, 32);
269}
270
272
279void cbor_reader_init(cbor_reader_t *r, const uint8_t *data, size_t size) {
280 r->data = data;
281 r->size = size;
282 r->offset = 0;
283 r->error = false;
284}
285
291bool cbor_reader_error(const cbor_reader_t *r) {
292 return r->error;
293}
294
300bool cbor_reader_available(const cbor_reader_t *r) {
301 return !r->error && r->offset < r->size;
302}
303
309int cbor_reader_peek_type(const cbor_reader_t *r) {
310 if (r->error || r->offset >= r->size) return -1;
311 return r->data[r->offset] >> 5;
312}
313
320static bool read_byte(cbor_reader_t *r, uint8_t *b) {
321 if (r->error) return false;
322 if (r->offset >= r->size) {
323 r->error = true;
324 LOG_E(TAG, "Read underflow");
325 return false;
326 }
327 *b = r->data[r->offset++];
328 return true;
329}
330
338static bool read_type_value(cbor_reader_t *r, uint8_t *type, uint64_t *value) {
339 uint8_t initial;
340 if (!read_byte(r, &initial)) return false;
341
342 *type = initial >> 5;
343 uint8_t info = initial & 0x1F;
344
345 if (info < 24) {
346 *value = info;
347 } else if (info == 24) {
348 uint8_t b;
349 if (!read_byte(r, &b)) return false;
350 *value = b;
351 } else if (info == 25) {
352 uint8_t b[2];
353 if (!read_byte(r, &b[0]) || !read_byte(r, &b[1])) return false;
354 *value = ((uint16_t)b[0] << 8) | b[1];
355 } else if (info == 26) {
356 uint8_t b[4];
357 for (int i = 0; i < 4; i++) {
358 if (!read_byte(r, &b[i])) return false;
359 }
360 *value = ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) |
361 ((uint32_t)b[2] << 8) | b[3];
362 } else if (info == 27) {
363 uint8_t b[8];
364 for (int i = 0; i < 8; i++) {
365 if (!read_byte(r, &b[i])) return false;
366 }
367 *value = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) |
368 ((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32) |
369 ((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
370 ((uint64_t)b[6] << 8) | b[7];
371 } else if (info == 31) {
372 // Indefinite length - not supported in CTAP2
373 r->error = true;
374 LOG_E(TAG, "Indefinite length not supported");
375 return false;
376 } else {
377 r->error = true;
378 LOG_E(TAG, "Invalid additional info: %d", info);
379 return false;
380 }
381
382 return true;
383}
384
391bool cbor_read_item(cbor_reader_t *r, cbor_item_t *item) {
392 uint8_t type;
393 uint64_t value;
394
395 if (!read_type_value(r, &type, &value)) return false;
396
397 item->type = type;
398 item->value = value;
399 item->bytes = NULL;
400 item->length = 0;
401
402 // For bytes/text, also read the data pointer
403 if (type == CBOR_BYTES || type == CBOR_TEXT) {
404 if (r->offset + value > r->size) {
405 r->error = true;
406 return false;
407 }
408 item->bytes = r->data + r->offset;
409 item->length = value;
410 r->offset += value;
411 }
412
413 return true;
414}
415
422bool cbor_read_uint(cbor_reader_t *r, uint64_t *value) {
423 cbor_item_t item;
424 if (!cbor_read_item(r, &item)) return false;
425 if (item.type != CBOR_UNSIGNED) {
426 r->error = true;
427 return false;
428 }
429 *value = item.value;
430 return true;
431}
432
439bool cbor_read_int(cbor_reader_t *r, int64_t *value) {
440 cbor_item_t item;
441 if (!cbor_read_item(r, &item)) return false;
442
443 if (item.type == CBOR_UNSIGNED) {
444 *value = (int64_t)item.value;
445 } else if (item.type == CBOR_NEGATIVE) {
446 *value = -1 - (int64_t)item.value;
447 } else {
448 r->error = true;
449 return false;
450 }
451 return true;
452}
453
462bool cbor_read_bytes(cbor_reader_t *r, uint8_t *out, size_t max_len, size_t *out_len) {
463 cbor_item_t item;
464 if (!cbor_read_item(r, &item)) return false;
465 if (item.type != CBOR_BYTES) {
466 r->error = true;
467 return false;
468 }
469
470 size_t copy_len = (item.length < max_len) ? item.length : max_len;
471 if (out && copy_len > 0) {
472 memcpy(out, item.bytes, copy_len);
473 }
474 if (out_len) *out_len = copy_len;
475 return true;
476}
477
486bool cbor_read_text(cbor_reader_t *r, char *out, size_t max_len, size_t *out_len) {
487 if (max_len == 0) return false; // Prevent underflow in (max_len - 1)
488
489 cbor_item_t item;
490 if (!cbor_read_item(r, &item)) return false;
491 if (item.type != CBOR_TEXT) {
492 r->error = true;
493 return false;
494 }
495
496 size_t copy_len = (item.length < max_len - 1) ? item.length : (max_len - 1);
497 if (out && copy_len > 0) {
498 memcpy(out, item.bytes, copy_len);
499 }
500 if (out && max_len > 0) {
501 out[copy_len] = '\0';
502 }
503 if (out_len) *out_len = copy_len;
504 return true;
505}
506
513bool cbor_read_bool(cbor_reader_t *r, bool *value) {
514 uint8_t b;
515 if (!read_byte(r, &b)) return false;
516
517 if (b == CBOR_FALSE) {
518 *value = false;
519 return true;
520 } else if (b == CBOR_TRUE) {
521 *value = true;
522 return true;
523 }
524
525 r->error = true;
526 return false;
527}
528
534int cbor_read_map(cbor_reader_t *r) {
535 cbor_item_t item;
536 if (!cbor_read_item(r, &item)) return -1;
537 if (item.type != CBOR_MAP) {
538 r->error = true;
539 return -1;
540 }
541 return (int)item.value;
542}
543
549int cbor_read_array(cbor_reader_t *r) {
550 cbor_item_t item;
551 if (!cbor_read_item(r, &item)) return -1;
552 if (item.type != CBOR_ARRAY) {
553 r->error = true;
554 return -1;
555 }
556 return (int)item.value;
557}
558
560#define CBOR_MAX_RECURSION_DEPTH 8
561#define CBOR_MAX_CONTAINER_SIZE 256
562
563static bool cbor_skip_item_impl(cbor_reader_t *r, uint8_t depth) {
564 if (depth > CBOR_MAX_RECURSION_DEPTH) {
565 LOG_W(TAG, "Max recursion depth exceeded");
566 return false;
567 }
568
569 cbor_item_t item;
570 if (!cbor_read_item(r, &item)) return false;
571
572 // For containers, skip all nested items with limits
573 if (item.type == CBOR_ARRAY) {
574 if (item.value > CBOR_MAX_CONTAINER_SIZE) {
575 LOG_W(TAG, "Array too large: %llu", (unsigned long long)item.value);
576 return false;
577 }
578 for (uint64_t i = 0; i < item.value; i++) {
579 if (!cbor_skip_item_impl(r, depth + 1)) return false;
580 }
581 } else if (item.type == CBOR_MAP) {
582 if (item.value > CBOR_MAX_CONTAINER_SIZE) {
583 LOG_W(TAG, "Map too large: %llu", (unsigned long long)item.value);
584 return false;
585 }
586 for (uint64_t i = 0; i < item.value * 2; i++) {
587 if (!cbor_skip_item_impl(r, depth + 1)) return false;
588 }
589 }
590 // Bytes/text data already consumed by read_item
591
592 return true;
593}
594
600bool cbor_skip_item(cbor_reader_t *r) {
601 return cbor_skip_item_impl(r, 0);
602}
603
613bool cbor_parse_cose_key(cbor_reader_t *r, int *kty, int *alg,
614 uint8_t *x, uint8_t *y) {
615 int count = cbor_read_map(r);
616 if (count < 0) return false;
617
618 *kty = 0;
619 *alg = 0;
620
621 for (int i = 0; i < count; i++) {
622 int64_t key;
623 if (!cbor_read_int(r, &key)) return false;
624
625 switch (key) {
626 case 1: // kty
627 {
628 uint64_t v;
629 if (!cbor_read_uint(r, &v)) return false;
630 *kty = (int)v;
631 }
632 break;
633 case 3: // alg
634 {
635 int64_t v;
636 if (!cbor_read_int(r, &v)) return false;
637 *alg = (int)v;
638 }
639 break;
640 case -2: // x coordinate
641 {
642 size_t len;
643 if (!cbor_read_bytes(r, x, 32, &len)) return false;
644 }
645 break;
646 case -3: // y coordinate
647 if (y) {
648 size_t len;
649 if (!cbor_read_bytes(r, y, 32, &len)) return false;
650 }
651 break;
652 default:
653 // Skip unknown keys
654 if (!cbor_skip_item(r)) return false;
655 break;
656 }
657 }
658
659 return true;
660}
static const char * TAG
void cbor_encode_text_len(cbor_writer_t *w, const char *str, size_t len)
Encodes CBOR text string with explicit length.
void cbor_encode_cose_key_p256(cbor_writer_t *w, const uint8_t *x, const uint8_t *y)
Encodes COSE P-256 public key map.
void cbor_encode_uint(cbor_writer_t *w, uint64_t value)
Encodes CBOR unsigned integer.
static void write_byte(cbor_writer_t *w, uint8_t b)
Writes one byte to CBOR output buffer.
void cbor_reader_init(cbor_reader_t *r, const uint8_t *data, size_t size)
CBOR reader implementation.
void cbor_encode_bool(cbor_writer_t *w, bool value)
Encodes CBOR boolean.
#define CBOR_MAX_CONTAINER_SIZE
void cbor_encode_cose_key_ed25519(cbor_writer_t *w, const uint8_t *pubkey)
Encodes COSE Ed25519 public key map.
void cbor_writer_init(cbor_writer_t *w, uint8_t *buffer, size_t size)
CBOR writer implementation.
void cbor_encode_null(cbor_writer_t *w)
Encodes CBOR null.
bool cbor_read_text(cbor_reader_t *r, char *out, size_t max_len, size_t *out_len)
Reads CBOR text string into output buffer.
int cbor_read_map(cbor_reader_t *r)
Reads CBOR map header and returns pair count.
static bool cbor_skip_item_impl(cbor_reader_t *r, uint8_t depth)
int cbor_read_array(cbor_reader_t *r)
Reads CBOR array header and returns element count.
void cbor_encode_text(cbor_writer_t *w, const char *str)
Encodes CBOR text string.
bool cbor_skip_item(cbor_reader_t *r)
Skips one complete CBOR item including nested container content.
size_t cbor_writer_length(const cbor_writer_t *w)
Returns number of bytes written by CBOR writer.
static bool read_type_value(cbor_reader_t *r, uint8_t *type, uint64_t *value)
Reads CBOR item header and decoded value.
bool cbor_reader_error(const cbor_reader_t *r)
Returns whether reader is in error state.
static void write_type_value(cbor_writer_t *w, uint8_t type, uint64_t value)
Encodes CBOR major type and value header.
int cbor_reader_peek_type(const cbor_reader_t *r)
Peeks major type of next CBOR item.
bool cbor_writer_error(const cbor_writer_t *w)
Returns whether writer encountered an error.
#define CBOR_MAX_RECURSION_DEPTH
Defensive limits for recursive container skipping.
static bool read_byte(cbor_reader_t *r, uint8_t *b)
Reads one byte from CBOR reader.
void cbor_encode_bytes(cbor_writer_t *w, const uint8_t *data, size_t len)
Encodes CBOR byte-string.
bool cbor_read_bool(cbor_reader_t *r, bool *value)
Reads CBOR boolean simple value.
bool cbor_read_item(cbor_reader_t *r, cbor_item_t *item)
Reads next CBOR item metadata and optional inline payload pointer.
static void write_bytes(cbor_writer_t *w, const uint8_t *data, size_t len)
Writes byte span to CBOR output buffer.
void cbor_encode_array(cbor_writer_t *w, size_t count)
Encodes CBOR array header.
bool cbor_parse_cose_key(cbor_reader_t *r, int *kty, int *alg, uint8_t *x, uint8_t *y)
Parses COSE key map and extracts key type, algorithm, and coordinates.
bool cbor_read_int(cbor_reader_t *r, int64_t *value)
Reads CBOR integer (positive or negative).
bool cbor_read_uint(cbor_reader_t *r, uint64_t *value)
Reads CBOR unsigned integer.
void cbor_encode_int(cbor_writer_t *w, int64_t value)
Encodes CBOR signed integer.
bool cbor_read_bytes(cbor_reader_t *r, uint8_t *out, size_t max_len, size_t *out_len)
Reads CBOR byte-string into optional output buffer.
bool cbor_reader_available(const cbor_reader_t *r)
Returns whether unread data remains.
void cbor_encode_map(cbor_writer_t *w, size_t count)
Encodes CBOR map header.
#define CBOR_NULL
#define CBOR_FALSE
#define CBOR_UNSIGNED
#define CBOR_NEGATIVE
#define CBOR_MAP
#define CBOR_TRUE
#define CBOR_ARRAY
#define CBOR_BYTES
#define CBOR_TEXT
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145