CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
tlv.cpp
Go to the documentation of this file.
1
4
6
7#include <string.h>
8
9size_t tlv_tag_size(uint16_t tag) {
10 return (tag > 0xFF) ? 2 : 1;
11}
12
13size_t tlv_len_size(size_t value_len) {
14 if (value_len < 0x80) return 1;
15 if (value_len <= 0xFF) return 2;
16 return 3;
17}
18
19tlv_status_t tlv_write_tag(uint8_t *buf, size_t buf_max, uint16_t tag,
20 size_t *written) {
21 if (!buf || !written) return TLV_ERR_NULL;
22 const size_t need = tlv_tag_size(tag);
23 if (buf_max < need) return TLV_ERR_BUF_TOO_SMALL;
24 if (need == 2) {
25 buf[0] = static_cast<uint8_t>((tag >> 8) & 0xFF);
26 buf[1] = static_cast<uint8_t>(tag & 0xFF);
27 } else {
28 buf[0] = static_cast<uint8_t>(tag & 0xFF);
29 }
30 *written = need;
31 return TLV_OK;
32}
33
34tlv_status_t tlv_write_len(uint8_t *buf, size_t buf_max, size_t value_len,
35 size_t *written) {
36 if (!buf || !written) return TLV_ERR_NULL;
37 if (value_len > 0xFFFF) return TLV_ERR_BAD_LENGTH;
38 const size_t need = tlv_len_size(value_len);
39 if (buf_max < need) return TLV_ERR_BUF_TOO_SMALL;
40 if (need == 1) {
41 buf[0] = static_cast<uint8_t>(value_len);
42 } else if (need == 2) {
43 buf[0] = 0x81;
44 buf[1] = static_cast<uint8_t>(value_len);
45 } else {
46 buf[0] = 0x82;
47 buf[1] = static_cast<uint8_t>((value_len >> 8) & 0xFF);
48 buf[2] = static_cast<uint8_t>(value_len & 0xFF);
49 }
50 *written = need;
51 return TLV_OK;
52}
53
54tlv_status_t tlv_build(uint8_t *buf, size_t buf_max, uint16_t tag,
55 const uint8_t *value, size_t value_len, size_t *written) {
56 if (!buf || !written) return TLV_ERR_NULL;
57 if (value_len > 0 && !value) return TLV_ERR_NULL;
58
59 size_t pos = 0;
60 size_t n = 0;
61 tlv_status_t st = tlv_write_tag(buf, buf_max, tag, &n);
62 if (st != TLV_OK) return st;
63 pos += n;
64 st = tlv_write_len(buf + pos, buf_max - pos, value_len, &n);
65 if (st != TLV_OK) return st;
66 pos += n;
67 if (value_len > 0) {
68 if (buf_max - pos < value_len) return TLV_ERR_BUF_TOO_SMALL;
69 memcpy(buf + pos, value, value_len);
70 pos += value_len;
71 }
72 *written = pos;
73 return TLV_OK;
74}
75
76tlv_status_t tlv_read_tag(const uint8_t *buf, size_t buf_len, size_t *pos,
77 uint16_t *tag_out) {
78 if (!buf || !pos || !tag_out) return TLV_ERR_NULL;
79 if (*pos >= buf_len) return TLV_ERR_BUF_TOO_SMALL;
80
81 const uint8_t b0 = buf[*pos];
82 // ISO 7816-4 Annex D: if bits 1..5 of the first tag byte are all set,
83 // a second tag byte follows.
84 if ((b0 & 0x1F) == 0x1F) {
85 if (*pos + 1 >= buf_len) return TLV_ERR_BUF_TOO_SMALL;
86 const uint8_t b1 = buf[*pos + 1];
87 // OpenPGP tags fit in two bytes; reject multi-byte continuation.
88 if (b1 & 0x80) return TLV_ERR_BAD_TAG;
89 *tag_out = static_cast<uint16_t>((b0 << 8) | b1);
90 *pos += 2;
91 } else {
92 *tag_out = b0;
93 *pos += 1;
94 }
95 return TLV_OK;
96}
97
98tlv_status_t tlv_read_len(const uint8_t *buf, size_t buf_len, size_t *pos,
99 size_t *length_out) {
100 if (!buf || !pos || !length_out) return TLV_ERR_NULL;
101 if (*pos >= buf_len) return TLV_ERR_BUF_TOO_SMALL;
102
103 const uint8_t first = buf[*pos];
104 if (first < 0x80) {
105 *length_out = first;
106 *pos += 1;
107 return TLV_OK;
108 }
109 const uint8_t n = first & 0x7F;
110 if (n == 0 || n > 2) return TLV_ERR_BAD_LENGTH; // OpenPGP uses at most 0x82
111 if (*pos + 1 + n > buf_len) return TLV_ERR_BUF_TOO_SMALL;
112
113 size_t length = 0;
114 for (uint8_t i = 0; i < n; ++i) {
115 length = (length << 8) | buf[*pos + 1 + i];
116 }
117 *length_out = length;
118 *pos += 1 + n;
119 return TLV_OK;
120}
121
122tlv_status_t tlv_parse(const uint8_t *buf, size_t buf_len, size_t *pos, tlv_t *out) {
123 if (!buf || !pos || !out) return TLV_ERR_NULL;
124 const size_t start = *pos;
125
126 uint16_t tag = 0;
127 tlv_status_t st = tlv_read_tag(buf, buf_len, pos, &tag);
128 if (st != TLV_OK) return st;
129
130 size_t length = 0;
131 st = tlv_read_len(buf, buf_len, pos, &length);
132 if (st != TLV_OK) return st;
133
134 if (*pos + length > buf_len) return TLV_ERR_BUF_TOO_SMALL;
135
136 out->tag = tag;
137 out->length = length;
138 out->value = (length > 0) ? buf + *pos : nullptr;
139 *pos += length;
140 out->total_size = *pos - start;
141 return TLV_OK;
142}
Parsed TLV: pointers alias into the caller-supplied buffer.
Definition tlv.h:123
size_t length
Definition tlv.h:125
size_t total_size
Definition tlv.h:127
uint16_t tag
Definition tlv.h:124
const uint8_t * value
Definition tlv.h:126
tlv_status_t tlv_parse(const uint8_t *buf, size_t buf_len, size_t *pos, tlv_t *out)
Parse a single TLV starting at pos. On success pos advances past the entire field and out is filled.
Definition tlv.cpp:122
tlv_status_t tlv_write_tag(uint8_t *buf, size_t buf_max, uint16_t tag, size_t *written)
Encode a tag. Writes either one or two bytes depending on the magnitude of tag.
Definition tlv.cpp:19
tlv_status_t tlv_build(uint8_t *buf, size_t buf_max, uint16_t tag, const uint8_t *value, size_t value_len, size_t *written)
Build a complete TLV: tag, length, and value (which may be NULL when value_len == 0).
Definition tlv.cpp:54
tlv_status_t tlv_write_len(uint8_t *buf, size_t buf_max, size_t value_len, size_t *written)
Encode a BER definite length field.
Definition tlv.cpp:34
tlv_status_t tlv_read_tag(const uint8_t *buf, size_t buf_len, size_t *pos, uint16_t *tag_out)
Read a tag from the input buffer at pos.
Definition tlv.cpp:76
size_t tlv_len_size(size_t value_len)
Compute the BER definite-length encoded size for a length value.
Definition tlv.cpp:13
tlv_status_t tlv_read_len(const uint8_t *buf, size_t buf_len, size_t *pos, size_t *length_out)
Read a BER definite length field starting at pos.
Definition tlv.cpp:98
size_t tlv_tag_size(uint16_t tag)
BER-TLV codec implementation (see tlv.h).
Definition tlv.cpp:9
tlv_status_t
BER-TLV codec for OpenPGP Data Objects.
Definition tlv.h:32
@ TLV_ERR_BUF_TOO_SMALL
Definition tlv.h:34
@ TLV_ERR_BAD_TAG
Definition tlv.h:36
@ TLV_ERR_NULL
Definition tlv.h:37
@ TLV_ERR_BAD_LENGTH
Definition tlv.h:35
@ TLV_OK
Definition tlv.h:33