Skip to content

Commit 0227ae8

Browse files
committed
Preserve compatible resident credential containers
Keep legacy flash and prior container roots readable without migration, cache resident metadata and public keys and preserve silent credential discovery across restarts. Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
1 parent ad6109c commit 0227ae8

23 files changed

Lines changed: 1193 additions & 228 deletions

.github/workflows/test.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,20 @@ jobs:
3737
with:
3838
submodules: recursive
3939
persist-credentials: false
40+
- name: Checkout legacy firmware and submodules
41+
uses: actions/checkout@v6
42+
with:
43+
ref: 43cf570ac3ba4ad8a40685d4bf2c7ccb8d43b2fa
44+
path: legacy
45+
submodules: recursive
46+
persist-credentials: false
4047
- name: Build in container
4148
run: ./tests/build-in-docker.sh
49+
- name: Build legacy emulator in container
50+
run: ./tests/build-legacy-in-docker.sh
4251
- name: Start emulation and test
4352
run: ./tests/run-test-in-docker.sh
53+
- name: Test legacy flash compatibility
54+
run: ./tests/run-test-041-in-docker.sh
55+
- name: Test silent authentication after restart
56+
run: ./tests/run-test-042-in-docker.sh

pico-keys-sdk

src/fido/cbor_config.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,13 @@ int cbor_config(const uint8_t *data, size_t len) {
271271
resetPersistentPinUvAuthToken();
272272
resetPinUvAuthToken();
273273
}
274-
uint8_t *dataf = (uint8_t *) calloc(1, 2 + minPinLengthRPIDs_len * 32);
274+
uint8_t *dataf = (uint8_t *) calloc(1, 2 + minPinLengthRPIDs_len * RP_ID_HASH_LEN);
275275
dataf[0] = (uint8_t)newMinPinLength;
276276
dataf[1] = forceChangePin == ptrue ? 1 : 0;
277277
for (size_t m = 0; m < minPinLengthRPIDs_len; m++) {
278-
mbedtls_sha256((uint8_t *) minPinLengthRPIDs[m].data, minPinLengthRPIDs[m].len, dataf + 2 + m * 32, 0);
278+
mbedtls_sha256((uint8_t *) minPinLengthRPIDs[m].data, minPinLengthRPIDs[m].len, dataf + 2 + m * RP_ID_HASH_LEN, 0);
279279
}
280-
file_put_data(ef_minpin, dataf, (uint16_t)(2 + minPinLengthRPIDs_len * 32));
280+
file_put_data(ef_minpin, dataf, (uint16_t)(2 + minPinLengthRPIDs_len * RP_ID_HASH_LEN));
281281
if (pinPolicy == ptrue) {
282282
file_t *ef_pin_policy = file_search_by_fid(EF_PIN_COMPLEXITY_POLICY, NULL, SPECIFY_EF);
283283
if (ef_pin_policy) {

src/fido/cbor_cred_mgmt.c

Lines changed: 25 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
4040
CborByteString pinUvAuthParam = { 0 }, rpIdHash = { 0 };
4141
PublicKeyCredentialDescriptor credentialId = { 0 };
4242
PublicKeyCredentialUserEntity user = { 0 };
43+
CredentialRp rp_record = { 0 };
4344
size_t resp_size = 0;
4445
CborEncoder encoder, mapEncoder, mapEncoder2;
4546
uint8_t *raw_subpara = NULL;
@@ -115,7 +116,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
115116
}
116117
CBOR_PARSE_MAP_END(map, 1);
117118

118-
if ((rpIdHash.present && rpIdHash.len != 32) || (credentialId.id.present && credentialId.id.len != CRED_RESIDENT_LEN)) {
119+
if ((rpIdHash.present && rpIdHash.len != RP_ID_HASH_LEN) || (credentialId.id.present && credentialId.id.len != CRED_RESIDENT_LEN)) {
119120
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
120121
}
121122

@@ -157,7 +158,6 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
157158
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_RESIDENT_CREDENTIALS - existing));
158159
}
159160
else if (subcommand == 0x02 || subcommand == 0x03) {
160-
file_t *rp_ef = NULL;
161161
if (subcommand == 0x02) {
162162
if (verify((uint8_t)pinUvAuthProtocol, ppaut.data, (const uint8_t *) "\x02", 1, pinUvAuthParam.data) == CborNoError) {
163163
if (!(ppaut.permissions & CTAP_PERMISSION_PCMR)) {
@@ -172,57 +172,38 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
172172
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
173173
}
174174
}
175-
rp_counter = 1;
176-
rp_total = 0;
175+
rp_counter = 0;
176+
if (credential_rp_count(&rp_total) != PICOKEYS_OK) {
177+
CBOR_ERROR(CTAP2_ERR_PROCESSING);
178+
}
177179
}
178180
else {
179-
if (rp_counter > rp_total) {
181+
if (rp_counter >= rp_total) {
180182
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
181183
}
182184
}
183-
uint16_t skip = 0;
184-
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
185-
file_t *tef = file_search((uint16_t)(EF_RP + i));
186-
if (file_has_data(tef) && *file_get_data(tef) > 0) {
187-
if (++skip == rp_counter) {
188-
if (rp_ef == NULL) {
189-
rp_ef = tef;
190-
}
191-
if (subcommand == 0x03) {
192-
break;
193-
}
194-
}
195-
if (subcommand == 0x02) {
196-
rp_total++;
197-
}
198-
}
199-
}
200-
if (rp_ef == NULL) {
201-
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
185+
int rp_ret = credential_rp_load(rp_counter, &rp_record);
186+
if (rp_ret != PICOKEYS_OK) {
187+
CBOR_ERROR(rp_ret == PICOKEYS_ERR_FILE_NOT_FOUND ? CTAP2_ERR_NO_CREDENTIALS : CTAP2_ERR_PROCESSING);
202188
}
203189
rp_counter++;
204190
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, subcommand == 0x02 ? 3 : 2));
205191
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
206192
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 1));
207193
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
208-
uint8_t *rp_id = NULL;
209-
size_t rp_id_len = 0;
210-
if (credential_rp_id_decrypt(rp_ef, &rp_id, &rp_id_len) != 0) {
211-
CBOR_ERROR(CTAP2_ERR_PROCESSING);
212-
}
213-
error = cbor_encode_text_string(&mapEncoder2, (char *) rp_id, rp_id_len);
214-
free(rp_id);
194+
error = cbor_encode_text_string(&mapEncoder2, (char *) rp_record.id, rp_record.id_len);
215195
if (error != CborNoError) {
216-
printf("Cannot encode CBOR [%s:%d]: cbor_encode_text_string(&mapEncoder2, (char *) rp_id, rp_id_len) (%d)\n", __FILE__, __LINE__, error);
196+
printf("Cannot encode CBOR [%s:%d]: cbor_encode_text_string(&mapEncoder2, (char *) rp_record.id, rp_record.id_len) (%d)\n", __FILE__, __LINE__, error);
217197
goto err;
218198
}
219199
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
220200
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
221-
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef) + 1, 32));
201+
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, rp_record.id_hash, sizeof(rp_record.id_hash)));
222202
if (subcommand == 0x02) {
223203
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
224204
CBOR_CHECK(cbor_encode_uint(&mapEncoder, rp_total));
225205
}
206+
credential_rp_free(&rp_record);
226207
}
227208
else if (subcommand == 0x04 || subcommand == 0x05) {
228209
if (subcommand == 0x04 && rpIdHash.present == false) {
@@ -241,7 +222,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
241222
}
242223
if (is_preview == false &&
243224
(!(paut.permissions & CTAP_PERMISSION_CM) ||
244-
(paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
225+
(paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, RP_ID_HASH_LEN) != 0))) {
245226
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
246227
}
247228
}
@@ -406,30 +387,21 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
406387
if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) {
407388
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
408389
}
409-
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
390+
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, RP_ID_HASH_LEN) != 0))) {
410391
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
411392
}
412393
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
413394
file_t *ef = file_search((uint16_t)(EF_CRED + i));
414395
if (file_has_data(ef) && credential_resident_matches_id(ef, credentialId.id.data, credentialId.id.len)) {
415-
uint8_t rp_id_hash[32];
396+
bool legacy_rp = !resident_container_is_marker(ef);
397+
uint8_t rp_id_hash[RP_ID_HASH_LEN];
416398
if (credential_resident_rp_id_hash(ef, rp_id_hash) != PICOKEYS_OK || credential_resident_delete(ef) != PICOKEYS_OK) {
417399
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
418400
}
419-
for (int j = 0; j < MAX_RESIDENT_CREDENTIALS; j++) {
420-
file_t *rp_ef = file_search((uint16_t)(EF_RP + j));
421-
if (file_has_data(rp_ef) && memcmp(file_get_data(rp_ef) + 1, rp_id_hash, 32) == 0) {
422-
uint8_t *rp_data = (uint8_t *) calloc(1, file_get_size(rp_ef));
423-
memcpy(rp_data, file_get_data(rp_ef), file_get_size(rp_ef));
424-
rp_data[0] -= 1;
425-
if (rp_data[0] == 0) {
426-
file_delete(rp_ef);
427-
}
428-
else {
429-
file_put_data(rp_ef, rp_data, file_get_size(rp_ef));
430-
}
431-
free(rp_data);
432-
break;
401+
if (legacy_rp) {
402+
int rp_ret = credential_rp_legacy_decrement(rp_id_hash);
403+
if (rp_ret != PICOKEYS_OK && rp_ret != PICOKEYS_ERR_FILE_NOT_FOUND) {
404+
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
433405
}
434406
}
435407
dev_state_update(DEV_STATE_CRED_STATE);
@@ -447,14 +419,14 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
447419
if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) {
448420
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
449421
}
450-
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
422+
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rpIdHash.data, RP_ID_HASH_LEN) != 0))) {
451423
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
452424
}
453425
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
454426
file_t *ef = file_search((uint16_t)(EF_CRED + i));
455427
if (file_has_data(ef) && credential_resident_matches_id(ef, credentialId.id.data, credentialId.id.len)) {
456428
Credential cred = { 0 };
457-
uint8_t rp_id_hash[32];
429+
uint8_t rp_id_hash[RP_ID_HASH_LEN];
458430
if (credential_resident_rp_id_hash(ef, rp_id_hash) != PICOKEYS_OK || credential_load_resident(ef, rp_id_hash, &cred) != 0) {
459431
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
460432
}
@@ -485,6 +457,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
485457
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
486458
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
487459
err:
460+
credential_rp_free(&rp_record);
488461
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
489462

490463
if (asserted == false) {

src/fido/cbor_get_assertion.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
217217
display_name = NULL;
218218

219219
uint8_t flags = 0;
220-
uint8_t rp_id_hash[32] = {0};
220+
uint8_t rp_id_hash[RP_ID_HASH_LEN] = {0};
221221
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, rp_id_hash, 0);
222222

223223
bool resident = false;
@@ -288,11 +288,11 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
288288
if (!(paut.permissions & CTAP_PERMISSION_GA)) {
289289
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
290290
}
291-
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0) {
291+
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, RP_ID_HASH_LEN) != 0) {
292292
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
293293
}
294294
if (paut.has_rp_id == false) {
295-
memcpy(paut.rp_id_hash, rp_id_hash, 32);
295+
memcpy(paut.rp_id_hash, rp_id_hash, RP_ID_HASH_LEN);
296296
paut.has_rp_id = true;
297297
}
298298
flags |= FIDO2_AUT_FLAG_UV;
@@ -643,10 +643,10 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
643643

644644
uint32_t ctr = get_sign_counter();
645645

646-
size_t aut_data_len = 32 + 1 + 4 + ext_len;
646+
size_t aut_data_len = RP_ID_HASH_LEN + 1 + 4 + ext_len;
647647
aut_data = (uint8_t *) calloc(1, aut_data_len + clientDataHash.len);
648648
uint8_t *pa = aut_data;
649-
memcpy(pa, rp_id_hash, 32); pa += 32;
649+
memcpy(pa, rp_id_hash, RP_ID_HASH_LEN); pa += RP_ID_HASH_LEN;
650650
*pa++ = flags;
651651
pa += put_uint32_be(ctr, pa);
652652
memcpy(pa, ext, ext_len); pa += ext_len;

src/fido/cbor_make_credential.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ static bool minpin_contains_rp(const uint8_t *rp_id_hash) {
3737
}
3838

3939
uint32_t minpin_size = file_get_size(ef_minpin);
40-
if (minpin_size < 2 + SHA256_DIGEST_LENGTH) {
40+
if (minpin_size < 2 + RP_ID_HASH_LEN) {
4141
return false;
4242
}
4343

4444
uint8_t *minpin_data = file_get_data(ef_minpin);
45-
for (uint32_t offset = 2; offset <= minpin_size - SHA256_DIGEST_LENGTH; offset += SHA256_DIGEST_LENGTH) {
46-
if (memcmp(minpin_data + offset, rp_id_hash, SHA256_DIGEST_LENGTH) == 0) {
45+
for (uint32_t offset = 2; offset <= minpin_size - RP_ID_HASH_LEN; offset += RP_ID_HASH_LEN) {
46+
if (memcmp(minpin_data + offset, rp_id_hash, RP_ID_HASH_LEN) == 0) {
4747
return true;
4848
}
4949
}
@@ -251,7 +251,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
251251
#ifndef ENABLE_EMULATION
252252
bool button_pressed = false;
253253
#endif
254-
uint8_t rp_id_hash[32] = {0};
254+
uint8_t rp_id_hash[RP_ID_HASH_LEN] = {0};
255255
mbedtls_sha256((uint8_t *) rp.id.data, rp.id.len, rp_id_hash, 0);
256256

257257
if (pinUvAuthParam.present == true) {
@@ -414,15 +414,15 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
414414
if (!(paut.permissions & CTAP_PERMISSION_MC)) {
415415
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
416416
}
417-
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0) {
417+
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, RP_ID_HASH_LEN) != 0) {
418418
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
419419
}
420420
if (getUserVerifiedFlagValue() == false) {
421421
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
422422
}
423423
flags |= FIDO2_AUT_FLAG_UV;
424424
if (paut.has_rp_id == false) {
425-
memcpy(paut.rp_id_hash, rp_id_hash, 32);
425+
memcpy(paut.rp_id_hash, rp_id_hash, RP_ID_HASH_LEN);
426426
paut.has_rp_id = true;
427427
}
428428
}
@@ -652,10 +652,10 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
652652
CBOR_CHECK(COSE_key(&ekey, &encoder, &mapEncoder));
653653
size_t rs = cbor_encoder_get_buffer_size(&encoder, cbor_buf);
654654

655-
size_t aut_data_len = 32 + 1 + 4 + (16 + 2 + (options.rk == ptrue ? CRED_RESIDENT_LEN : cred_id_len) + rs) + ext_len;
655+
size_t aut_data_len = RP_ID_HASH_LEN + 1 + 4 + (16 + 2 + (options.rk == ptrue ? CRED_RESIDENT_LEN : cred_id_len) + rs) + ext_len;
656656
aut_data = (uint8_t *) calloc(1, aut_data_len + clientDataHash.len);
657657
uint8_t *pa = aut_data;
658-
memcpy(pa, rp_id_hash, 32); pa += 32;
658+
memcpy(pa, rp_id_hash, RP_ID_HASH_LEN); pa += RP_ID_HASH_LEN;
659659
*pa++ = flags;
660660
pa += put_uint32_be(ctr, pa);
661661
memcpy(pa, aaguid, 16); pa += 16;

0 commit comments

Comments
 (0)