Skip to content

Commit 925a947

Browse files
committed
add ccid device support
support OpenPGP PIV ccid card
1 parent 02a6fd5 commit 925a947

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+6553
-50
lines changed

main/CMakeLists.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
idf_component_register(
2-
SRCS "device.c" "ctaphid.c" "secret.c" "ctap-parser.c" "main.c" "secret.c" "u2f.c" "ctap.c" "common.c" "fs.c"
3-
"crypto/ecc.c" "crypto/hmac.c" "crypto/algo.c" "crypto/sha.c" "crypto/sha3.c" "crypto/memzero.c" "crypto/rand.c" "crypto/sm3.c" "crypto/block-cipher.c" "crypto/aes.c" "crypto/esp32_ed25519.c"
2+
SRCS "device.c" "main.c" "common.c" "fs.c" "apdu.c" "applets.c" "fs.c" "key.c" "pin.c"
3+
"usb/ccid/ccid_device.c" "usb/ccid/ccid.c" "usb/ctaphid/ctaphid.c"
4+
"applets/admin/admin.c" "applets/ctap/ctap.c" "applets/ctap/ctap-parser.c" "applets/ctap/secret.c" "applets/ctap/u2f.c" "applets/meta/meta.c" "applets/oath/oath.c" "applets/ndef/ndef.c" "applets/openpgp/key.c" "applets/openpgp/openpgp.c" "applets/piv/piv.c"
5+
"crypto/ecc.c" "crypto/hmac.c" "crypto/algo.c" "crypto/sha.c" "crypto/sha3.c" "crypto/memzero.c" "crypto/rand.c" "crypto/sm3.c" "crypto/block-cipher.c" "crypto/aes.c" "crypto/rsa.c" "crypto/des.c" "crypto/esp32_ed25519.c"
46
"littlefs/lfs.c" "littlefs/lfs_util.c"
5-
INCLUDE_DIRS "." "crypto/include" "littlefs"
7+
INCLUDE_DIRS "include" "crypto/include" "littlefs"
68
REQUIRES driver mbedtls efuse esp_partition esp_timer
79
EMBED_FILES "cert/u2f_cert.bin" "cert/u2f_cert_key.bin" "cert/u2f_aaguid.bin"
810
)

main/apdu.c

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
#include <admin.h>
3+
#include <apdu.h>
4+
#include <applets.h>
5+
#include <common.h>
6+
#include <ctap.h>
7+
#include <device.h>
8+
#include <meta.h>
9+
#include <ndef.h>
10+
#include <oath.h>
11+
#include <openpgp.h>
12+
#include <piv.h>
13+
14+
enum APPLET {
15+
APPLET_NULL,
16+
APPLET_PIV,
17+
APPLET_FIDO,
18+
APPLET_OATH,
19+
APPLET_ADMIN,
20+
APPLET_OPENPGP,
21+
APPLET_NDEF,
22+
APPLET_META,
23+
APPLET_ENUM_END,
24+
} current_applet;
25+
26+
enum PIV_STATE {
27+
PIV_STATE_GET_DATA,
28+
PIV_STATE_GET_DATA_RESPONSE,
29+
PIV_STATE_OTHER,
30+
};
31+
32+
static const uint8_t PIV_AID[] = {0xA0, 0x00, 0x00, 0x03, 0x08};
33+
static const uint8_t OATH_AID[] = {0xA0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01};
34+
static const uint8_t ADMIN_AID[] = {0xF0, 0x00, 0x00, 0x00, 0x00};
35+
static const uint8_t OPENPGP_AID[] = {0xD2, 0x76, 0x00, 0x01, 0x24, 0x01};
36+
static const uint8_t FIDO_AID[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
37+
static const uint8_t NDEF_AID[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01};
38+
static const uint8_t META_AID[] = {0xA0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17};
39+
40+
static const uint8_t *const AID[] = {
41+
[APPLET_NULL] = NULL, [APPLET_PIV] = PIV_AID, [APPLET_FIDO] = FIDO_AID, [APPLET_OATH] = OATH_AID,
42+
[APPLET_ADMIN] = ADMIN_AID, [APPLET_OPENPGP] = OPENPGP_AID, [APPLET_NDEF] = NDEF_AID, [APPLET_META] = META_AID,
43+
};
44+
45+
static const uint8_t AID_Size[] = {
46+
[APPLET_NULL] = 0,
47+
[APPLET_PIV] = sizeof(PIV_AID),
48+
[APPLET_FIDO] = sizeof(FIDO_AID),
49+
[APPLET_OATH] = sizeof(OATH_AID),
50+
[APPLET_ADMIN] = sizeof(ADMIN_AID),
51+
[APPLET_OPENPGP] = sizeof(OPENPGP_AID),
52+
[APPLET_NDEF] = sizeof(NDEF_AID),
53+
[APPLET_META] = sizeof(META_AID),
54+
};
55+
56+
static volatile uint32_t buffer_owner = BUFFER_OWNER_NONE;
57+
static uint8_t chaining_buffer[APDU_BUFFER_SIZE];
58+
static CAPDU_CHAINING capdu_chaining = {
59+
.capdu.data = chaining_buffer,
60+
};
61+
static RAPDU_CHAINING rapdu_chaining = {
62+
.rapdu.data = chaining_buffer,
63+
};
64+
65+
int build_capdu(CAPDU *capdu, const uint8_t *cmd, uint16_t len) {
66+
if (len < 4) return -1;
67+
CLA = cmd[0];
68+
INS = cmd[1];
69+
P1 = cmd[2];
70+
P2 = cmd[3];
71+
LC = 0;
72+
LE = 0;
73+
74+
if (len == 4) // Case 1
75+
return 0;
76+
LC = cmd[4];
77+
if (len == 5) { // Case 2S
78+
LE = LC;
79+
LC = 0;
80+
if (LE == 0) LE = 0x100;
81+
} else if (LC > 0 && len == 5 + LC) { // Case 3S
82+
memmove(DATA, cmd + 5, LC);
83+
LE = 0x100;
84+
} else if (LC > 0 && len == 6 + LC) { // Case 4S
85+
memmove(DATA, cmd + 5, LC);
86+
LE = cmd[5 + LC];
87+
if (LE == 0) LE = 0x100;
88+
} else if (len == 7) { // Case 2E
89+
if (LC != 0) return -1;
90+
LE = (cmd[5] << 8) | cmd[6];
91+
if (LE == 0) LE = 0x10000;
92+
} else {
93+
if (LC != 0 || len < 7) return -1;
94+
LC = (cmd[5] << 8) | cmd[6];
95+
if (LC == 0) return -1;
96+
if (len == 7 + LC) { // Case 3E
97+
memmove(DATA, cmd + 7, LC);
98+
LE = 0x10000;
99+
return 0;
100+
} else if (len == 9 + LC) { // Case 4E
101+
memmove(DATA, cmd + 7, LC);
102+
LE = (cmd[7 + LC] << 8) | cmd[8 + LC];
103+
if (LE == 0) LE = 0x10000;
104+
} else
105+
return -1;
106+
}
107+
return 0;
108+
}
109+
110+
int apdu_input(CAPDU_CHAINING *ex, const CAPDU *sh) {
111+
restart:
112+
if (!ex->in_chaining) {
113+
ex->capdu.cla = sh->cla & 0xEF;
114+
ex->capdu.ins = sh->ins;
115+
ex->capdu.p1 = sh->p1;
116+
ex->capdu.p2 = sh->p2;
117+
ex->capdu.lc = 0;
118+
} else if (ex->capdu.cla != (sh->cla & 0xEF) || ex->capdu.ins != sh->ins || ex->capdu.p1 != sh->p1 ||
119+
ex->capdu.p2 != sh->p2) {
120+
ex->in_chaining = 0;
121+
goto restart;
122+
}
123+
ex->in_chaining = 1;
124+
if (ex->capdu.lc + sh->lc > APDU_BUFFER_SIZE) return APDU_CHAINING_OVERFLOW;
125+
memcpy(ex->capdu.data + ex->capdu.lc, sh->data, sh->lc);
126+
ex->capdu.lc += sh->lc;
127+
128+
if (sh->cla & 0x10) // not last block
129+
return APDU_CHAINING_NOT_LAST_BLOCK;
130+
else {
131+
ex->in_chaining = 0;
132+
ex->capdu.le = sh->le;
133+
return APDU_CHAINING_LAST_BLOCK;
134+
}
135+
}
136+
137+
int apdu_output(RAPDU_CHAINING *ex, RAPDU *sh) {
138+
uint16_t to_send = ex->rapdu.len - ex->sent;
139+
if (to_send > sh->len) to_send = sh->len;
140+
memcpy(sh->data, ex->rapdu.data + ex->sent, to_send);
141+
sh->len = to_send;
142+
ex->sent += to_send;
143+
if (ex->sent < ex->rapdu.len) {
144+
if (ex->rapdu.len - ex->sent > 0xFF)
145+
sh->sw = 0x61FF;
146+
else
147+
sh->sw = 0x6100 + (ex->rapdu.len - ex->sent);
148+
} else
149+
sh->sw = ex->rapdu.sw;
150+
return 0;
151+
}
152+
153+
void process_apdu(CAPDU *capdu, RAPDU *rapdu) {
154+
static enum PIV_STATE piv_state;
155+
if (current_applet == APPLET_PIV) {
156+
// Offload some APDU chaining commands of PIV applet,
157+
// because the length of concatenated payloads may exceed chaining buffer size.
158+
if (INS == PIV_INS_GET_DATA)
159+
piv_state = PIV_STATE_GET_DATA;
160+
else if ((piv_state == PIV_STATE_GET_DATA || piv_state == PIV_STATE_GET_DATA_RESPONSE) && INS == 0xC0)
161+
piv_state = PIV_STATE_GET_DATA_RESPONSE;
162+
else
163+
piv_state = PIV_STATE_OTHER;
164+
if (piv_state == PIV_STATE_GET_DATA || piv_state == PIV_STATE_GET_DATA_RESPONSE || INS == PIV_INS_PUT_DATA) {
165+
LE = MIN(LE, APDU_BUFFER_SIZE); // Always clamp the Le to valid range
166+
piv_process_apdu(capdu, rapdu);
167+
return;
168+
}
169+
}
170+
int ret = apdu_input(&capdu_chaining, capdu);
171+
if (ret == APDU_CHAINING_NOT_LAST_BLOCK) {
172+
LL = 0;
173+
SW = SW_NO_ERROR;
174+
} else if (ret == APDU_CHAINING_LAST_BLOCK) {
175+
capdu = &capdu_chaining.capdu;
176+
LE = MIN(LE, APDU_BUFFER_SIZE);
177+
if ((CLA == 0x80 || CLA == 0x00) && INS == 0xC0) { // GET RESPONSE
178+
rapdu->len = LE;
179+
apdu_output(&rapdu_chaining, rapdu);
180+
return;
181+
}
182+
rapdu_chaining.sent = 0;
183+
if (CLA == 0x00 && INS == 0xA4 && P1 == 0x04 && P2 == 0x00) {
184+
uint8_t i, end = APPLET_ENUM_END;
185+
for (i = APPLET_NULL + 1; i != end; ++i) {
186+
if (LC >= AID_Size[i] && memcmp(DATA, AID[i], AID_Size[i]) == 0) {
187+
if (i == APPLET_NDEF && !cfg_is_ndef_enable()) {
188+
LL = 0;
189+
SW = SW_FILE_NOT_FOUND;
190+
DBG_MSG("NDEF is disable\n");
191+
return;
192+
}
193+
if (i == APPLET_PIV) piv_state = PIV_STATE_OTHER; // Reset `piv_state`
194+
if (i != current_applet) applets_poweroff();
195+
current_applet = i;
196+
DBG_MSG("applet switched to: %d\n", current_applet);
197+
break;
198+
}
199+
}
200+
if (i == end) {
201+
LL = 0;
202+
SW = SW_FILE_NOT_FOUND;
203+
DBG_MSG("applet not found\n");
204+
return;
205+
}
206+
}
207+
switch (current_applet) {
208+
case APPLET_OPENPGP:
209+
openpgp_process_apdu(capdu, &rapdu_chaining.rapdu);
210+
rapdu->len = LE;
211+
apdu_output(&rapdu_chaining, rapdu);
212+
break;
213+
case APPLET_PIV:
214+
piv_process_apdu(capdu, &rapdu_chaining.rapdu);
215+
rapdu->len = LE;
216+
apdu_output(&rapdu_chaining, rapdu);
217+
break;
218+
case APPLET_FIDO:
219+
#ifdef TEST
220+
if (CLA == 0x00 && INS == 0xEE && LC == 0x04 && memcmp(DATA, "\x12\x56\xAB\xF0", 4) == 0) {
221+
printf("MAGIC REBOOT command received!\r\n");
222+
testmode_set_initial_ticks(0);
223+
testmode_set_initial_ticks(device_get_tick());
224+
ctap_install(0);
225+
SW = 0x9000;
226+
LL = 0;
227+
break;
228+
}
229+
if (CLA == 0x00 && INS == 0xEF) {
230+
testmode_inject_error(P1, P2, LC, DATA);
231+
SW = 0x9000;
232+
LL = 0;
233+
break;
234+
}
235+
#endif
236+
ctap_process_apdu(capdu, &rapdu_chaining.rapdu);
237+
rapdu->len = LE;
238+
apdu_output(&rapdu_chaining, rapdu);
239+
break;
240+
case APPLET_OATH:
241+
oath_process_apdu(capdu, rapdu);
242+
break;
243+
case APPLET_ADMIN:
244+
admin_process_apdu(capdu, rapdu);
245+
break;
246+
case APPLET_NDEF:
247+
ndef_process_apdu(capdu, rapdu);
248+
break;
249+
case APPLET_META:
250+
meta_process_apdu(capdu, rapdu);
251+
break;
252+
default:
253+
LL = 0;
254+
SW = SW_FILE_NOT_FOUND;
255+
}
256+
} else {
257+
LL = 0;
258+
SW = SW_CHECKING_ERROR;
259+
}
260+
}
261+
262+
int acquire_apdu_buffer(uint8_t owner) {
263+
device_atomic_compare_and_swap(&buffer_owner, BUFFER_OWNER_NONE, owner);
264+
return buffer_owner == owner ? 0 : -1;
265+
}
266+
267+
int release_apdu_buffer(uint8_t owner) {
268+
device_atomic_compare_and_swap(&buffer_owner, owner, BUFFER_OWNER_NONE);
269+
return buffer_owner == BUFFER_OWNER_NONE ? 0 : -1;
270+
}

main/applets.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
#include <admin.h>
3+
#include <applets.h>
4+
#include <ctap.h>
5+
#include <ndef.h>
6+
#include <oath.h>
7+
#include <openpgp.h>
8+
#include <piv.h>
9+
10+
void applets_install(void) {
11+
openpgp_install(0);
12+
piv_install(0);
13+
oath_install(0);
14+
ctap_install(0);
15+
admin_install(0);
16+
ndef_install(0);
17+
}
18+
19+
void applets_poweroff(void) {
20+
piv_poweroff();
21+
oath_poweroff();
22+
admin_poweroff();
23+
openpgp_poweroff();
24+
ndef_poweroff();
25+
}

0 commit comments

Comments
 (0)