• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

canokeys / canokey-core / 27757492817

18 Jun 2026 11:51AM UTC coverage: 90.675% (+0.3%) from 90.336%
27757492817

push

github

web-flow
Merge pull request #154 from canokeys/codex/dynamic-oath-ctap-capacity

- Add LittleFS free-space estimation helpers and use them for admission control.
- Update OATH storage to reuse deleted record slots and to rely on free space (not a fixed 100-record cap).
- Replace CTAP discoverable credential “slot table” logic with flash-scanning metadata/cursors and space-based capacity logic, updating tests accordingly.

450 of 457 new or added lines in 8 files covered. (98.47%)

1 existing line in 1 file now uncovered.

11036 of 12171 relevant lines covered (90.67%)

263629.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.83
/applets/admin/admin.c
1
// SPDX-License-Identifier: Apache-2.0
2
#include <admin.h>
3
#include <crypto-util.h>
4
#include <ctap.h>
5
#include <device-config.h>
6
#include <device.h>
7
#include <fs.h>
8
#if ENABLE_APPLET_NDEF
9
#include <ndef.h>
10
#endif
11
#include <oath.h>
12
#include <openpgp.h>
13
#include <pass.h>
14
#include <pin.h>
15
#include <piv.h>
16

17
#define PIN_RETRY_COUNTER 3
18

19
#define ADMIN_PIN_FILE "admin-pin"
20

21
typedef struct {
22
  const char *path;
23
  const uint8_t *attrs;
24
  uint8_t attr_count;
25
} admin_fs_usage_source_t;
26

27
typedef struct {
28
  uint8_t id;
29
  const admin_fs_usage_source_t *sources;
30
  uint8_t source_count;
31
} admin_applet_usage_def_t;
32

33
static pin_t pin = {.min_length = 6, .max_length = PIN_MAX_LENGTH, .is_validated = 0, .path = ADMIN_PIN_FILE};
34

35
// Logical usage manifests for ADMIN_FLASH_USAGE_APPLETS. Keep these in sync
36
// with applet-owned LittleFS paths and user attributes. The SYSTEM record
37
// reports LittleFS physical usage that cannot be attributed to these payloads.
38
static const uint8_t admin_pin_attrs[] = {0x00, 0x01};
39
static const admin_fs_usage_source_t admin_usage_sources[] = {
40
    {ADMIN_PIN_FILE, admin_pin_attrs, sizeof(admin_pin_attrs)},
41
};
42

43
static const uint8_t openpgp_data_attrs[] = {0x5E, 0x5B, 0x2D, 0x35, 0xC4, 0x93, 0xD6, 0xD7, 0xD8, 0xC1, 0xC2,
44
                                             0xC3, 0xFF, 0xFE, 0xFD, 0xFC, 0xFB};
45
static const uint8_t openpgp_key_attrs[] = {0x00, 0x01};
46
static const uint8_t openpgp_pin_attrs[] = {0x00, 0x01};
47
static const admin_fs_usage_source_t openpgp_usage_sources[] = {
48
    {"pgp-data", openpgp_data_attrs, sizeof(openpgp_data_attrs)},
49
    {"pgp-sigk", openpgp_key_attrs, sizeof(openpgp_key_attrs)},
50
    {"pgp-deck", openpgp_key_attrs, sizeof(openpgp_key_attrs)},
51
    {"pgp-autk", openpgp_key_attrs, sizeof(openpgp_key_attrs)},
52
    {"pgp-sigc", NULL, 0},
53
    {"pgp-decc", NULL, 0},
54
    {"pgp-autc", NULL, 0},
55
    {"pgp-pw1", openpgp_pin_attrs, sizeof(openpgp_pin_attrs)},
56
    {"pgp-pw3", openpgp_pin_attrs, sizeof(openpgp_pin_attrs)},
57
    {"pgp-rc", openpgp_pin_attrs, sizeof(openpgp_pin_attrs)},
58
};
59

60
static const uint8_t piv_pin_attrs[] = {0x00, 0x01, 0x81};
61
static const uint8_t piv_admin_key_attrs[] = {0x81};
62
static const uint8_t piv_do_meta_attrs[] = {0x90, 0x91, 0x92, 0x93};
63
static const admin_fs_usage_source_t piv_usage_sources[] = {
64
    {"piv-pauc", NULL, 0}, {"piv-sigc", NULL, 0}, {"piv-cauc", NULL, 0}, {"piv-mntc", NULL, 0},
65
    {"piv-82c", NULL, 0},  {"piv-83c", NULL, 0},  {"piv-chu", NULL, 0},  {"piv-ccc", NULL, 0},
66
    {"piv-pi", NULL, 0},   {"piv-fig", NULL, 0},   {"piv-face", NULL, 0}, {"piv-sec", NULL, 0},
67
    {"piv-kh", NULL, 0},   {"piv-iris", NULL, 0},  {"piv-do", piv_do_meta_attrs, sizeof(piv_do_meta_attrs)},
68
    {"piv-pauk", NULL, 0}, {"piv-sigk", NULL, 0},  {"piv-cauk", NULL, 0}, {"piv-mntk", NULL, 0},
69
    {"piv-82", NULL, 0},   {"piv-83", NULL, 0},    {"piv-admk", piv_admin_key_attrs, sizeof(piv_admin_key_attrs)},
70
    {"piv-pin", piv_pin_attrs, sizeof(piv_pin_attrs)}, {"piv-puk", piv_pin_attrs, sizeof(piv_pin_attrs)},
71
};
72

73
static const uint8_t oath_attrs[] = {0x02, 0x03};
74
static const admin_fs_usage_source_t oath_usage_sources[] = {
75
    {"oath", oath_attrs, sizeof(oath_attrs)},
76
};
77

78
static const uint8_t ctap_cert_attrs[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
79
static const uint8_t ctap_dc_attrs[] = {0x00};
80
static const admin_fs_usage_source_t ctap_usage_sources[] = {
81
    {"ctap_cert", ctap_cert_attrs, sizeof(ctap_cert_attrs)},
82
    {"ctap_dc", ctap_dc_attrs, sizeof(ctap_dc_attrs)},
83
    {"ctap_dm", NULL, 0},
84
    {"ctap_lb", NULL, 0},
85
    {"ctap_lbt", NULL, 0},
86
    {"ctap_mpr", NULL, 0},
87
    {"ctap_np", NULL, 0},
88
};
89

90
static const admin_fs_usage_source_t ndef_usage_sources[] = {
91
    {"E103", NULL, 0},
92
    {"NDEF", NULL, 0},
93
};
94

95
static const admin_fs_usage_source_t pass_usage_sources[] = {
96
    {"pass", NULL, 0},
97
};
98

99
static const admin_applet_usage_def_t applet_usage_defs[] = {
100
    {ADMIN_APPLET_USAGE_ID_ADMIN, admin_usage_sources, sizeof(admin_usage_sources) / sizeof(admin_usage_sources[0])},
101
    {ADMIN_APPLET_USAGE_ID_OPENPGP, openpgp_usage_sources,
102
     sizeof(openpgp_usage_sources) / sizeof(openpgp_usage_sources[0])},
103
    {ADMIN_APPLET_USAGE_ID_PIV, piv_usage_sources, sizeof(piv_usage_sources) / sizeof(piv_usage_sources[0])},
104
    {ADMIN_APPLET_USAGE_ID_OATH, oath_usage_sources, sizeof(oath_usage_sources) / sizeof(oath_usage_sources[0])},
105
    {ADMIN_APPLET_USAGE_ID_CTAP, ctap_usage_sources, sizeof(ctap_usage_sources) / sizeof(ctap_usage_sources[0])},
106
    {ADMIN_APPLET_USAGE_ID_NDEF, ndef_usage_sources, sizeof(ndef_usage_sources) / sizeof(ndef_usage_sources[0])},
107
    {ADMIN_APPLET_USAGE_ID_PASS, pass_usage_sources, sizeof(pass_usage_sources) / sizeof(pass_usage_sources[0])},
108
};
109

110
static const admin_device_config_t default_cfg = {.led_normally_on = 1, .ndef_en = 1, .webusb_landing_en = 1};
111

112
static admin_device_config_t current_config;
113
static admin_device_config_t admin_get_current_config(void);
114

115
__attribute__((weak)) int admin_vendor_specific(const CAPDU *capdu, RAPDU *rapdu) {
116
  UNUSED(capdu);
117
  UNUSED(rapdu);
118
  return 0;
1✔
119
}
120

121
__attribute__((weak)) int admin_vendor_version(const CAPDU *capdu, RAPDU *rapdu) {
122
  UNUSED(capdu);
123
  UNUSED(rapdu);
124
  return 0;
×
125
}
126

127
__attribute__((weak)) int admin_vendor_hw_variant(const CAPDU *capdu, RAPDU *rapdu) {
128
  UNUSED(capdu);
129
  UNUSED(rapdu);
130
  return 0;
×
131
}
132

133
__attribute__((weak)) int admin_vendor_hw_sn(const CAPDU *capdu, RAPDU *rapdu) {
134
  UNUSED(capdu);
135
  UNUSED(rapdu);
136
  return 0;
×
137
}
138

139
__attribute__((weak)) int admin_vendor_nfc_enable(const CAPDU *capdu, RAPDU *rapdu, bool pin_validated) {
140
  UNUSED(capdu);
141
  UNUSED(rapdu);
142
  UNUSED(pin_validated);
143
  return 0;
×
144
}
145

146
// Query the platform hook on every read so a platform-backed config is not
147
// shadowed by core RAM after a vendor/admin APDU updates flash directly.
148
uint8_t device_config_is_led_normally_on(void) { return admin_get_current_config().led_normally_on; }
3,125✔
149

150
uint8_t device_config_is_ndef_enabled(void) { return admin_get_current_config().ndef_en; }
21✔
151

152
uint8_t device_config_is_webusb_landing_enabled(void) { return admin_get_current_config().webusb_landing_en; }
1✔
153

154
static admin_device_config_t admin_get_current_config(void) {
3,187✔
155
  admin_device_config_t cfg = default_cfg;
3,187✔
156
  if (admin_platform_device_config_read(&cfg) == 0) return cfg;
3,187✔
157
  return default_cfg;
12✔
158
}
159

160
void admin_poweroff(void) { pin.is_validated = 0; }
708✔
161

162
int admin_install(const uint8_t reset) {
163
  admin_poweroff();
343✔
164
  // Device config is platform-backed. Core keeps no LittleFS fallback, which
165
  // avoids two independent sources of truth for LED/NDEF/WebUSB flags.
166
  if (reset || admin_platform_device_config_read(&current_config) < 0) {
343✔
167
    current_config = default_cfg;
5✔
168
    if (admin_platform_device_config_write(&current_config) < 0) return -1;
5✔
169
  }
170
  if (reset || get_file_size(pin.path) < 0) {
343✔
171
    if (pin_create(&pin, "123456", 6, PIN_RETRY_COUNTER) < 0) return -1;
8✔
172
  }
173
  return 0;
343✔
174
}
175

176
static int admin_verify(const CAPDU *capdu, RAPDU *rapdu) {
112✔
177
  if (P1 != 0x00 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
112✔
178
  if (LC == 0) {
112✔
179
    if (pin.is_validated) return 0;
28✔
180
    const int retries = pin_get_retries(&pin);
28✔
181
    if (retries < 0) return -1;
28✔
182
    EXCEPT(pin_get_retry_sw((uint8_t)retries));
28✔
183
  }
184
  uint8_t ctr;
84✔
185
  const int err = pin_verify(&pin, DATA, LC, &ctr);
84✔
186
  if (err == PIN_IO_FAIL) return -1;
84✔
187
  if (err == PIN_LENGTH_INVALID) EXCEPT(SW_WRONG_LENGTH);
84✔
188
  if (ctr == 0) EXCEPT(SW_AUTHENTICATION_BLOCKED);
57✔
189
  if (err == PIN_AUTH_FAIL) EXCEPT(pin_get_retry_sw(ctr));
53✔
190
  return 0;
44✔
191
}
192

193
static int admin_change_pin(const CAPDU *capdu, RAPDU *rapdu) {
7✔
194
  if (P1 != 0x00 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
7✔
195
  const int err = pin_update(&pin, DATA, LC);
7✔
196
  if (err == PIN_IO_FAIL) return -1;
7✔
197
  if (err == PIN_LENGTH_INVALID) EXCEPT(SW_WRONG_LENGTH);
7✔
198
  return 0;
3✔
199
}
200

201
static int admin_write_sn(const CAPDU *capdu, RAPDU *rapdu) {
3✔
202
  if (P1 != 0x00 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
3✔
203
  if (LC != 0x04) EXCEPT(SW_WRONG_LENGTH);
3✔
204
  if (admin_platform_serial_write_once(DATA) < 0) EXCEPT(SW_CONDITIONS_NOT_SATISFIED);
3✔
205
  return 0;
2✔
206
}
207

208
static int admin_read_sn(const CAPDU *capdu, RAPDU *rapdu) {
4✔
209
  if (P1 != 0x00 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
4✔
210
  if (LE < 4) EXCEPT(SW_WRONG_LENGTH);
4✔
211

212
  device_config_fill_serial(RDATA);
3✔
213
  LL = 4;
3✔
214

215
  return 0;
3✔
216
}
217

218
static int admin_config(const CAPDU *capdu, RAPDU *rapdu) {
22✔
219
  current_config = admin_get_current_config();
22✔
220
  switch (P1) {
22✔
221
  case ADMIN_P1_CFG_LED_ON:
5✔
222
    current_config.led_normally_on = P2 & 1;
5✔
223
    break;
5✔
224
  case ADMIN_P1_CFG_NDEF:
7✔
225
    current_config.ndef_en = P2 & 1;
7✔
226
    break;
7✔
227
  case ADMIN_P1_CFG_WEBUSB_LANDING:
5✔
228
    current_config.webusb_landing_en = P2 & 1;
5✔
229
    break;
5✔
230
  default:
5✔
231
    EXCEPT(SW_WRONG_P1P2);
5✔
232
  }
233
  const int ret = admin_platform_device_config_write(&current_config);
17✔
234
  stop_blinking();
17✔
235
  return ret;
17✔
236
}
237

238
static int admin_read_config(const CAPDU *capdu, RAPDU *rapdu) {
20✔
239
  if (P1 != 0x00 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
20✔
240
  if (LE < 6) EXCEPT(SW_WRONG_LENGTH);
19✔
241

242
  const admin_device_config_t cfg = admin_get_current_config();
18✔
243

244
  RDATA[0] = cfg.led_normally_on;
18✔
245
  RDATA[1] = 0; // reserved
18✔
246
#if ENABLE_APPLET_NDEF
247
  RDATA[2] = ndef_is_read_only();
18✔
248
#else
249
  RDATA[2] = 0;
250
#endif
251
  RDATA[3] = cfg.ndef_en;
18✔
252
  RDATA[4] = cfg.webusb_landing_en;
18✔
253
  RDATA[5] = 0; // reserved
18✔
254
  LL = 6;
18✔
255

256
  return 0;
18✔
257
}
258

259
static void admin_put_u32_be(uint8_t *buf, uint32_t value) {
8✔
260
  buf[0] = (uint8_t)(value >> 24);
8✔
261
  buf[1] = (uint8_t)(value >> 16);
8✔
262
  buf[2] = (uint8_t)(value >> 8);
8✔
263
  buf[3] = (uint8_t)value;
8✔
264
}
8✔
265

266
static int admin_sum_fs_usage(const admin_fs_usage_source_t *source, uint32_t *bytes, uint8_t *flags) {
46✔
267
  int size = get_file_size(source->path);
46✔
268
  if (size == LFS_ERR_NOENT) {
46✔
269
    *flags |= ADMIN_APPLET_USAGE_FLAG_MISSING;
15✔
270
  } else if (size < 0) {
31✔
NEW
271
    return size;
×
272
  } else {
273
    *bytes += (uint32_t)size;
31✔
274
  }
275

276
  for (uint8_t i = 0; i < source->attr_count; ++i) {
97✔
277
    size = get_attr_size(source->path, source->attrs[i]);
51✔
278
    if (size == LFS_ERR_NOENT || size == LFS_ERR_NOATTR) {
51✔
279
      *flags |= ADMIN_APPLET_USAGE_FLAG_MISSING;
11✔
280
      continue;
11✔
281
    }
282
    if (size < 0) return size;
40✔
283
    *bytes += (uint32_t)size;
40✔
284
  }
285

286
  return 0;
46✔
287
}
288

289
static int admin_flash_usage_applets(const CAPDU *capdu, RAPDU *rapdu) {
2✔
290
  UNUSED(capdu);
291
  if (LE < ADMIN_APPLET_USAGE_RESPONSE_LENGTH) EXCEPT(SW_WRONG_LENGTH);
2✔
292

293
  size_t off = 0;
1✔
294
  uint32_t attributed_bytes = 0;
1✔
295
  for (size_t i = 0; i < sizeof(applet_usage_defs) / sizeof(applet_usage_defs[0]); ++i) {
8✔
296
    uint32_t bytes = 0;
7✔
297
    uint8_t flags = 0;
7✔
298
    for (uint8_t j = 0; j < applet_usage_defs[i].source_count; ++j) {
53✔
299
      if (admin_sum_fs_usage(&applet_usage_defs[i].sources[j], &bytes, &flags) < 0) return -1;
46✔
300
    }
301
    if (UINT32_MAX - attributed_bytes < bytes) {
7✔
NEW
302
      attributed_bytes = UINT32_MAX;
×
303
    } else {
304
      attributed_bytes += bytes;
7✔
305
    }
306

307
    RDATA[off++] = applet_usage_defs[i].id;
7✔
308
    RDATA[off++] = flags;
7✔
309
    admin_put_u32_be(RDATA + off, bytes);
7✔
310
    off += 4;
7✔
311
  }
312

313
  int fs_usage_bytes = get_fs_usage_bytes();
1✔
314
  if (fs_usage_bytes < 0) return -1;
1✔
315
  const uint32_t unattributed_bytes =
1✔
316
      (uint32_t)fs_usage_bytes > attributed_bytes ? (uint32_t)fs_usage_bytes - attributed_bytes : 0;
1✔
317
  RDATA[off++] = ADMIN_APPLET_USAGE_ID_SYSTEM;
1✔
318
  RDATA[off++] = 0;
1✔
319
  admin_put_u32_be(RDATA + off, unattributed_bytes);
1✔
320
  off += 4;
1✔
321

322
  LL = off;
1✔
323
  return 0;
1✔
324
}
325

326
static int admin_flash_usage(const CAPDU *capdu, RAPDU *rapdu) {
8✔
327
  if (P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
8✔
328
  if (P1 == ADMIN_FLASH_USAGE_APPLETS) return admin_flash_usage_applets(capdu, rapdu);
7✔
329
  if (P1 != ADMIN_FLASH_USAGE_TOTAL) EXCEPT(SW_WRONG_P1P2);
5✔
330
  if (LE < 2) EXCEPT(SW_WRONG_LENGTH);
5✔
331

332
  RDATA[0] = get_fs_usage();
4✔
333
  RDATA[1] = get_fs_size();
4✔
334
  LL = 2;
4✔
335

336
  return 0;
4✔
337
}
338

339
static int admin_write_kbd_keymap(const CAPDU *capdu, RAPDU *rapdu) {
3✔
340
  UNUSED(rapdu);
341
  // Payload is 128 ASCII entries, each {modifier, HID usage}. P2 carries a
342
  // host-defined layout id so tooling can identify what was installed.
343
  if (P1 != 0x00) EXCEPT(SW_WRONG_P1P2);
3✔
344
  if (LC != ADMIN_KBD_KEYMAP_LENGTH) EXCEPT(SW_WRONG_LENGTH);
2✔
345
  return admin_platform_kbd_keymap_write(P2, DATA, LC);
1✔
346
}
347

348
static int admin_read_kbd_keymap(const CAPDU *capdu, RAPDU *rapdu) {
7✔
349
  if (P1 != 0x00) EXCEPT(SW_WRONG_P1P2);
7✔
350
  if (LC != 0) EXCEPT(SW_WRONG_LENGTH);
7✔
351
  switch (P2) {
6✔
352
  case ADMIN_P2_KBD_READ_LAYOUT_ID:
2✔
353
    if (LE < 1) EXCEPT(SW_WRONG_LENGTH);
2✔
354
    if (admin_platform_kbd_keymap_read(RDATA, NULL, 0) < 0) EXCEPT(SW_REFERENCE_DATA_NOT_FOUND);
2✔
355
    LL = 1;
1✔
356
    return 0;
1✔
357
  case ADMIN_P2_KBD_READ_KEYMAP: {
3✔
358
    uint8_t layout_id;
3✔
359
    if (LE < ADMIN_KBD_KEYMAP_LENGTH) EXCEPT(SW_WRONG_LENGTH);
3✔
360
    if (admin_platform_kbd_keymap_read(&layout_id, RDATA, ADMIN_KBD_KEYMAP_LENGTH) < 0)
2✔
361
      EXCEPT(SW_REFERENCE_DATA_NOT_FOUND);
1✔
362
    LL = ADMIN_KBD_KEYMAP_LENGTH;
1✔
363
    return 0;
1✔
364
  }
365
  default:
1✔
366
    EXCEPT(SW_WRONG_P1P2);
1✔
367
  }
368
}
369

370
static int admin_clear_kbd_keymap(const CAPDU *capdu, RAPDU *rapdu) {
3✔
371
  UNUSED(rapdu);
372
  if (P1 != 0x00 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
3✔
373
  if (LC != 0) EXCEPT(SW_WRONG_LENGTH);
2✔
374
  return admin_platform_kbd_keymap_clear();
1✔
375
}
376

377
static int admin_factory_reset(const CAPDU *capdu, RAPDU *rapdu) {
4✔
378
  int ret;
379
  if (P1 != 0x00) EXCEPT(SW_WRONG_P1P2);
4✔
380
  if (LC != 5) EXCEPT(SW_WRONG_LENGTH);
4✔
381
  if (memcmp_s(DATA, "RESET", 5) != 0) EXCEPT(SW_WRONG_DATA);
4✔
382
#ifndef FUZZ
383
  ret = pin_get_retries(&pin);
4✔
384
  if (ret > 0) EXCEPT(SW_CONDITIONS_NOT_SATISFIED);
4✔
385

386
  if (is_nfc()) EXCEPT(SW_CONDITIONS_NOT_SATISFIED);
3✔
387
  if (strong_user_presence_test() < 0) EXCEPT(SW_SECURITY_STATUS_NOT_SATISFIED);
3✔
388
#endif
389

390
  DBG_MSG("factory reset begins\n");
3✔
391
  ret = openpgp_install(1);
3✔
392
  if (ret < 0) return ret;
3✔
393
  ret = piv_install(1);
3✔
394
  if (ret < 0) return ret;
3✔
395
  ret = oath_install(1);
3✔
396
  if (ret < 0) return ret;
3✔
397
  ret = ctap_install(1);
3✔
398
  if (ret < 0) return ret;
3✔
399
#if ENABLE_APPLET_NDEF
400
  ret = ndef_install(1);
3✔
401
  if (ret < 0) return ret;
3✔
402
#endif
403
  ret = pass_install(1);
3✔
404
  if (ret < 0) return ret;
3✔
405
  ret = admin_install(1);
3✔
406
  if (ret < 0) return ret;
3✔
407

408
  return 0;
3✔
409
}
410

411
void device_config_fill_serial(uint8_t *buf) {
412
  if (admin_platform_serial_read(buf) < 0) memset(buf, 0, 4);
456✔
413
}
456✔
414

415
int admin_process_apdu(const CAPDU *capdu, RAPDU *rapdu) {
416
  LL = 0;
347✔
417
  SW = SW_NO_ERROR;
347✔
418

419
  int ret = 0;
347✔
420
  switch (INS) {
347✔
421
  case ADMIN_INS_SELECT:
49✔
422
    if (P1 != 0x04 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
49✔
423
    return 0;
49✔
424

425
  case ADMIN_INS_READ_VERSION:
4✔
426
    if (P1 > 1 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
4✔
427
    if (P1 == 0)
4✔
428
      ret = admin_vendor_version(capdu, rapdu);
2✔
429
    else if (P1 == 1)
2✔
430
      ret = admin_vendor_hw_variant(capdu, rapdu);
2✔
431
    goto done;
4✔
432

433
  case ADMIN_INS_READ_SN:
6✔
434
    if (P1 > 1 || P2 != 0x00) EXCEPT(SW_WRONG_P1P2);
6✔
435
    if (P1 == 0)
6✔
436
      ret = admin_read_sn(capdu, rapdu);
4✔
437
    else if (P1 == 1)
2✔
438
      ret = admin_vendor_hw_sn(capdu, rapdu);
2✔
439
    goto done;
6✔
440

441
  case ADMIN_INS_FLASH_USAGE:
8✔
442
    ret = admin_flash_usage(capdu, rapdu);
8✔
443
    goto done;
8✔
444

445
  case ADMIN_INS_NFC_ENABLE:
×
446
    ret = admin_vendor_nfc_enable(capdu, rapdu, pin.is_validated);
×
447
    goto done;
×
448

449
  case ADMIN_INS_FACTORY_RESET:
4✔
450
    ret = admin_factory_reset(capdu, rapdu);
4✔
451
    goto done;
4✔
452

453
  case ADMIN_INS_VERIFY:
112✔
454
    ret = admin_verify(capdu, rapdu);
112✔
455
    goto done;
112✔
456

457
  default:
164✔
458
    break;
164✔
459
  }
460

461
#ifndef FUZZ
462
  if (!pin.is_validated) EXCEPT(SW_SECURITY_STATUS_NOT_SATISFIED);
164✔
463
#endif
464

465
  switch (INS) {
117✔
466
  case ADMIN_INS_WRITE_FIDO_PRIVATE_KEY:
2✔
467
    ret = ctap_install_private_key(capdu, rapdu);
2✔
468
    break;
2✔
469
  case ADMIN_INS_WRITE_FIDO_CERT:
2✔
470
    ret = ctap_install_cert(capdu, rapdu);
2✔
471
    break;
2✔
472
  case ADMIN_INS_RESET_OPENPGP:
1✔
473
    ret = openpgp_install(1);
1✔
474
    break;
1✔
475
  case ADMIN_INS_RESET_PIV:
1✔
476
    ret = piv_install(1);
1✔
477
    break;
1✔
478
  case ADMIN_INS_RESET_OATH:
1✔
479
    ret = oath_install(1);
1✔
480
    break;
1✔
481
  case ADMIN_INS_RESET_NDEF:
3✔
482
#if ENABLE_APPLET_NDEF
483
    ret = ndef_install(1);
3✔
484
#else
485
    EXCEPT(SW_INS_NOT_SUPPORTED);
486
#endif
487
    break;
3✔
488
  case ADMIN_INS_TOGGLE_NDEF_READ_ONLY:
22✔
489
#if ENABLE_APPLET_NDEF
490
    ret = ndef_toggle_read_only(capdu, rapdu);
22✔
491
#else
492
    EXCEPT(SW_INS_NOT_SUPPORTED);
493
#endif
494
    break;
22✔
495
  case ADMIN_INS_RESET_PASS:
×
496
#if ENABLE_PASS
497
    ret = pass_install(1);
×
498
#else
499
    EXCEPT(SW_INS_NOT_SUPPORTED);
500
#endif
501
    break;
×
502
  case ADMIN_INS_RESET_CTAP:
×
503
    ret = ctap_install(1);
×
504
    break;
×
505
  case ADMIN_INS_READ_CTAP_SM2_CONFIG:
×
506
    ret = ctap_read_sm2_config(capdu, rapdu);
×
507
    break;
×
508
  case ADMIN_INS_WRITE_CTAP_SM2_CONFIG:
×
509
    ret = ctap_write_sm2_config(capdu, rapdu);
×
510
    break;
×
511
  case ADMIN_INS_CHANGE_PIN:
7✔
512
    ret = admin_change_pin(capdu, rapdu);
7✔
513
    break;
7✔
514
  case ADMIN_INS_WRITE_SN:
3✔
515
    ret = admin_write_sn(capdu, rapdu);
3✔
516
    break;
3✔
517
  case ADMIN_INS_CONFIG:
22✔
518
    ret = admin_config(capdu, rapdu);
22✔
519
    break;
22✔
520
  case ADMIN_INS_READ_CONFIG:
20✔
521
    ret = admin_read_config(capdu, rapdu);
20✔
522
    break;
20✔
523
  case ADMIN_INS_READ_PASS_CONFIG:
6✔
524
#if ENABLE_PASS
525
    ret = pass_read_config(capdu, rapdu);
6✔
526
#else
527
    EXCEPT(SW_INS_NOT_SUPPORTED);
528
#endif
529
    break;
6✔
530
  case ADMIN_INS_WRITE_PASS_CONFIG:
12✔
531
#if ENABLE_PASS
532
    ret = pass_write_config(capdu, rapdu);
12✔
533
#else
534
    EXCEPT(SW_INS_NOT_SUPPORTED);
535
#endif
536
    break;
12✔
537
  case ADMIN_INS_WRITE_KBD_KEYMAP:
3✔
538
    ret = admin_write_kbd_keymap(capdu, rapdu);
3✔
539
    break;
3✔
540
  case ADMIN_INS_READ_KBD_KEYMAP:
7✔
541
    ret = admin_read_kbd_keymap(capdu, rapdu);
7✔
542
    break;
7✔
543
  case ADMIN_INS_CLEAR_KBD_KEYMAP:
3✔
544
    ret = admin_clear_kbd_keymap(capdu, rapdu);
3✔
545
    break;
3✔
546
  case ADMIN_INS_VENDOR_SPECIFIC:
1✔
547
    ret = admin_vendor_specific(capdu, rapdu);
1✔
548
    break;
1✔
549
  default:
1✔
550
    EXCEPT(SW_INS_NOT_SUPPORTED);
1✔
551
  }
552

553
done:
250✔
554
  if (ret < 0) EXCEPT(SW_UNABLE_TO_PROCESS);
250✔
555
  return 0;
250✔
556
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc