15 Commits

Author SHA1 Message Date
Pol Henarejos
0b7beeec8c Introduce GET BULK DATA to execute GET DATA in multiple DO with a single APDU.
It saves considerable bandwidth since only one APDU/RAPDU are transmitted.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-19 18:30:50 +01:00
Pol Henarejos
1f037da326 Do no parse flash data as TLV.
Solves #50.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-19 15:57:59 +01:00
Pol Henarejos
ffbdef14b6 Set rollback globally to avoid incompatibilities.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-19 15:50:32 +01:00
Pol Henarejos
ad59aa8c1a Fixed ACL for EF_CHR_CERT.
Fixes #51.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-18 00:52:49 +01:00
Pol Henarejos
fcca95715e Fixed a bug allowing to write without PW3.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-18 00:52:19 +01:00
Pol Henarejos
704df76499 DO is cleared when no data is provided.
Solves #50.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-18 00:38:52 +01:00
Pol Henarejos
e6cc190c4f Do not interpret private DO as TLV.
Solves #50.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-18 00:29:59 +01:00
Pol Henarejos
615737807a Add support for private DO.
Closes #50.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-17 16:46:03 +01:00
Pol Henarejos
e563bb3379 Fixed pw2 verify persistence.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-17 16:45:49 +01:00
Pol Henarejos
374cff588c Fix secure boot enable.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 16:31:12 +01:00
Pol Henarejos
ca8d81fd20 Fix key rotation. Now also rotates cert & metadata.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 01:32:47 +01:00
Pol Henarejos
5d71e69c1d Do not allow slot move from retired to active.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 00:52:25 +01:00
Pol Henarejos
75691b6a42 Fix crash when attestating.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 00:46:58 +01:00
Pol Henarejos
811f33e282 Fix extension set in attestation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 00:46:26 +01:00
Pol Henarejos
90b62f067d Add support for HIGH/LOW ESP32 LED
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-04 23:47:07 +01:00
10 changed files with 326 additions and 63 deletions

View File

@@ -76,7 +76,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/openpgp/defs.c ${CMAKE_CURRENT_LIST_DIR}/src/openpgp/defs.c
) )
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/openpgp/version.h" 3) SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/openpgp/version.h")
if(ESP_PLATFORM) if(ESP_PLATFORM)
project(pico_openpgp) project(pico_openpgp)

View File

@@ -29,10 +29,20 @@ int cmd_get_data() {
if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) { if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND(); return SW_REFERENCE_NOT_FOUND();
} }
if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) { if (fid == EF_PRIV_DO_3) {
if (!has_pw2 && !has_pw3) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
}
else if (fid == EF_PRIV_DO_4) {
if (!has_pw3) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
}
else if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) {
return SW_SECURITY_STATUS_NOT_SATISFIED(); return SW_SECURITY_STATUS_NOT_SATISFIED();
} }
if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected if (currentEF && currentEF->fid == fid) { // previously selected same EF
ef = currentEF; ef = currentEF;
} }
else { else {
@@ -44,23 +54,25 @@ int cmd_get_data() {
} }
uint16_t fids[] = { 1, fid }; uint16_t fids[] = { 1, fid };
uint16_t data_len = parse_do(fids, 1); uint16_t data_len = parse_do(fids, 1);
uint8_t *p = NULL; if (!(ef->type & FILE_DATA_FLASH)) {
uint16_t tg = 0; uint8_t *p = NULL;
uint16_t tg_len = 0; uint16_t tg = 0;
asn1_ctx_t ctxi; uint16_t tg_len = 0;
asn1_ctx_init(res_APDU, data_len, &ctxi); asn1_ctx_t ctxi;
if (walk_tlv(&ctxi, &p, &tg, &tg_len, NULL)) { asn1_ctx_init(res_APDU, data_len, &ctxi);
uint8_t dec = 2; if (walk_tlv(&ctxi, &p, &tg, &tg_len, NULL)) {
if ((tg & 0x1f) == 0x1f) { uint8_t dec = 2;
dec++; if ((tg & 0x1f) == 0x1f) {
} dec++;
if ((res_APDU[dec - 1] & 0xF0) == 0x80) { }
dec += (res_APDU[dec - 1] & 0x0F); if ((res_APDU[dec - 1] & 0xF0) == 0x80) {
} dec += (res_APDU[dec - 1] & 0x0F);
if (tg_len + dec == data_len) { }
memmove(res_APDU, res_APDU + dec, data_len - dec); if (tg_len + dec == data_len) {
data_len -= dec; memmove(res_APDU, res_APDU + dec, data_len - dec);
res_APDU_size -= dec; data_len -= dec;
res_APDU_size -= dec;
}
} }
} }
if (is_gpg == false) { if (is_gpg == false) {
@@ -128,3 +140,10 @@ int cmd_get_next_data() {
select_file(ef); select_file(ef);
return cmd_get_data(); return cmd_get_data();
} }
int cmd_get_bulk_data() {
if (apdu.nc < 3) {
return SW_WRONG_LENGTH();
}
return bulk_cmd(cmd_get_data);
}

View File

@@ -32,43 +32,54 @@ int cmd_put_data() {
if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) { if (!authenticate_action(ef, ACL_OP_UPDATE_ERASE)) {
return SW_SECURITY_STATUS_NOT_SATISFIED(); return SW_SECURITY_STATUS_NOT_SATISFIED();
} }
if ((fid == EF_PRIV_DO_1 || fid == EF_PRIV_DO_3) && (!has_pw2 && !has_pw3)) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (!(fid == EF_PRIV_DO_1 || fid == EF_PRIV_DO_3) && !has_pw3) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (fid == EF_PW_STATUS) { if (fid == EF_PW_STATUS) {
fid = EF_PW_PRIV; fid = EF_PW_PRIV;
apdu.nc = 4; //we silently ommit the reset parameters apdu.nc = 4; //we silently ommit the reset parameters
} }
if (currentEF && (currentEF->fid & 0x1FF0) == (fid & 0x1FF0)) { //previously selected if (currentEF && currentEF->fid == fid) { // previously selected same EF
ef = currentEF; ef = currentEF;
} }
if (apdu.nc > 0 && (ef->type & FILE_DATA_FLASH)) { if (ef->type & FILE_DATA_FLASH) {
int r = 0; int r = 0;
if (fid == EF_RC) { if (apdu.nc > 0) {
has_rc = false; if (fid == EF_RC) {
if ((r = load_dek()) != PICOKEY_OK) { has_rc = false;
return SW_EXEC_ERROR(); if ((r = load_dek()) != PICOKEY_OK) {
} return SW_EXEC_ERROR();
uint8_t dhash[33]; }
dhash[0] = apdu.nc; uint8_t dhash[33];
double_hash_pin(apdu.data, apdu.nc, dhash + 1); dhash[0] = apdu.nc;
r = file_put_data(ef, dhash, sizeof(dhash)); double_hash_pin(apdu.data, apdu.nc, dhash + 1);
r = file_put_data(ef, dhash, sizeof(dhash));
file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF); file_t *tf = search_by_fid(EF_DEK, NULL, SPECIFY_EF);
if (!tf) { if (!tf) {
return SW_REFERENCE_NOT_FOUND(); return SW_REFERENCE_NOT_FOUND();
}
uint8_t def[IV_SIZE + 32 + 32 + 32 + 32];
memcpy(def, file_get_data(tf), file_get_size(tf));
hash_multi(apdu.data, apdu.nc, session_rc);
memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32);
aes_encrypt_cfb_256(session_rc, def, def + IV_SIZE + 32, 32);
r = file_put_data(tf, def, sizeof(def));
} }
uint8_t def[IV_SIZE + 32 + 32 + 32 + 32]; else {
memcpy(def, file_get_data(tf), file_get_size(tf)); r = file_put_data(ef, apdu.data, apdu.nc);
hash_multi(apdu.data, apdu.nc, session_rc); }
memcpy(def + IV_SIZE + 32, dek + IV_SIZE, 32); if (r != PICOKEY_OK) {
aes_encrypt_cfb_256(session_rc, def, def + IV_SIZE + 32, 32); return SW_MEMORY_FAILURE();
r = file_put_data(tf, def, sizeof(def)); }
low_flash_available();
} }
else { else {
r = file_put_data(ef, apdu.data, apdu.nc); delete_file(ef);
} }
if (r != PICOKEY_OK) {
return SW_MEMORY_FAILURE();
}
low_flash_available();
} }
return SW_OK(); return SW_OK();
} }

View File

@@ -55,12 +55,12 @@ uint8_t historical_bytes[] = {
uint8_t extended_capabilities[] = { uint8_t extended_capabilities[] = {
10, 0, 10, 0,
0x77, /* 0x7f, /*
* No Secure Messaging supported * No Secure Messaging supported
* GET CHALLENGE supported * GET CHALLENGE supported
* Key import supported * Key import supported
* PW status byte can be put * PW status byte can be put
* No private_use_DO * private_use_DO
* Algorithm attrs are changable * Algorithm attrs are changable
* ENC/DEC with AES * ENC/DEC with AES
* KDF-DO available * KDF-DO available
@@ -68,7 +68,7 @@ uint8_t extended_capabilities[] = {
0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */ 0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */
0x00, 128, /* Max size of GET CHALLENGE */ 0x00, 128, /* Max size of GET CHALLENGE */
0x08, 0x00, /* max. length of cardholder certificate (2KiB) */ 0x08, 0x00, /* max. length of cardholder certificate (2KiB) */
0x00, 0xff, 0x08, 0x00, /* max. length of private DO (2KiB) */
0x00, 0x1 0x00, 0x1
}; };
@@ -114,7 +114,7 @@ file_t file_entries[] = {
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 10 */ { .fid = EF_CH_CERT, .parent = 0, .name = NULL, /* 10 */ { .fid = EF_CH_CERT, .parent = 0, .name = NULL,
.type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *) parse_ch_cert, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *) parse_ch_cert,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 11 */ { .fid = EF_EXLEN_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, /* 11 */ { .fid = EF_EXLEN_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF,
.data = exlen_info, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, .data = exlen_info, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 12 */ { .fid = EF_GFM, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, /* 12 */ { .fid = EF_GFM, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF,
@@ -476,10 +476,28 @@ file_t file_entries[] = {
/* 131 */ { .fid = EF_PW_RETRIES, .parent = 0, .name = NULL, /* 131 */ { .fid = EF_PW_RETRIES, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP }, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 131 */ { .fid = EF_PRIV_DO_1, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 132 */ { .fid = EF_PRIV_DO_2, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 133 */ { .fid = EF_PRIV_DO_3, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 134 */ { .fid = EF_PRIV_DO_4, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
/* 135 */ { .fid = EF_PW_RETRIES, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 136 */ { .fid = EF_PW_STATUS, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 132 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, /* 137 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF,
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO }, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
/* 133 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, /* 138 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL,
.ef_structure = 0, .acl = ACL_NONE } //end .ef_structure = 0, .acl = ACL_NONE } //end
}; };

View File

@@ -163,4 +163,9 @@
#define EF_DEV_CONF 0x1122 #define EF_DEV_CONF 0x1122
#define EF_PRIV_DO_1 0x0101
#define EF_PRIV_DO_2 0x0102
#define EF_PRIV_DO_3 0x0103
#define EF_PRIV_DO_4 0x0104
#endif #endif

View File

@@ -282,7 +282,7 @@ int dek_decrypt(uint8_t *data, size_t len) {
void init_openpgp() { void init_openpgp() {
isUserAuthenticated = false; isUserAuthenticated = false;
has_pw1 = has_pw3 = false; has_pw1 = has_pw2 = has_pw3 = false;
algo_dec = EF_ALGO_PRIV2; algo_dec = EF_ALGO_PRIV2;
algo_aut = EF_ALGO_PRIV3; algo_aut = EF_ALGO_PRIV3;
pk_dec = EF_PK_DEC; pk_dec = EF_PK_DEC;
@@ -293,7 +293,7 @@ void init_openpgp() {
int openpgp_unload() { int openpgp_unload() {
isUserAuthenticated = false; isUserAuthenticated = false;
has_pw1 = has_pw3 = false; has_pw1 = has_pw2 = has_pw3 = false;
algo_dec = EF_ALGO_PRIV2; algo_dec = EF_ALGO_PRIV2;
algo_aut = EF_ALGO_PRIV3; algo_aut = EF_ALGO_PRIV3;
pk_dec = EF_PK_DEC; pk_dec = EF_PK_DEC;
@@ -793,6 +793,7 @@ extern int cmd_terminate_df();
extern int cmd_pso(); extern int cmd_pso();
extern int cmd_keypair_gen(); extern int cmd_keypair_gen();
extern int cmd_reset_retry(); extern int cmd_reset_retry();
extern int cmd_get_bulk_data();
#define INS_VERIFY 0x20 #define INS_VERIFY 0x20
#define INS_MSE 0x22 #define INS_MSE 0x22
@@ -807,6 +808,7 @@ extern int cmd_reset_retry();
#define INS_SELECT_DATA 0xA5 #define INS_SELECT_DATA 0xA5
#define INS_GET_DATA 0xCA #define INS_GET_DATA 0xCA
#define INS_GET_NEXT_DATA 0xCC #define INS_GET_NEXT_DATA 0xCC
#define INS_GET_BULK_DATA 0xCE
#define INS_PUT_DATA 0xDA #define INS_PUT_DATA 0xDA
#define INS_IMPORT_DATA 0xDB #define INS_IMPORT_DATA 0xDB
#define INS_TERMINATE_DF 0xE6 #define INS_TERMINATE_DF 0xE6
@@ -830,7 +832,8 @@ static const cmd_t cmds[] = {
{ INS_VERSION, cmd_version_openpgp }, { INS_VERSION, cmd_version_openpgp },
{ INS_SELECT_DATA, cmd_select_data }, { INS_SELECT_DATA, cmd_select_data },
{ INS_GET_NEXT_DATA, cmd_get_next_data }, { INS_GET_NEXT_DATA, cmd_get_next_data },
{ 0x00, 0x0 } { INS_GET_BULK_DATA, cmd_get_bulk_data },
{ 0x00, NULL }
}; };
int openpgp_process_apdu() { int openpgp_process_apdu() {

View File

@@ -124,7 +124,7 @@ static int x509_create_cert(void *pk_ctx, uint8_t algo, uint8_t slot, bool attes
mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x08", 10, 0, &meta[1], 2); mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x08", 10, 0, &meta[1], 2);
} }
uint8_t v = 1; uint8_t v = 1;
mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x09", 10, 0, &v, sizeof(serial)); mbedtls_x509write_crt_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xC4\x0A\x03\x09", 10, 0, &v, sizeof(v));
} }
else { else {
uint8_t wslot = slot; uint8_t wslot = slot;
@@ -153,9 +153,7 @@ static int x509_create_cert(void *pk_ctx, uint8_t algo, uint8_t slot, bool attes
} }
mbedtls_x509write_crt_set_subject_key_identifier(&ctx); mbedtls_x509write_crt_set_subject_key_identifier(&ctx);
mbedtls_x509write_crt_set_authority_key_identifier(&ctx); mbedtls_x509write_crt_set_authority_key_identifier(&ctx);
mbedtls_x509write_crt_set_key_usage(&ctx, mbedtls_x509write_crt_set_key_usage(&ctx, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN);
MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
MBEDTLS_X509_KU_KEY_CERT_SIGN);
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL); int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL);
/* skey cannot be freed, as it is freed later */ /* skey cannot be freed, as it is freed later */
if (attestation) { if (attestation) {
@@ -1020,6 +1018,9 @@ static int cmd_move_key() {
if ((!IS_KEY(to) && to != 0xFF) || !IS_KEY(from)) { if ((!IS_KEY(to) && to != 0xFF) || !IS_KEY(from)) {
return SW_INCORRECT_P1P2(); return SW_INCORRECT_P1P2();
} }
if (IS_RETIRED(from) && IS_ACTIVE(to)) {
return SW_INCORRECT_P1P2();
}
if (from == 0x93) { if (from == 0x93) {
from = EF_PIV_KEY_RETIRED18; from = EF_PIV_KEY_RETIRED18;
} }
@@ -1030,9 +1031,85 @@ static int cmd_move_key() {
if (!(efs = search_by_fid(from, NULL, SPECIFY_EF)) || (!(efd = search_by_fid(to, NULL, SPECIFY_EF)) && to != 0xFF)) { if (!(efs = search_by_fid(from, NULL, SPECIFY_EF)) || (!(efd = search_by_fid(to, NULL, SPECIFY_EF)) && to != 0xFF)) {
return SW_FILE_NOT_FOUND(); return SW_FILE_NOT_FOUND();
} }
uint16_t cert_from_fid = 0;
uint16_t cert_to_fid = 0;
if (from == EF_PIV_KEY_AUTHENTICATION) {
cert_from_fid = EF_PIV_AUTHENTICATION;
}
else if (from == EF_PIV_KEY_SIGNATURE) {
cert_from_fid = EF_PIV_SIGNATURE;
}
else if (from == EF_PIV_KEY_KEYMGM) {
cert_from_fid = EF_PIV_KEY_MANAGEMENT;
}
else if (from == EF_PIV_KEY_CARDAUTH) {
cert_from_fid = EF_PIV_CARD_AUTH;
}
else if (from == EF_PIV_KEY_RETIRED18) {
cert_from_fid = EF_PIV_RETIRED18;
}
else {
cert_from_fid = from + 0xC08B;
}
if (to != 0xFF) {
if (to == EF_PIV_KEY_AUTHENTICATION) {
cert_to_fid = EF_PIV_AUTHENTICATION;
}
else if (to == EF_PIV_KEY_SIGNATURE) {
cert_to_fid = EF_PIV_SIGNATURE;
}
else if (to == EF_PIV_KEY_KEYMGM) {
cert_to_fid = EF_PIV_KEY_MANAGEMENT;
}
else if (to == EF_PIV_KEY_CARDAUTH) {
cert_to_fid = EF_PIV_CARD_AUTH;
}
else if (to == EF_PIV_KEY_RETIRED18) {
cert_to_fid = EF_PIV_RETIRED18;
}
else {
cert_to_fid = to + 0xC08B;
}
}
if (to != 0xFF) { if (to != 0xFF) {
file_put_data(efd, file_get_data(efs), file_get_size(efs)); file_put_data(efd, file_get_data(efs), file_get_size(efs));
} }
file_t *ef_cert_from = search_by_fid(cert_from_fid, NULL, SPECIFY_EF);
if (to != 0xFF) {
file_t *ef_cert_to = search_by_fid(cert_to_fid, NULL, SPECIFY_EF);
if (!ef_cert_to) {
return SW_FILE_NOT_FOUND();
}
if (file_has_data(ef_cert_from)) {
file_put_data(ef_cert_to, file_get_data(ef_cert_from), file_get_size(ef_cert_from));
}
else {
flash_clear_file(ef_cert_to);
}
}
if (ef_cert_from) {
flash_clear_file(ef_cert_from);
}
uint8_t *meta_src = NULL;
int meta_len = meta_find(from, &meta_src);
if (to != 0xFF) {
if (meta_len > 0 && meta_src != NULL) {
uint8_t *meta_copy = (uint8_t *)calloc(1, (size_t)meta_len);
if (!meta_copy) {
return SW_MEMORY_FAILURE();
}
memcpy(meta_copy, meta_src, (size_t)meta_len);
meta_add(to, meta_copy, (uint16_t)meta_len);
free(meta_copy);
}
else {
meta_delete(to);
}
}
meta_delete(from);
flash_clear_file(efs); flash_clear_file(efs);
low_flash_available(); low_flash_available();
return SW_OK(); return SW_OK();
@@ -1154,6 +1231,7 @@ static int cmd_attestation() {
return SW_INCORRECT_PARAMS(); return SW_INCORRECT_PARAMS();
} }
int r = 0; int r = 0;
uint8_t abuf[2048];
if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048) { if (meta[0] == PIV_ALGO_RSA1024 || meta[0] == PIV_ALGO_RSA2048) {
mbedtls_rsa_context ctx; mbedtls_rsa_context ctx;
mbedtls_rsa_init(&ctx); mbedtls_rsa_init(&ctx);
@@ -1162,7 +1240,7 @@ static int cmd_attestation() {
mbedtls_rsa_free(&ctx); mbedtls_rsa_free(&ctx);
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} }
r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048); r = x509_create_cert(&ctx, meta[0], key_ref, true, abuf, sizeof(abuf));
mbedtls_rsa_free(&ctx); mbedtls_rsa_free(&ctx);
} }
else if (meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) { else if (meta[0] == PIV_ALGO_ECCP256 || meta[0] == PIV_ALGO_ECCP384) {
@@ -1173,7 +1251,7 @@ static int cmd_attestation() {
mbedtls_ecdsa_free(&ctx); mbedtls_ecdsa_free(&ctx);
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} }
r = x509_create_cert(&ctx, meta[0], key_ref, true, res_APDU, 2048); r = x509_create_cert(&ctx, meta[0], key_ref, true, abuf, sizeof(abuf));
mbedtls_ecdsa_free(&ctx); mbedtls_ecdsa_free(&ctx);
} }
else { else {
@@ -1182,7 +1260,7 @@ static int cmd_attestation() {
if (r <= 0) { if (r <= 0) {
return SW_EXEC_ERROR(); return SW_EXEC_ERROR();
} }
memmove(res_APDU, res_APDU + 2048 - r, r); memcpy(res_APDU, abuf + sizeof(abuf) - r, r);
res_APDU_size = r; res_APDU_size = r;
return SW_OK(); return SW_OK();
} }

View File

@@ -201,7 +201,7 @@ def test_extended_capabilities(card):
pytest.skip("Yubikey returns 6B00 when no key") pytest.skip("Yubikey returns 6B00 when no key")
else: else:
a = get_data_object(card, 0xc0) a = get_data_object(card, 0xc0)
assert a == None or match(b'[\x70\x74\x75\x77]\x00\x00.[\x00\x08]\x00\x00\xff[\x00\x01][\x00\x01]', a) assert a == None or match(b'[\x70\x7F\x75\x77]\x00\x00.[\x00\x08]\x00\x08\x00[\x00\x01][\x00\x01]', a)
def test_key_attributes_1(card): def test_key_attributes_1(card):
if card.is_yubikey: if card.is_yubikey:

View File

@@ -28,6 +28,26 @@ from card_const import *
from constants_for_test import * from constants_for_test import *
import pytest import pytest
PRIVATE_DO_0101 = (0x01, 0x01)
PRIVATE_DO_0102 = (0x01, 0x02)
PRIVATE_DO_0103 = (0x01, 0x03)
PRIVATE_DO_0104 = (0x01, 0x04)
def _assert_sw(e, sw_list):
sw = e.args[0]
assert sw in sw_list
def _expect_security_error(callable_):
try:
callable_()
except ValueError as e:
_assert_sw(e, ["6982", "6985"])
return
assert False
class Test_Card_Personalize_Card_2(object): class Test_Card_Personalize_Card_2(object):
def test_verify_pw3_0(self, card): def test_verify_pw3_0(self, card):
v = card.verify(3, PW3_TEST0) v = card.verify(3, PW3_TEST0)
@@ -202,3 +222,112 @@ class Test_Card_Personalize_Card_2(object):
def test_verify_pw3_2(self, card): def test_verify_pw3_2(self, card):
v = card.verify(3, PW3_TEST0) v = card.verify(3, PW3_TEST0)
assert v assert v
def test_private_do_0101_write_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
r = card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_pw3_ok")
assert r
def test_private_do_0101_write_fail_with_pw1_81(self, card):
card.cmd_select_openpgp()
v = card.verify(1, PW1_TEST4)
assert v
_expect_security_error(
lambda: card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_pw1_81")
)
def test_private_do_0101_write_ok_with_pw1_82(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
r = card.cmd_put_data(PRIVATE_DO_0101[0], PRIVATE_DO_0101[1], b"priv0101_ok")
assert r
def test_private_do_0101_read_always(self, card):
card.cmd_select_openpgp()
data = get_data_object(card, 0x0101)
assert data == b"priv0101_ok" or data == b"priv0101_pw3_ok"
def test_private_do_0102_write_fail_with_pw1(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
_expect_security_error(
lambda: card.cmd_put_data(PRIVATE_DO_0102[0], PRIVATE_DO_0102[1], b"priv0102_pw1")
)
def test_private_do_0102_write_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
r = card.cmd_put_data(PRIVATE_DO_0102[0], PRIVATE_DO_0102[1], b"priv0102_ok")
assert r
def test_private_do_0102_read_always(self, card):
card.cmd_select_openpgp()
data = get_data_object(card, 0x0102)
assert data == b"priv0102_ok"
def test_private_do_0103_read_fail_without_auth(self, card):
card.cmd_select_openpgp()
_expect_security_error(lambda: get_data_object(card, 0x0103))
def test_private_do_0103_read_fail_with_pw1_81(self, card):
card.cmd_select_openpgp()
v = card.verify(1, PW1_TEST4)
assert v
_expect_security_error(lambda: get_data_object(card, 0x0103))
def test_private_do_0103_write_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
r = card.cmd_put_data(PRIVATE_DO_0103[0], PRIVATE_DO_0103[1], b"priv0103_pw3_ok")
assert r
def test_private_do_0103_read_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
data = get_data_object(card, 0x0103)
assert data == b"priv0103_pw3_ok"
def test_private_do_0103_write_ok_with_pw1_82(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
r = card.cmd_put_data(PRIVATE_DO_0103[0], PRIVATE_DO_0103[1], b"priv0103_ok")
assert r
def test_private_do_0103_read_ok_with_pw1_82(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
data = get_data_object(card, 0x0103)
assert data == b"priv0103_ok"
def test_private_do_0104_read_fail_without_auth(self, card):
card.cmd_select_openpgp()
_expect_security_error(lambda: get_data_object(card, 0x0104))
def test_private_do_0104_read_fail_with_pw1(self, card):
card.cmd_select_openpgp()
v = card.verify(2, PW1_TEST4)
assert v
_expect_security_error(lambda: get_data_object(card, 0x0104))
def test_private_do_0104_write_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
r = card.cmd_put_data(PRIVATE_DO_0104[0], PRIVATE_DO_0104[1], b"priv0104_ok")
assert r
def test_private_do_0104_read_ok_with_pw3(self, card):
card.cmd_select_openpgp()
v = card.verify(3, PW3_TEST0)
assert v
data = get_data_object(card, 0x0104)
assert data == b"priv0104_ok"