CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
t1_block.cpp
Go to the documentation of this file.
1
6
8
9#include <string.h>
10
11namespace {
12
13constexpr uint8_t kPcbBlockKindMask = 0xC0;
14constexpr uint8_t kPcbIBlock = 0x00; // bit 8 = 0
15constexpr uint8_t kPcbRBlock = 0x80; // bits 8-7 = 10
16constexpr uint8_t kPcbSBlock = 0xC0; // bits 8-7 = 11
17
18// I-block PCB bits.
19constexpr uint8_t kPcbINs = 0x40; // bit 7
20constexpr uint8_t kPcbIMore = 0x20; // bit 6
21constexpr uint8_t kPcbIReservedMask = 0x1F; // bits 5-1 must be zero
22
23// R-block PCB bits.
24constexpr uint8_t kPcbRNr = 0x10; // bit 5
25constexpr uint8_t kPcbRErrMask = 0x03; // bits 2-1
26constexpr uint8_t kPcbRReservedMask = 0x2C; // bits 6, 4, 3 must be zero
27
28// S-block PCB bits.
29constexpr uint8_t kPcbSSubtypeMask = 0x3F; // bits 6-1 carry the subtype
30
31} // namespace
32
33uint8_t t1_lrc(const uint8_t *buf, size_t len) {
34 uint8_t lrc = 0;
35 for (size_t i = 0; i < len; ++i) {
36 lrc ^= buf[i];
37 }
38 return lrc;
39}
40
41uint16_t t1_crc(const uint8_t *buf, size_t len) {
42 // ISO/IEC 13239 (a.k.a. CRC-CCITT, polynomial 0x1021), initial 0xFFFF,
43 // no final XOR, MSB-first.
44 uint16_t crc = 0xFFFF;
45 for (size_t i = 0; i < len; ++i) {
46 crc ^= static_cast<uint16_t>(buf[i]) << 8;
47 for (int bit = 0; bit < 8; ++bit) {
48 if (crc & 0x8000) {
49 crc = static_cast<uint16_t>((crc << 1) ^ 0x1021);
50 } else {
51 crc = static_cast<uint16_t>(crc << 1);
52 }
53 }
54 }
55 return crc;
56}
57
58static t1_status_t append_edc(uint8_t *out, size_t prologue_and_inf_len,
59 bool use_crc, size_t out_cap, size_t *out_len) {
60 const size_t edc_len = use_crc ? 2 : 1;
61 if (prologue_and_inf_len + edc_len > out_cap) {
62 return T1_ERR_OUT_BUF;
63 }
64 if (use_crc) {
65 uint16_t crc = t1_crc(out, prologue_and_inf_len);
66 out[prologue_and_inf_len] = static_cast<uint8_t>((crc >> 8) & 0xFF);
67 out[prologue_and_inf_len + 1] = static_cast<uint8_t>(crc & 0xFF);
68 } else {
69 out[prologue_and_inf_len] = t1_lrc(out, prologue_and_inf_len);
70 }
71 *out_len = prologue_and_inf_len + edc_len;
72 return T1_OK;
73}
74
75t1_status_t t1_block_encode_i(uint8_t nad, uint8_t ns, bool more,
76 const uint8_t *inf, uint8_t inf_len,
77 bool use_crc,
78 uint8_t *out, size_t out_cap, size_t *out_len) {
79 if (!out || !out_len) return T1_ERR_NULL;
80 if (inf_len > T1_INF_MAX) return T1_ERR_INF_TOO_BIG;
81 if (inf_len > 0 && !inf) return T1_ERR_NULL;
82 if (ns > 1) return T1_ERR_PCB;
83
84 const size_t prologue = 3;
85 if (prologue + inf_len > out_cap) {
86 return T1_ERR_OUT_BUF;
87 }
88
89 uint8_t pcb = kPcbIBlock;
90 if (ns) pcb |= kPcbINs;
91 if (more) pcb |= kPcbIMore;
92
93 out[0] = nad;
94 out[1] = pcb;
95 out[2] = inf_len;
96 if (inf_len > 0) {
97 memcpy(out + prologue, inf, inf_len);
98 }
99
100 return append_edc(out, prologue + inf_len, use_crc, out_cap, out_len);
101}
102
103t1_status_t t1_block_encode_r(uint8_t nad, uint8_t nr, t1_r_error_t err,
104 bool use_crc,
105 uint8_t *out, size_t out_cap, size_t *out_len) {
106 if (!out || !out_len) return T1_ERR_NULL;
107 if (nr > 1) return T1_ERR_PCB;
108 if (static_cast<uint8_t>(err) > 0x02) return T1_ERR_PCB;
109
110 if (3 > out_cap) return T1_ERR_OUT_BUF;
111
112 uint8_t pcb = kPcbRBlock;
113 if (nr) pcb |= kPcbRNr;
114 pcb |= static_cast<uint8_t>(err);
115
116 out[0] = nad;
117 out[1] = pcb;
118 out[2] = 0;
119
120 return append_edc(out, 3, use_crc, out_cap, out_len);
121}
122
124 const uint8_t *inf, uint8_t inf_len,
125 bool use_crc,
126 uint8_t *out, size_t out_cap, size_t *out_len) {
127 if (!out || !out_len) return T1_ERR_NULL;
128 if (inf_len > T1_INF_MAX) return T1_ERR_INF_TOO_BIG;
129 if (inf_len > 0 && !inf) return T1_ERR_NULL;
130 if (static_cast<uint8_t>(subtype) & ~kPcbSSubtypeMask) return T1_ERR_PCB;
131
132 const size_t prologue = 3;
133 if (prologue + inf_len > out_cap) {
134 return T1_ERR_OUT_BUF;
135 }
136
137 out[0] = nad;
138 out[1] = static_cast<uint8_t>(kPcbSBlock | static_cast<uint8_t>(subtype));
139 out[2] = inf_len;
140 if (inf_len > 0) {
141 memcpy(out + prologue, inf, inf_len);
142 }
143
144 return append_edc(out, prologue + inf_len, use_crc, out_cap, out_len);
145}
146
147t1_status_t t1_block_decode(const uint8_t *buf, size_t buf_len, bool use_crc,
148 t1_block_t *out) {
149 if (!buf || !out) return T1_ERR_NULL;
150 if (buf_len < 4) return T1_ERR_SHORT; // NAD + PCB + LEN + at least EDC byte
151 if (use_crc && buf_len < 5) return T1_ERR_SHORT;
152
153 const uint8_t len_byte = buf[2];
154 if (len_byte == T1_LEN_RESERVED) return T1_ERR_LEN;
155
156 const size_t edc_len = use_crc ? 2 : 1;
157 const size_t expected_total = static_cast<size_t>(3) + len_byte + edc_len;
158 if (buf_len < expected_total) return T1_ERR_LEN;
159
160 // Validate EDC over [NAD .. INF].
161 if (use_crc) {
162 uint16_t expected = t1_crc(buf, 3 + len_byte);
163 uint16_t got = static_cast<uint16_t>((buf[3 + len_byte] << 8) | buf[3 + len_byte + 1]);
164 out->edc_ok = (expected == got);
165 } else {
166 uint8_t expected = t1_lrc(buf, 3 + len_byte);
167 out->edc_ok = (expected == buf[3 + len_byte]);
168 }
169 out->use_crc = use_crc;
170
171 out->nad = buf[0];
172 out->pcb = buf[1];
173 out->inf_len = len_byte;
174 out->inf = (len_byte > 0) ? buf + 3 : nullptr;
175
176 const uint8_t pcb = out->pcb;
177 const uint8_t kind_bits = pcb & kPcbBlockKindMask;
178
179 if ((pcb & 0x80) == 0) {
180 // I-block.
181 if (pcb & kPcbIReservedMask) return T1_ERR_PCB;
182 out->kind = T1_BLOCK_I;
183 out->i_ns = (pcb & kPcbINs) ? 1 : 0;
184 out->i_more = (pcb & kPcbIMore) != 0;
185 } else if (kind_bits == kPcbRBlock) {
186 if (pcb & kPcbRReservedMask) return T1_ERR_PCB;
187 if (len_byte != 0) return T1_ERR_LEN;
188 out->kind = T1_BLOCK_R;
189 out->r_nr = (pcb & kPcbRNr) ? 1 : 0;
190 out->r_err = static_cast<t1_r_error_t>(pcb & kPcbRErrMask);
191 } else {
192 out->kind = T1_BLOCK_S;
193 out->s_subtype = static_cast<t1_s_subtype_t>(pcb & kPcbSSubtypeMask);
194 }
195
196 return T1_OK;
197}
Decoded block. INF points into the buffer supplied to t1_block_decode.
Definition t1_block.h:69
uint8_t inf_len
Definition t1_block.h:83
uint8_t pcb
Definition t1_block.h:71
bool use_crc
Definition t1_block.h:86
uint8_t i_ns
Definition t1_block.h:74
const uint8_t * inf
Definition t1_block.h:82
t1_block_kind_t kind
Definition t1_block.h:72
uint8_t r_nr
Definition t1_block.h:77
uint8_t nad
Definition t1_block.h:70
bool i_more
Definition t1_block.h:75
t1_r_error_t r_err
Definition t1_block.h:78
t1_s_subtype_t s_subtype
Definition t1_block.h:80
bool edc_ok
Definition t1_block.h:85
static t1_status_t append_edc(uint8_t *out, size_t prologue_and_inf_len, bool use_crc, size_t out_cap, size_t *out_len)
Definition t1_block.cpp:58
t1_status_t t1_block_encode_r(uint8_t nad, uint8_t nr, t1_r_error_t err, bool use_crc, uint8_t *out, size_t out_cap, size_t *out_len)
Encode an R-block.
Definition t1_block.cpp:103
t1_status_t t1_block_encode_s(uint8_t nad, t1_s_subtype_t subtype, const uint8_t *inf, uint8_t inf_len, bool use_crc, uint8_t *out, size_t out_cap, size_t *out_len)
Encode an S-block.
Definition t1_block.cpp:123
uint16_t t1_crc(const uint8_t *buf, size_t len)
Compute the ISO/IEC 13239 CRC-16 of a buffer (polynomial 0x1021, initial value 0xFFFF,...
Definition t1_block.cpp:41
uint8_t t1_lrc(const uint8_t *buf, size_t len)
Compute the longitudinal redundancy check (LRC) of a buffer.
Definition t1_block.cpp:33
t1_status_t t1_block_encode_i(uint8_t nad, uint8_t ns, bool more, const uint8_t *inf, uint8_t inf_len, bool use_crc, uint8_t *out, size_t out_cap, size_t *out_len)
Encode an I-block.
Definition t1_block.cpp:75
t1_status_t t1_block_decode(const uint8_t *buf, size_t buf_len, bool use_crc, t1_block_t *out)
Decode a T=1 block.
Definition t1_block.cpp:147
#define T1_LEN_RESERVED
Reserved LEN value; must not appear on the wire.
Definition t1_block.h:40
#define T1_INF_MAX
ISO 7816-3 T=1 block-format encoder / decoder.
Definition t1_block.h:34
t1_s_subtype_t
S-block subtype (PCB bits 5-0).
Definition t1_block.h:57
@ T1_BLOCK_S
Definition t1_block.h:46
@ T1_BLOCK_R
Definition t1_block.h:45
@ T1_BLOCK_I
Definition t1_block.h:44
t1_r_error_t
R-block error indicator (PCB bits 1-0).
Definition t1_block.h:50
t1_status_t
Result of t1_block_decode / t1_block_encode.
Definition t1_block.h:90
@ T1_ERR_NULL
Definition t1_block.h:98
@ T1_OK
Definition t1_block.h:91
@ T1_ERR_OUT_BUF
Definition t1_block.h:97
@ T1_ERR_PCB
Definition t1_block.h:96
@ T1_ERR_INF_TOO_BIG
Definition t1_block.h:94
@ T1_ERR_SHORT
Definition t1_block.h:92
@ T1_ERR_LEN
Definition t1_block.h:93