CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
t1_state.cpp
Go to the documentation of this file.
1
4
6
7#include <string.h>
8
9namespace {
10
11constexpr uint8_t kCardNad = 0x00;
12
13uint8_t flip(uint8_t bit) {
14 return bit ? 0 : 1;
15}
16
17} // namespace
18
19static size_t encode_rblock(t1_state_t *state, uint8_t nr, t1_r_error_t err) {
20 size_t len = 0;
21 t1_block_encode_r(kCardNad, nr, err, state->use_crc,
22 state->control_block, sizeof(state->control_block), &len);
23 state->control_len = len;
24 return len;
25}
26
27static size_t encode_sblock_response(t1_state_t *state, t1_s_subtype_t subtype,
28 const uint8_t *inf, uint8_t inf_len) {
29 size_t len = 0;
30 t1_block_encode_s(kCardNad, subtype, inf, inf_len, state->use_crc,
31 state->control_block, sizeof(state->control_block), &len);
32 state->control_len = len;
33 return len;
34}
35
36void t1_state_init(t1_state_t *state, bool use_crc) {
37 if (!state) return;
38 memset(state, 0, sizeof(*state));
39 state->ns_send = 0;
40 state->ns_expected = 0;
41 state->ifsc = T1_DEFAULT_IFSC;
42 state->ifsd = T1_DEFAULT_IFSC;
43 state->use_crc = use_crc;
44}
45
46static t1_feed_t handle_iblock(t1_state_t *state, const t1_block_t *blk) {
47 if (!blk->edc_ok) {
49 return T1_FEED_NEED_OUT;
50 }
51 if (blk->i_ns != state->ns_expected) {
52 // Out-of-sequence: ask for the expected N(S) via R-block.
54 return T1_FEED_NEED_OUT;
55 }
56 // Sequence OK: append payload.
57 if (!state->apdu_in_progress) {
58 state->apdu_len = 0;
59 state->apdu_in_progress = true;
60 }
61 if (state->apdu_len + blk->inf_len > sizeof(state->apdu_buf)) {
62 // APDU too large; abort.
63 encode_sblock_response(state, T1_S_ABORT_RESP, nullptr, 0);
64 state->apdu_in_progress = false;
65 state->apdu_len = 0;
66 return T1_FEED_NEED_OUT;
67 }
68 if (blk->inf_len > 0) {
69 memcpy(state->apdu_buf + state->apdu_len, blk->inf, blk->inf_len);
70 state->apdu_len += blk->inf_len;
71 }
72
73 state->ns_expected = flip(state->ns_expected);
74
75 if (blk->i_more) {
76 // Reader has more data; ACK with R-block carrying our updated N(R).
77 encode_rblock(state, state->ns_expected, T1_R_OK);
78 return T1_FEED_WAIT_MORE;
79 }
80 state->apdu_in_progress = false;
81 return T1_FEED_APDU_READY;
82}
83
84static t1_feed_t handle_rblock(t1_state_t *state, const t1_block_t *blk) {
85 // Reader is asking for a specific N(S). Common during response-chunk
86 // transmission. Simplest correct behaviour: re-arm ns_send to whatever
87 // the reader expects and let the next outbound chunk go out.
88 if (!blk->edc_ok) {
90 return T1_FEED_NEED_OUT;
91 }
92 if (state->resp_pending) {
93 state->ns_send = blk->r_nr;
94 }
95 return T1_FEED_IGNORED;
96}
97
98static t1_feed_t handle_sblock(t1_state_t *state, const t1_block_t *blk) {
99 if (!blk->edc_ok) {
100 encode_rblock(state, state->ns_expected, T1_R_EDC_ERR);
101 return T1_FEED_NEED_OUT;
102 }
103 switch (blk->s_subtype) {
104 case T1_S_IFS_REQ:
105 if (blk->inf_len == 1 && blk->inf != nullptr) {
106 state->ifsd = blk->inf[0];
108 return T1_FEED_NEED_OUT;
109 }
111 return T1_FEED_NEED_OUT;
112 case T1_S_ABORT_REQ:
113 // Drop any pending chained APDU / response.
114 state->apdu_in_progress = false;
115 state->apdu_len = 0;
116 state->resp_pending = false;
117 state->resp_len = 0;
118 state->resp_sent = 0;
119 encode_sblock_response(state, T1_S_ABORT_RESP, nullptr, 0);
120 return T1_FEED_NEED_OUT;
121 case T1_S_WTX_REQ:
122 // Reader is granting us more time; reply with same INF.
124 return T1_FEED_NEED_OUT;
125 case T1_S_RESYNCH_REQ:
126 // Reset sequence numbers and pending traffic.
127 state->ns_send = 0;
128 state->ns_expected = 0;
129 state->apdu_in_progress = false;
130 state->apdu_len = 0;
131 state->resp_pending = false;
132 state->resp_len = 0;
133 state->resp_sent = 0;
134 encode_sblock_response(state, T1_S_RESYNCH_RESP, nullptr, 0);
135 return T1_FEED_NEED_OUT;
136 default:
137 // Unknown subtype — ignore per ISO 7816-3 robustness guidance.
138 return T1_FEED_IGNORED;
139 }
140}
141
143 if (!state || !blk) return T1_FEED_IGNORED;
144 switch (blk->kind) {
145 case T1_BLOCK_I:
146 return handle_iblock(state, blk);
147 case T1_BLOCK_R:
148 return handle_rblock(state, blk);
149 case T1_BLOCK_S:
150 return handle_sblock(state, blk);
151 }
152 return T1_FEED_IGNORED;
153}
154
155bool t1_state_queue_response(t1_state_t *state, const uint8_t *resp, size_t resp_len) {
156 if (!state || (resp_len > 0 && !resp)) return false;
157 if (resp_len > sizeof(state->resp_buf)) return false;
158 if (resp_len > 0) {
159 memcpy(state->resp_buf, resp, resp_len);
160 }
161 state->resp_len = resp_len;
162 state->resp_sent = 0;
163 state->resp_pending = (resp_len > 0);
164 return true;
165}
166
167size_t t1_state_next_outbound(t1_state_t *state, uint8_t *out, size_t out_cap) {
168 if (!state || !out) return 0;
169
170 // Control blocks (R / S responses) always go first.
171 if (state->control_len > 0) {
172 const size_t n = state->control_len;
173 if (n > out_cap) return 0;
174 memcpy(out, state->control_block, n);
175 state->control_len = 0;
176 return n;
177 }
178
179 if (!state->resp_pending) {
180 return 0;
181 }
182
183 const size_t remaining = state->resp_len - state->resp_sent;
184 if (remaining == 0) {
185 state->resp_pending = false;
186 return 0;
187 }
188
189 const uint16_t chunk_cap = state->ifsd ? state->ifsd : T1_DEFAULT_IFSC;
190 const size_t chunk_len = (remaining > chunk_cap) ? chunk_cap : remaining;
191 const bool more = (chunk_len < remaining);
192
193 size_t encoded = 0;
195 kCardNad, state->ns_send, more,
196 state->resp_buf + state->resp_sent, static_cast<uint8_t>(chunk_len),
197 state->use_crc, out, out_cap, &encoded);
198 if (status != T1_OK) {
199 return 0;
200 }
201
202 state->resp_sent += chunk_len;
203 state->ns_send = flip(state->ns_send);
204 if (!more) {
205 state->resp_pending = false;
206 }
207 return encoded;
208}
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 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
bool i_more
Definition t1_block.h:75
t1_s_subtype_t s_subtype
Definition t1_block.h:80
bool edc_ok
Definition t1_block.h:85
Card-side T=1 state. Embedded entirely in caller-provided storage.
Definition t1_state.h:44
size_t control_len
Definition t1_state.h:69
size_t apdu_len
Definition t1_state.h:58
uint16_t ifsc
Definition t1_state.h:50
bool apdu_in_progress
Definition t1_state.h:59
uint8_t apdu_buf[4096]
Definition t1_state.h:57
size_t resp_sent
Definition t1_state.h:64
uint8_t resp_buf[4096]
Definition t1_state.h:62
uint8_t control_block[254+5]
Definition t1_state.h:68
uint8_t ns_send
Definition t1_state.h:46
bool use_crc
Definition t1_state.h:54
size_t resp_len
Definition t1_state.h:63
uint8_t ns_expected
Definition t1_state.h:47
uint16_t ifsd
Definition t1_state.h:51
bool resp_pending
Definition t1_state.h:65
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_s_subtype_t
S-block subtype (PCB bits 5-0).
Definition t1_block.h:57
@ T1_S_IFS_REQ
Definition t1_block.h:59
@ T1_S_RESYNCH_REQ
Definition t1_block.h:58
@ T1_S_ABORT_RESP
Definition t1_block.h:64
@ T1_S_WTX_RESP
Definition t1_block.h:65
@ T1_S_RESYNCH_RESP
Definition t1_block.h:62
@ T1_S_ABORT_REQ
Definition t1_block.h:60
@ T1_S_IFS_RESP
Definition t1_block.h:63
@ T1_S_WTX_REQ
Definition t1_block.h:61
@ 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_R_OK
Definition t1_block.h:51
@ T1_R_EDC_ERR
Definition t1_block.h:52
@ T1_R_OTHER_ERR
Definition t1_block.h:53
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
t1_status_t
Result of t1_block_decode / t1_block_encode.
Definition t1_block.h:90
@ T1_OK
Definition t1_block.h:91
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
#define T1_DEFAULT_IFSC
Default IFSC at protocol start (ISO 7816-3, before negotiation).
Definition t1_block.h:37
static t1_feed_t handle_sblock(t1_state_t *state, const t1_block_t *blk)
Definition t1_state.cpp:98
static size_t encode_sblock_response(t1_state_t *state, t1_s_subtype_t subtype, const uint8_t *inf, uint8_t inf_len)
Definition t1_state.cpp:27
size_t t1_state_next_outbound(t1_state_t *state, uint8_t *out, size_t out_cap)
Emit the next outbound block (control reply or response chunk).
Definition t1_state.cpp:167
static t1_feed_t handle_iblock(t1_state_t *state, const t1_block_t *blk)
Definition t1_state.cpp:46
bool t1_state_queue_response(t1_state_t *state, const uint8_t *resp, size_t resp_len)
Register the application's APDU response so the state machine can chunk it into I-blocks.
Definition t1_state.cpp:155
static size_t encode_rblock(t1_state_t *state, uint8_t nr, t1_r_error_t err)
Definition t1_state.cpp:19
t1_feed_t t1_state_feed(t1_state_t *state, const t1_block_t *blk)
Feed an incoming decoded block to the state machine.
Definition t1_state.cpp:142
void t1_state_init(t1_state_t *state, bool use_crc)
Initialise the state machine. Sets sequence numbers to 0, IFSC/IFSD to defaults.
Definition t1_state.cpp:36
static t1_feed_t handle_rblock(t1_state_t *state, const t1_block_t *blk)
Definition t1_state.cpp:84
t1_feed_t
Outcome of feeding a block to the state machine.
Definition t1_state.h:36
@ T1_FEED_WAIT_MORE
Definition t1_state.h:39
@ T1_FEED_NEED_OUT
Definition t1_state.h:37
@ T1_FEED_IGNORED
Definition t1_state.h:40
@ T1_FEED_APDU_READY
Definition t1_state.h:38