fix(openpgp): harden stored data and key handling

This commit is contained in:
Pol Henarejos
2026-07-09 17:21:13 +02:00
parent 951badbf51
commit d157ea9072
11 changed files with 465 additions and 108 deletions
+17 -3
View File
@@ -24,7 +24,9 @@ int cmd_get_data(void) {
if (apdu.nc > 0) {
return SW_WRONG_LENGTH();
}
res_APDU_size = 0;
uint16_t fid = (P1(apdu) << 8) | P2(apdu);
uint16_t requested_fid = fid;
file_t *ef;
if (!(ef = file_search_by_fid(fid, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND();
@@ -42,18 +44,28 @@ int cmd_get_data(void) {
else if (!file_authenticate_action(ef, ACL_OP_READ_SEARCH)) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (currentEF && currentEF->fid == fid) { // previously selected same EF
if (fid == EF_CH_CERT) {
if (currentEF && currentEF->fid >= EF_CH_1 && currentEF->fid <= EF_CH_3) {
ef = currentEF;
}
else if (!(ef = file_search_by_fid(EF_CH_1, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND();
}
}
else if (currentEF && currentEF->fid == fid) { // previously selected same EF
ef = currentEF;
}
else {
select_file(ef);
}
if (ef->data) {
if (fid == EF_PW_STATUS || fid == EF_HIST_BYTES || fid == EF_FULL_AID || fid == EF_SEC_TPL) {
if (requested_fid == EF_PW_STATUS || requested_fid == EF_HIST_BYTES ||
requested_fid == EF_FULL_AID || requested_fid == EF_SEC_TPL) {
is_gpg = true;
}
uint16_t fids[] = { 1, fid };
uint16_t fids[] = { 1, ef->fid };
uint16_t data_len = parse_do(fids, 1);
data_len = MIN(data_len, res_APDU_size);
if (!(ef->type & FILE_DATA_FLASH)) {
uint8_t *p = NULL;
uint16_t tg = 0;
@@ -86,6 +98,8 @@ int cmd_get_data(void) {
if (data_len >= 256) {
off++;
}
data_len = MIN(data_len, OPENPGP_MAX_RESPONSE_SIZE - off);
res_APDU_size = data_len;
memmove(res_APDU + off, res_APDU, data_len);
off = 0;
if (P1(apdu) > 0x0) {
+6
View File
@@ -142,8 +142,14 @@ int cmd_import_data(void) {
algo = file_get_data(algo_ef);
algo_len = file_get_size(algo_ef);
}
if (algo_len == 0 || algo_len > OPENPGP_MAX_ALGORITHM_ATTR_SIZE) {
return SW_WRONG_DATA();
}
int r = 0;
if (algo[0] == ALGO_RSA) {
if (algo_len < 3) {
return SW_WRONG_DATA();
}
mbedtls_rsa_context rsa;
if (p[0] == NULL || len[0] == 0 || p[1] == NULL || len[1] == 0 || p[2] == NULL ||
len[2] == 0) {
+6 -1
View File
@@ -32,12 +32,17 @@ int cmd_internal_aut(void) {
const uint8_t *algo = algorithm_attr_rsa2k + 1;
if (algo_ef && algo_ef->data) {
algo = file_get_data(algo_ef);
uint16_t algo_len = file_get_size(algo_ef);
if (algo_len == 0 || algo_len > OPENPGP_MAX_ALGORITHM_ATTR_SIZE) {
return SW_WRONG_DATA();
}
}
file_t *ef = file_search_by_fid(pk_aut, NULL, SPECIFY_EF);
if (!ef) {
return SW_REFERENCE_NOT_FOUND();
}
if (wait_button_pressed_fid(EF_UIF_AUT) == true) {
uint16_t uif_fid = pk_aut == EF_PK_DEC ? EF_UIF_DEC : EF_UIF_AUT;
if (wait_button_pressed_fid(uif_fid) == true) {
return SW_SECURE_MESSAGE_EXEC_ERROR();
}
int r = PICOKEYS_OK;
+11 -4
View File
@@ -56,8 +56,14 @@ int cmd_keypair_gen(void) {
algo = file_get_data(algo_ef);
algo_len = file_get_size(algo_ef);
}
if (algo_len == 0 || algo_len > OPENPGP_MAX_ALGORITHM_ATTR_SIZE) {
return SW_WRONG_DATA();
}
if (P1(apdu) == 0x80) { //generate
if (algo[0] == ALGO_RSA) {
if (algo_len < 3) {
return SW_WRONG_DATA();
}
int exponent = 65537, nlen = (algo[1] << 8) | algo[2];
printf("KEYPAIR RSA %d\r\n", nlen);
//if (nlen != 2048 && nlen != 4096)
@@ -117,9 +123,10 @@ int cmd_keypair_gen(void) {
uint8_t key_size = 32;
memcpy(aes_key, random_bytes_get(key_size), key_size);
r = store_keys(aes_key, ALGO_AES_256, EF_AES_KEY, true);
/* if storing the key fails, we silently continue */
//if (r != PICOKEYS_OK)
// return SW_EXEC_ERROR();
mbedtls_platform_zeroize(aes_key, sizeof(aes_key));
if (r != PICOKEYS_OK) {
return SW_EXEC_ERROR();
}
}
flash_commit();
return SW_OK();
@@ -129,7 +136,7 @@ int cmd_keypair_gen(void) {
if (!file_has_data(ef)) {
return SW_REFERENCE_NOT_FOUND();
}
res_APDU_size = file_get_size(ef);
res_APDU_size = MIN(file_get_size(ef), OPENPGP_MAX_RESPONSE_SIZE);
memcpy(res_APDU, file_get_data(ef), res_APDU_size);
return SW_OK();
}
+54 -14
View File
@@ -27,6 +27,7 @@
int cmd_pso(void) {
uint16_t algo_fid = 0x0, pk_fid = 0x0;
uint16_t uif_fid = 0x0;
bool is_aes = false;
if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) {
if (!has_pw3 && !has_pw1) {
@@ -34,6 +35,7 @@ int cmd_pso(void) {
}
algo_fid = EF_ALGO_PRIV1;
pk_fid = EF_PK_SIG;
uif_fid = EF_UIF_SIG;
}
else if (P1(apdu) == 0x80 && P2(apdu) == 0x86) {
if (!has_pw3 && !has_pw2) {
@@ -41,6 +43,15 @@ int cmd_pso(void) {
}
algo_fid = algo_dec;
pk_fid = pk_dec;
uif_fid = pk_dec == EF_PK_AUT ? EF_UIF_AUT : EF_UIF_DEC;
}
else if (P1(apdu) == 0x86 && P2(apdu) == 0x80) {
if (!has_pw3 && !has_pw2) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
algo_fid = algo_dec;
pk_fid = pk_dec;
uif_fid = pk_dec == EF_PK_AUT ? EF_UIF_AUT : EF_UIF_DEC;
}
else {
return SW_INCORRECT_P1P2();
@@ -52,32 +63,41 @@ int cmd_pso(void) {
const uint8_t *algo = algorithm_attr_rsa2k + 1;
if (algo_ef && algo_ef->data) {
algo = file_get_data(algo_ef);
}
if (apdu.data[0] == 0x2) { //AES PSO?
if (((apdu.nc - 1) % 16 == 0 && P1(apdu) == 0x80 && P2(apdu) == 0x86) ||
(apdu.nc % 16 == 0 && P1(apdu) == 0x86 && P2(apdu) == 0x80)) {
pk_fid = EF_AES_KEY;
is_aes = true;
uint16_t algo_len = file_get_size(algo_ef);
if (algo_len == 0 || algo_len > OPENPGP_MAX_ALGORITHM_ATTR_SIZE) {
return SW_WRONG_DATA();
}
}
bool aes_decipher = apdu.nc > 0 && P1(apdu) == 0x80 && P2(apdu) == 0x86 &&
apdu.data[0] == 0x02 && (apdu.nc - 1) % 16 == 0;
bool aes_encipher = apdu.nc > 0 && P1(apdu) == 0x86 && P2(apdu) == 0x80 &&
apdu.nc % 16 == 0;
if (aes_decipher || aes_encipher) {
pk_fid = EF_AES_KEY;
is_aes = true;
}
else if (P1(apdu) == 0x86 && P2(apdu) == 0x80) {
return SW_WRONG_LENGTH();
}
file_t *ef = file_search_by_fid(pk_fid, NULL, SPECIFY_EF);
if (!ef) {
return SW_REFERENCE_NOT_FOUND();
}
if (wait_button_pressed_fid(pk_fid == EF_PK_SIG ? EF_UIF_SIG : EF_UIF_DEC) == true) {
if (wait_button_pressed_fid(uif_fid) == true) {
return SW_SECURE_MESSAGE_EXEC_ERROR();
}
int r = PICOKEYS_OK;
size_t key_size = file_get_size(ef);
size_t key_size = 0;
if (is_aes) {
uint8_t aes_key[32];
r = load_aes_key(aes_key, ef);
r = load_aes_key(aes_key, &key_size, ef);
if (r != PICOKEYS_OK) {
memset(aes_key, 0, sizeof(aes_key));
return SW_EXEC_ERROR();
}
const uint16_t aes_key_bits = (uint16_t)(key_size * 8);
if (P1(apdu) == 0x80 && P2(apdu) == 0x86) { //decipher
r = aes_decrypt(aes_key, NULL, key_size, PICOKEYS_AES_MODE_CBC, apdu.data + 1, apdu.nc - 1);
r = aes_decrypt(aes_key, NULL, aes_key_bits, PICOKEYS_AES_MODE_CBC, apdu.data + 1, apdu.nc - 1);
memset(aes_key, 0, sizeof(aes_key));
if (r != PICOKEYS_OK) {
return SW_EXEC_ERROR();
@@ -86,7 +106,7 @@ int cmd_pso(void) {
res_APDU_size = apdu.nc - 1;
}
else if (P1(apdu) == 0x86 && P2(apdu) == 0x80) { //encipher
r = aes_encrypt(aes_key, NULL, key_size, PICOKEYS_AES_MODE_CBC, apdu.data, apdu.nc);
r = aes_encrypt(aes_key, NULL, aes_key_bits, PICOKEYS_AES_MODE_CBC, apdu.data, apdu.nc);
memset(aes_key, 0, sizeof(aes_key));
if (r != PICOKEYS_OK) {
return SW_EXEC_ERROR();
@@ -105,6 +125,7 @@ int cmd_pso(void) {
mbedtls_rsa_free(&ctx);
return SW_EXEC_ERROR();
}
key_size = mbedtls_rsa_get_len(&ctx);
if (P1(apdu) == 0x9E && P2(apdu) == 0x9A) {
size_t olen = 0;
r = rsa_sign(&ctx, apdu.data, apdu.nc, res_APDU, &olen);
@@ -165,23 +186,42 @@ int cmd_pso(void) {
}
//if (len != 2*key_size-1)
// return SW_WRONG_LENGTH();
memcpy(kdata, file_get_data(ef), key_size);
if (dek_decrypt(kdata, key_size) != 0) {
if (load_key_data(ef, kdata, sizeof(kdata), &key_size, true) != PICOKEYS_OK ||
key_size < 2) {
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return SW_EXEC_ERROR();
}
mbedtls_ecdh_init(&ctx);
mbedtls_ecp_group_id gid = kdata[0];
r = mbedtls_ecdh_setup(&ctx, gid);
if (r != 0) {
mbedtls_platform_zeroize(kdata, sizeof(kdata));
mbedtls_ecdh_free(&ctx);
return SW_DATA_INVALID();
}
r = mbedtls_ecp_read_key(gid, (mbedtls_ecdsa_context *)&ctx.ctx.mbed_ecdh, kdata + 1, key_size - 1);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
if (r != 0) {
mbedtls_ecdh_free(&ctx);
return SW_DATA_INVALID();
}
r = mbedtls_ecdh_read_public(&ctx, data - 1, len + 1);
if (mbedtls_ecp_get_type(&ctx.ctx.mbed_ecdh.grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
size_t montgomery_len = mbedtls_mpi_size(&ctx.ctx.mbed_ecdh.grp.P);
const uint8_t *peer = data;
if (len == montgomery_len + 1 && data[0] == 0x40) {
peer = data + 1;
len--;
}
if (len != montgomery_len) {
mbedtls_ecdh_free(&ctx);
return SW_WRONG_LENGTH();
}
r = mbedtls_ecp_point_read_binary(&ctx.ctx.mbed_ecdh.grp, &ctx.ctx.mbed_ecdh.Qp,
peer, len);
}
else {
r = mbedtls_ecdh_read_public(&ctx, data - 1, len + 1);
}
if (r != 0) {
mbedtls_ecdh_free(&ctx);
return SW_DATA_INVALID();
+40 -6
View File
@@ -19,13 +19,20 @@
int cmd_put_data(void) {
uint16_t fid = (P1(apdu) << 8) | P2(apdu);
uint16_t requested_fid = fid;
bool is_algorithm_attr = fid == EF_ALGO_SIG || fid == EF_ALGO_DEC || fid == EF_ALGO_AUT;
file_t *ef;
if (fid == EF_RESET_CODE) {
fid = EF_RC;
}
else if (fid == EF_ALGO_SIG || fid == EF_ALGO_DEC || fid == EF_ALGO_AUT) {
else if (is_algorithm_attr) {
fid |= 0x1000;
}
if (is_algorithm_attr && apdu.nc > 0 &&
(apdu.nc > OPENPGP_MAX_ALGORITHM_ATTR_SIZE ||
(apdu.data[0] == ALGO_RSA && apdu.nc < 3))) {
return SW_WRONG_DATA();
}
if (!(ef = file_search_by_fid(fid, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND();
}
@@ -40,15 +47,39 @@ int cmd_put_data(void) {
}
if (fid == EF_PW_STATUS) {
fid = EF_PW_PRIV;
apdu.nc = 4; //we silently ommit the reset parameters
if (apdu.nc == 0) {
return SW_WRONG_LENGTH();
}
if (!(ef = file_search_by_fid(fid, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND();
}
}
if (currentEF && currentEF->fid == fid) { // previously selected same EF
if (requested_fid == EF_CH_CERT) {
if (currentEF && currentEF->fid >= EF_CH_1 && currentEF->fid <= EF_CH_3) {
ef = currentEF;
}
else if (!(ef = file_search_by_fid(EF_CH_1, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND();
}
fid = ef->fid;
}
else if (currentEF && currentEF->fid == fid) { // previously selected same EF
ef = currentEF;
}
if (ef->type & FILE_DATA_FLASH) {
int r = 0;
if (apdu.nc > 0) {
if (fid == EF_RC) {
if (requested_fid == EF_PW_STATUS) {
uint8_t pw_status[7] = { 0x1, 127, 127, 127, 3, 3, 3 };
if (file_has_data(ef)) {
memset(pw_status, 0, sizeof(pw_status));
uint16_t status_len = MIN(file_get_size(ef), sizeof(pw_status));
memcpy(pw_status, file_get_data(ef), status_len);
}
memcpy(pw_status, apdu.data, MIN(apdu.nc, 4));
r = file_put_data(ef, pw_status, sizeof(pw_status));
}
else if (fid == EF_RC) {
has_rc = false;
if ((r = load_dek()) != PICOKEYS_OK) {
return SW_EXEC_ERROR();
@@ -59,7 +90,7 @@ int cmd_put_data(void) {
pin_derive_verifier(apdu.data, apdu.nc, dhash + 2);
file_put_data(ef, dhash, sizeof(dhash));
file_t *tf = file_search_by_fid(EF_DEK, NULL, SPECIFY_EF);
file_t *tf = file_search_by_fid(EF_DEK_RC, NULL, SPECIFY_EF);
if (!tf) {
return SW_REFERENCE_NOT_FOUND();
}
@@ -79,7 +110,10 @@ int cmd_put_data(void) {
flash_commit();
}
else {
file_delete(ef);
if (flash_clear_file(ef) != PICOKEYS_OK) {
return SW_MEMORY_FAILURE();
}
flash_commit();
}
}
return SW_OK();
+15 -4
View File
@@ -23,29 +23,40 @@ int cmd_select_data(void) {
if (P2(apdu) != 0x4) {
return SW_WRONG_P1P2();
}
if (apdu.nc < 5u) {
return SW_WRONG_LENGTH();
}
if (apdu.data[0] != 0x60) {
return SW_WRONG_DATA();
}
if (apdu.nc != (uint32_t) apdu.data[1] + 2u || apdu.nc < 5u) {
if (apdu.nc != (uint32_t) apdu.data[1] + 2u) {
return SW_WRONG_LENGTH();
}
if (apdu.data[2] != 0x5C) {
return SW_WRONG_DATA();
}
if (apdu.data[3] == 2) {
if (apdu.nc < 6u) {
return SW_WRONG_LENGTH();
}
fid = (apdu.data[4] << 8) | apdu.data[5];
}
else {
else if (apdu.data[3] == 1) {
fid = apdu.data[4];
}
else {
return SW_WRONG_DATA();
}
if (fid != EF_CH_CERT || P1(apdu) >= 3) {
return SW_REFERENCE_NOT_FOUND();
}
if (!(ef = file_search_by_fid(fid, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND();
}
if (!file_authenticate_action(ef, ACL_OP_UPDATE_ERASE)) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
fid &= ~0x6000; //Now get private DO
fid += P1(apdu);
fid = EF_CH_1 + P1(apdu);
if (!(ef = file_search_by_fid(fid, NULL, SPECIFY_EF))) {
return SW_REFERENCE_NOT_FOUND();
}
+68 -12
View File
@@ -32,6 +32,32 @@ int parse_algoinfo(const file_t *f, int mode);
int parse_app_data(const file_t *f, int mode);
int parse_discrete_do(const file_t *f, int mode);
static uint16_t response_remaining(void) {
return res_APDU_size < OPENPGP_MAX_RESPONSE_SIZE ?
OPENPGP_MAX_RESPONSE_SIZE - res_APDU_size : 0;
}
static uint8_t encoded_len_size(uint16_t len) {
if (len >= 256) {
return 3;
}
if (len >= 128) {
return 2;
}
return 1;
}
static uint16_t fit_tlv_value(uint16_t len, uint16_t available, uint8_t tag_size) {
if (available <= tag_size + 1) {
return 0;
}
uint16_t fitted = MIN(len, available - tag_size - 1);
while (fitted + tag_size + encoded_len_size(fitted) > available) {
fitted--;
}
return fitted;
}
int parse_do(uint16_t *fids, int mode) {
int len = 0;
file_t *ef;
@@ -39,14 +65,27 @@ int parse_do(uint16_t *fids, int mode) {
if ((ef = file_search_by_fid(fids[i + 1], NULL, SPECIFY_EF))) {
uint16_t data_len;
if ((ef->type & FILE_DATA_FUNC) == FILE_DATA_FUNC) {
if (mode == 1 && response_remaining() < 16) {
break;
}
int (*file_data_func)(const file_t *, int) = NULL;
memcpy(&file_data_func, &ef->data, sizeof(file_data_func));
uint16_t initial_size = res_APDU_size;
data_len = file_data_func(ef, mode);
if (mode == 1) {
data_len = res_APDU_size - initial_size;
}
}
else {
data_len = file_get_size(ef);
if (mode == 1) {
if (fids[0] > 1 && res_APDU_size > 0) {
uint8_t tag_size = fids[i + 1] < 0x0100 ? 1 : 2;
uint16_t available = response_remaining();
data_len = fit_tlv_value(data_len, available, tag_size);
if (available < tag_size + encoded_len_size(data_len)) {
break;
}
if (fids[i + 1] < 0x0100) {
res_APDU[res_APDU_size++] = fids[i + 1] & 0xff;
}
@@ -56,6 +95,9 @@ int parse_do(uint16_t *fids, int mode) {
}
res_APDU_size += tlv_format_len(data_len, res_APDU + res_APDU_size);
}
else {
data_len = MIN(data_len, response_remaining());
}
if (file_has_data(ef)) {
memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len);
}
@@ -69,19 +111,24 @@ int parse_do(uint16_t *fids, int mode) {
}
int parse_trium(uint16_t fid, uint8_t num, size_t size) {
uint16_t initial_size = res_APDU_size;
for (uint8_t i = 0; i < num; i++) {
uint16_t output_len = MIN(size, response_remaining());
if (output_len == 0) {
break;
}
file_t *ef;
if ((ef = file_search_by_fid(fid + i, NULL, SPECIFY_EF)) && ef->data) {
uint16_t data_len = file_get_size(ef);
uint16_t data_len = MIN(file_get_size(ef), output_len);
memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len);
res_APDU_size += data_len;
memset(res_APDU + res_APDU_size + data_len, 0, output_len - data_len);
}
else {
memset(res_APDU + res_APDU_size, 0, size);
res_APDU_size += size;
memset(res_APDU + res_APDU_size, 0, output_len);
}
res_APDU_size += output_len;
}
return num * size;
return res_APDU_size - initial_size;
}
int parse_ch_data(const file_t *f, int mode) {
@@ -106,13 +153,15 @@ int parse_sec_tpl(const file_t *f, int mode) {
(void) mode;
res_APDU[res_APDU_size++] = EF_SEC_TPL & 0xff;
res_APDU[res_APDU_size++] = 5;
res_APDU[res_APDU_size++] = EF_SIG_COUNT & 0xff;
res_APDU[res_APDU_size++] = 3;
memset(res_APDU + res_APDU_size, 0, 3);
file_t *ef = file_search_by_fid(EF_SIG_COUNT, NULL, SPECIFY_ANY);
if (ef && ef->data) {
res_APDU[res_APDU_size++] = EF_SIG_COUNT & 0xff;
res_APDU[res_APDU_size++] = 3;
memcpy(res_APDU + res_APDU_size, file_get_data(ef), 3);
res_APDU_size += 3;
uint16_t data_len = MIN(file_get_size(ef), 3);
memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len);
}
res_APDU_size += 3;
return 5 + 2;
}
@@ -193,10 +242,12 @@ int parse_pw_status(const file_t *f, int mode) {
res_APDU[res_APDU_size++] = 7;
}
ef = file_search_by_fid(EF_PW_PRIV, NULL, SPECIFY_ANY);
memset(res_APDU + res_APDU_size, 0, 7);
if (ef && ef->data) {
memcpy(res_APDU + res_APDU_size, file_get_data(ef), 7);
res_APDU_size += 7;
uint16_t data_len = MIN(file_get_size(ef), 7);
memcpy(res_APDU + res_APDU_size, file_get_data(ef), data_len);
}
res_APDU_size += 7;
return res_APDU_size - init_len;
}
@@ -373,8 +424,13 @@ int parse_algoinfo(const file_t *f, int mode) {
datalen += parse_algo(algorithm_attr_rsa2k, f->fid);
}
else {
uint16_t len = file_get_size(ef);
uint16_t len = MIN(file_get_size(ef), OPENPGP_MAX_ALGORITHM_ATTR_SIZE);
len = MIN(len, response_remaining());
if (res_APDU_size > 0) {
if (response_remaining() < 2) {
return datalen;
}
len = MIN(len, response_remaining() - 2);
res_APDU[res_APDU_size++] = f->fid & 0xff;
res_APDU[res_APDU_size++] = len & 0xff;
datalen += 2;
+4 -1
View File
@@ -252,8 +252,11 @@ file_t file_entries[] = {
/* 59 */ { .fid = EF_DEK_PW3, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE },
/* 60 */ { .fid = EF_AES_KEY, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_NONE },
// ** PIV ** //
/* 60 */ { .fid = EF_PIV_ADMIN_DATA, .parent = 0, .name = NULL,
/* 61 */ { .fid = EF_PIV_ADMIN_DATA, .parent = 0, .name = NULL,
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
.ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
/* 61 */ { .fid = EF_PIV_ATTESTATION, .parent = 0, .name = NULL,
+236 -49
View File
@@ -26,6 +26,8 @@
#include "random.h"
#include "eac.h"
#include "mbedtls/asn1.h"
#include "mbedtls/gcm.h"
#include "mbedtls/md.h"
#include "usb.h"
#include "ccid/ccid.h"
#include "otp.h"
@@ -44,6 +46,7 @@ uint8_t session_rc[32];
uint8_t session_pw3[32];
uint8_t dek[DEK_SIZE];
uint16_t algo_dec = EF_ALGO_PRIV2, algo_aut = EF_ALGO_PRIV3, pk_dec = EF_PK_DEC, pk_aut = EF_PK_AUT;
extern bool is_gpg;
uint8_t openpgp_aid[] = {
6,
@@ -272,7 +275,7 @@ static void release_dek(void) {
extern bool has_pwpiv;
extern uint8_t session_pwpiv[32];
int load_dek(void) {
if (!has_pw1 && !has_pw2 && !has_pw3 && !has_pwpiv) {
if (!has_pw1 && !has_pw2 && !has_pw3 && !has_rc && !has_pwpiv) {
return PICOKEYS_NO_LOGIN;
}
int r = PICOKEYS_OK;
@@ -298,6 +301,28 @@ int load_dek(void) {
r = aes_decrypt_cfb_256(session_pw1, dek, dek + IV_SIZE, 32);
}
}
else if (has_rc) {
file_t *ef_dek_rc = file_search(EF_DEK_RC);
if (file_has_data(ef_dek_rc)) {
uint8_t *ef_data = file_get_data(ef_dek_rc);
if (ef_data[0] == 0x3) { // Format
r = decrypt_with_aad(session_rc, ef_data + 1, DEK_AAD_SIZE, PIN_KDF_DEFAULT_VERSION, dek);
}
else {
return PICOKEYS_ERR_NULL_PARAM;
}
}
else {
file_t *tf = file_search_by_fid(EF_DEK, NULL, SPECIFY_EF);
if (!tf) {
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
memcpy(dek, file_get_data(tf), IV_SIZE);
memcpy(dek + IV_SIZE, file_get_data(tf) + IV_SIZE + 32, 32);
r = aes_decrypt_cfb_256(session_rc, dek, dek + IV_SIZE, 32);
}
}
else if (has_pw3) {
file_t *ef_dek_pw3 = file_search(EF_DEK_PW3);
if (file_has_data(ef_dek_pw3)) {
@@ -349,17 +374,7 @@ int load_dek(void) {
return PICOKEYS_OK;
}
int dek_encrypt(uint8_t *data, size_t len) {
int r;
if ((r = load_dek()) != PICOKEYS_OK) {
return r;
}
r = aes_encrypt_cfb_256(dek + IV_SIZE, dek, data, len);
release_dek();
return r;
}
int dek_decrypt(uint8_t *data, size_t len) {
static int dek_decrypt_legacy(uint8_t *data, size_t len) {
int r;
if ((r = load_dek()) != PICOKEYS_OK) {
return r;
@@ -369,6 +384,164 @@ int dek_decrypt(uint8_t *data, size_t len) {
return r;
}
#define ENCRYPTED_KEY_MAGIC_SIZE 4
#define ENCRYPTED_KEY_NONCE_SIZE 12
#define ENCRYPTED_KEY_TAG_SIZE 16
#define ENCRYPTED_KEY_OVERHEAD (ENCRYPTED_KEY_MAGIC_SIZE + ENCRYPTED_KEY_NONCE_SIZE + ENCRYPTED_KEY_TAG_SIZE)
static const uint8_t encrypted_key_magic[ENCRYPTED_KEY_MAGIC_SIZE] = { 'P', 'G', 'K', 1 };
static int derive_encrypted_key_nonce(uint16_t fid, const uint8_t *plaintext, size_t plaintext_len, uint8_t nonce[ENCRYPTED_KEY_NONCE_SIZE]) {
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (!md_info) {
return PICOKEYS_EXEC_ERROR;
}
mbedtls_md_context_t md;
mbedtls_md_init(&md);
uint8_t digest[32] = { 0 };
uint8_t fid_data[2] = { fid >> 8, fid & 0xff };
int r = mbedtls_md_setup(&md, md_info, 1);
if (r == 0) {
r = mbedtls_md_hmac_starts(&md, dek, sizeof(dek));
}
if (r == 0) {
r = mbedtls_md_hmac_update(&md, fid_data, sizeof(fid_data));
}
if (r == 0) {
r = mbedtls_md_hmac_update(&md, plaintext, plaintext_len);
}
if (r == 0) {
r = mbedtls_md_hmac_finish(&md, digest);
}
mbedtls_md_free(&md);
if (r == 0) {
memcpy(nonce, digest, ENCRYPTED_KEY_NONCE_SIZE);
}
mbedtls_platform_zeroize(digest, sizeof(digest));
return r == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR;
}
static void encrypted_key_aad(uint16_t fid, uint8_t aad[ENCRYPTED_KEY_MAGIC_SIZE + 2]) {
memcpy(aad, encrypted_key_magic, ENCRYPTED_KEY_MAGIC_SIZE);
aad[ENCRYPTED_KEY_MAGIC_SIZE] = fid >> 8;
aad[ENCRYPTED_KEY_MAGIC_SIZE + 1] = fid & 0xff;
}
static int store_encrypted_key(file_t *ef, const uint8_t *plaintext, size_t plaintext_len) {
if (!ef || !plaintext || plaintext_len > UINT16_MAX - ENCRYPTED_KEY_OVERHEAD) {
return PICOKEYS_WRONG_DATA;
}
int r = load_dek();
if (r != PICOKEYS_OK) {
return r;
}
size_t record_len = plaintext_len + ENCRYPTED_KEY_OVERHEAD;
uint8_t *record = calloc(1, record_len);
if (!record) {
release_dek();
return PICOKEYS_ERR_MEMORY_FATAL;
}
memcpy(record, encrypted_key_magic, ENCRYPTED_KEY_MAGIC_SIZE);
uint8_t *nonce = record + ENCRYPTED_KEY_MAGIC_SIZE;
uint8_t *ciphertext = nonce + ENCRYPTED_KEY_NONCE_SIZE;
uint8_t *tag = ciphertext + plaintext_len;
uint8_t aad[ENCRYPTED_KEY_MAGIC_SIZE + 2];
encrypted_key_aad(ef->fid, aad);
r = derive_encrypted_key_nonce(ef->fid, plaintext, plaintext_len, nonce);
mbedtls_gcm_context gcm;
mbedtls_gcm_init(&gcm);
if (r == PICOKEYS_OK) {
r = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, dek + IV_SIZE, 256);
}
if (r == 0) {
r = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, plaintext_len, nonce, ENCRYPTED_KEY_NONCE_SIZE, aad, sizeof(aad), plaintext, ciphertext, ENCRYPTED_KEY_TAG_SIZE, tag);
}
mbedtls_gcm_free(&gcm);
release_dek();
if (r != 0) {
mbedtls_platform_zeroize(record, record_len);
free(record);
return PICOKEYS_EXEC_ERROR;
}
r = file_put_data(ef, record, (uint16_t)record_len);
mbedtls_platform_zeroize(record, record_len);
free(record);
return r;
}
int load_key_data(file_t *fkey, uint8_t *out, size_t out_size, size_t *out_len, bool use_dek) {
if (!file_has_data(fkey) || !out || !out_len) {
return PICOKEYS_WRONG_DATA;
}
size_t stored_len = file_get_size(fkey);
const uint8_t *stored = file_get_data(fkey);
if (!use_dek) {
if (stored_len > out_size) {
return PICOKEYS_WRONG_DATA;
}
memcpy(out, stored, stored_len);
*out_len = stored_len;
return PICOKEYS_OK;
}
if (stored_len >= ENCRYPTED_KEY_MAGIC_SIZE &&
memcmp(stored, encrypted_key_magic, ENCRYPTED_KEY_MAGIC_SIZE) == 0) {
if (stored_len < ENCRYPTED_KEY_OVERHEAD) {
return PICOKEYS_WRONG_DATA;
}
size_t plaintext_len = stored_len - ENCRYPTED_KEY_OVERHEAD;
if (plaintext_len > out_size) {
return PICOKEYS_WRONG_DATA;
}
const uint8_t *nonce = stored + ENCRYPTED_KEY_MAGIC_SIZE;
const uint8_t *ciphertext = nonce + ENCRYPTED_KEY_NONCE_SIZE;
const uint8_t *tag = ciphertext + plaintext_len;
uint8_t aad[ENCRYPTED_KEY_MAGIC_SIZE + 2];
encrypted_key_aad(fkey->fid, aad);
int r = load_dek();
if (r != PICOKEYS_OK) {
return r;
}
mbedtls_gcm_context gcm;
mbedtls_gcm_init(&gcm);
r = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, dek + IV_SIZE, 256);
if (r == 0) {
r = mbedtls_gcm_auth_decrypt(&gcm, plaintext_len, nonce, ENCRYPTED_KEY_NONCE_SIZE, aad, sizeof(aad), tag, ENCRYPTED_KEY_TAG_SIZE, ciphertext, out);
}
mbedtls_gcm_free(&gcm);
release_dek();
if (r != 0) {
mbedtls_platform_zeroize(out, out_size);
return PICOKEYS_WRONG_DATA;
}
*out_len = plaintext_len;
return PICOKEYS_OK;
}
if (stored_len > out_size) {
return PICOKEYS_WRONG_DATA;
}
memcpy(out, stored, stored_len);
int r = dek_decrypt_legacy(out, stored_len);
if (r != 0) {
mbedtls_platform_zeroize(out, out_size);
return PICOKEYS_EXEC_ERROR;
}
r = store_encrypted_key(fkey, out, stored_len);
if (r != PICOKEYS_OK) {
mbedtls_platform_zeroize(out, out_size);
return r;
}
flash_commit();
*out_len = stored_len;
return PICOKEYS_OK;
}
static void init_openpgp(void) {
isUserAuthenticated = false;
has_pw1 = has_pw2 = has_pw3 = false;
@@ -406,6 +579,7 @@ static int openpgp_select_aid(app_t *a, uint8_t force) {
(void) force;
a->process_apdu = openpgp_process_apdu;
a->unload = openpgp_unload;
is_gpg = true;
init_openpgp();
file_process_fci(file_openpgp, 1);
memcpy(res_APDU + res_APDU_size, "\x64\x06\x53\x04", 4);
@@ -674,16 +848,8 @@ int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek) {
}
memcpy(kdata, key_ctx, key_size);
}
if (use_kek) {
r = dek_encrypt(kdata, key_size);
if (r != PICOKEYS_OK) {
return r;
}
}
//r = aes_encrypt_cfb_256(file_read(pw3->data+2), session_pw3, kdata, key_size);
//if (r != PICOKEYS_OK)
// return r;
r = file_put_data(ef, kdata, key_size);
r = use_kek ? store_encrypted_key(ef, kdata, key_size) : file_put_data(ef, kdata, key_size);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
if (r != PICOKEYS_OK) {
return r;
}
@@ -692,54 +858,72 @@ int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek) {
}
int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey, bool use_dek) {
int key_size = file_get_size(fkey);
size_t key_size = 0;
uint8_t kdata[4096 / 8];
memcpy(kdata, file_get_data(fkey), key_size);
if (use_dek && dek_decrypt(kdata, key_size) != 0) {
return PICOKEYS_EXEC_ERROR;
int r = load_key_data(fkey, kdata, sizeof(kdata), &key_size, use_dek);
if (r != PICOKEYS_OK || key_size == 0 || key_size % 2 != 0) {
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return r == PICOKEYS_OK ? PICOKEYS_WRONG_DATA : r;
}
if (mbedtls_mpi_read_binary(&ctx->P, kdata, key_size / 2) != 0) {
mbedtls_rsa_free(ctx);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return PICOKEYS_WRONG_DATA;
}
if (mbedtls_mpi_read_binary(&ctx->Q, kdata + key_size / 2, key_size / 2) != 0) {
mbedtls_rsa_free(ctx);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return PICOKEYS_WRONG_DATA;
}
if (mbedtls_mpi_lset(&ctx->E, 0x10001) != 0) {
mbedtls_rsa_free(ctx);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return PICOKEYS_EXEC_ERROR;
}
if (mbedtls_rsa_import(ctx, NULL, &ctx->P, &ctx->Q, NULL, &ctx->E) != 0) {
mbedtls_rsa_free(ctx);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return PICOKEYS_WRONG_DATA;
}
if (mbedtls_rsa_complete(ctx) != 0) {
mbedtls_rsa_free(ctx);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return PICOKEYS_WRONG_DATA;
}
if (mbedtls_rsa_check_privkey(ctx) != 0) {
mbedtls_rsa_free(ctx);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return PICOKEYS_WRONG_DATA;
}
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return PICOKEYS_OK;
}
int load_private_key_ecdsa(mbedtls_ecp_keypair *ctx, file_t *fkey, bool use_dek) {
int key_size = file_get_size(fkey);
size_t key_size = 0;
uint8_t kdata[67]; //Worst case, 521 bit + 1byte
memcpy(kdata, file_get_data(fkey), key_size);
if (use_dek && dek_decrypt(kdata, key_size) != 0) {
return PICOKEYS_EXEC_ERROR;
int r = load_key_data(fkey, kdata, sizeof(kdata), &key_size, use_dek);
if (r != PICOKEYS_OK || key_size < 2) {
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return r == PICOKEYS_OK ? PICOKEYS_WRONG_DATA : r;
}
mbedtls_ecp_group_id gid = kdata[0];
int r = mbedtls_ecp_read_key(gid, ctx, kdata + 1, key_size - 1);
r = mbedtls_ecp_read_key(gid, ctx, kdata + 1, key_size - 1);
if (r != 0) {
mbedtls_ecp_keypair_free(ctx);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
return PICOKEYS_EXEC_ERROR;
}
mbedtls_platform_zeroize(kdata, sizeof(kdata));
r = mbedtls_ecp_keypair_calc_public(ctx, random_fill_iterator, NULL);
#if defined(MBEDTLS_ECP_EDWARDS_ENABLED)
if (mbedtls_ecp_get_type(&ctx->grp) == MBEDTLS_ECP_TYPE_EDWARDS) {
r = mbedtls_ecp_point_edwards(&ctx->grp, &ctx->Q, &ctx->d, random_fill_iterator, NULL);
}
else
#endif
{
r = mbedtls_ecp_keypair_calc_public(ctx, random_fill_iterator, NULL);
}
if (r != 0) {
mbedtls_ecp_keypair_free(ctx);
return PICOKEYS_EXEC_ERROR;
@@ -747,51 +931,54 @@ int load_private_key_ecdsa(mbedtls_ecp_keypair *ctx, file_t *fkey, bool use_dek)
return PICOKEYS_OK;
}
int load_aes_key(uint8_t *aes_key, file_t *fkey) {
int key_size = file_get_size(fkey);
memcpy(aes_key, file_get_data(fkey), key_size);
if (dek_decrypt(aes_key, key_size) != 0) {
return PICOKEYS_EXEC_ERROR;
int load_aes_key(uint8_t *aes_key, size_t *key_size, file_t *fkey) {
int r = load_key_data(fkey, aes_key, 32, key_size, true);
if (r != PICOKEYS_OK || (*key_size != 16 && *key_size != 24 && *key_size != 32)) {
mbedtls_platform_zeroize(aes_key, 32);
return r == PICOKEYS_OK ? PICOKEYS_WRONG_DATA : r;
}
return PICOKEYS_OK;
return r;
}
mbedtls_ecp_group_id get_ec_group_id_from_attr(const uint8_t *algo, size_t algo_len) {
if (memcmp(algorithm_attr_p256k1 + 2, algo, algo_len) == 0) {
#define ALGORITHM_ATTR_MATCH(attr) \
(algo_len == (size_t)((attr)[0] - 1u) && memcmp((attr) + 2, algo, algo_len) == 0)
if (ALGORITHM_ATTR_MATCH(algorithm_attr_p256k1)) {
return MBEDTLS_ECP_DP_SECP256K1;
}
else if (memcmp(algorithm_attr_p256r1 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_p256r1)) {
return MBEDTLS_ECP_DP_SECP256R1;
}
else if (memcmp(algorithm_attr_p384r1 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_p384r1)) {
return MBEDTLS_ECP_DP_SECP384R1;
}
else if (memcmp(algorithm_attr_p521r1 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_p521r1)) {
return MBEDTLS_ECP_DP_SECP521R1;
}
else if (memcmp(algorithm_attr_bp256r1 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_bp256r1)) {
return MBEDTLS_ECP_DP_BP256R1;
}
else if (memcmp(algorithm_attr_bp384r1 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_bp384r1)) {
return MBEDTLS_ECP_DP_BP384R1;
}
else if (memcmp(algorithm_attr_bp512r1 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_bp512r1)) {
return MBEDTLS_ECP_DP_BP512R1;
}
else if (memcmp(algorithm_attr_cv25519 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_cv25519)) {
return MBEDTLS_ECP_DP_CURVE25519;
}
else if (memcmp(algorithm_attr_x448 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_x448)) {
return MBEDTLS_ECP_DP_CURVE448;
}
#ifdef MBEDTLS_EDDSA_C
else if (memcmp(algorithm_attr_ed25519 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_ed25519)) {
return MBEDTLS_ECP_DP_ED25519;
}
else if (memcmp(algorithm_attr_ed448 + 2, algo, algo_len) == 0) {
else if (ALGORITHM_ATTR_MATCH(algorithm_attr_ed448)) {
return MBEDTLS_ECP_DP_ED448;
}
#endif
#undef ALGORITHM_ATTR_MATCH
return MBEDTLS_ECP_DP_NONE;
}
+8 -14
View File
@@ -42,16 +42,8 @@ extern uint8_t dek[IV_SIZE + 32];
extern int store_keys(void *key_ctx, int type, uint16_t key_id, bool use_kek);
extern void make_rsa_response(mbedtls_rsa_context *rsa);
extern void make_ecdsa_response(mbedtls_ecdsa_context *ecdsa);
extern int ecdsa_sign(mbedtls_ecdsa_context *ctx,
const uint8_t *data,
size_t data_len,
uint8_t *out,
size_t *out_len);
extern int rsa_sign(mbedtls_rsa_context *ctx,
const uint8_t *data,
size_t data_len,
uint8_t *out,
size_t *out_len);
extern int ecdsa_sign(mbedtls_ecdsa_context *ctx, const uint8_t *data, size_t data_len, uint8_t *out, size_t *out_len);
extern int rsa_sign(mbedtls_rsa_context *ctx, const uint8_t *data, size_t data_len, uint8_t *out, size_t *out_len);
extern int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey, bool use_dek);
extern int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey, bool use_dek);
extern int pin_reset_retries(const file_t *pin, bool force);
@@ -74,11 +66,9 @@ extern int reset_sig_count(void);
extern uint16_t algo_dec, algo_aut, pk_dec, pk_aut;
extern bool wait_button_pressed_fid(uint16_t fid);
extern void scan_files_openpgp(void);
extern int load_aes_key(uint8_t *aes_key, file_t *fkey);
extern int load_aes_key(uint8_t *aes_key, size_t *key_size, file_t *fkey);
extern int load_key_data(file_t *fkey, uint8_t *out, size_t out_size, size_t *out_len, bool use_dek);
extern int inc_sig_count(void);
extern int dek_encrypt(uint8_t *data, size_t len);
extern int dek_decrypt(uint8_t *data, size_t len);
int cmd_select(void);
int cmd_get_data(void);
int cmd_get_next_data(void);
@@ -104,4 +94,8 @@ int cmd_get_bulk_data(void);
#define DEK_FILE_SIZE_OLD (IV_SIZE + 32 + 32 + 32 + 32)
#define OPENPGP_MAX_ALGORITHM_ATTR_SIZE 16
#define OPENPGP_MAX_OBJECT_SIZE 2048
#define OPENPGP_MAX_RESPONSE_SIZE 2048
#endif