Migrate PIN and MKEK to new system.

This new system is more robust, with derived keys by context and safe in case of flash/ram dumps.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos 2026-03-20 01:19:35 +01:00
parent 1f96fe619b
commit 75c56bb2c7
No known key found for this signature in database
GPG key ID: C0095B7870A4CCD3
7 changed files with 136 additions and 89 deletions

@ -1 +1 @@
Subproject commit 5e9ae65046bf718295c87ee8a969354a1840effa
Subproject commit 89a8042634f88a390bf16adb95418dd9e4511516

View file

@ -48,11 +48,11 @@ int cmd_change_pin(void) {
//encrypt MKEK with new pin
if (P2(apdu) == 0x81) {
hash_multi(apdu.data + pin_len, (uint16_t)(apdu.nc - pin_len), session_pin);
pin_derive_session(apdu.data + pin_len, (uint16_t)(apdu.nc - pin_len), session_pin);
has_session_pin = true;
}
else if (P2(apdu) == 0x88) {
hash_multi(apdu.data + pin_len, (uint16_t)(apdu.nc - pin_len), session_sopin);
pin_derive_session(apdu.data + pin_len, (uint16_t)(apdu.nc - pin_len), session_sopin);
has_session_sopin = true;
}
r = store_mkek(mkek);
@ -60,9 +60,10 @@ int cmd_change_pin(void) {
if (r != PICOKEY_OK) {
return SW_EXEC_ERROR();
}
uint8_t dhash[33];
uint8_t dhash[34];
dhash[0] = (uint8_t)apdu.nc - pin_len;
double_hash_pin(apdu.data + pin_len, (uint16_t)(apdu.nc - pin_len), dhash + 1);
dhash[1] = 1; // Format
pin_derive_verifier(apdu.data + pin_len, (uint16_t)(apdu.nc - pin_len), dhash + 2);
file_put_data(file_pin, dhash, sizeof(dhash));
low_flash_available();
return SW_OK();

View file

@ -60,21 +60,23 @@ int cmd_initialize(void) {
}
else if (tag == 0x81) { //user pin
if (file_pin1 && file_pin1->data) {
uint8_t dhash[33];
dhash[0] = (uint8_t)tag_len;
double_hash_pin(tag_data, tag_len, dhash + 1);
file_put_data(file_pin1, dhash, sizeof(dhash));
hash_multi(tag_data, tag_len, session_pin);
uint8_t pin_data[34];
pin_data[0] = (uint8_t)tag_len;
pin_data[1] = 1; // Format
pin_derive_verifier(tag_data, tag_len, pin_data + 2);
file_put_data(file_pin1, pin_data, sizeof(pin_data));
pin_derive_session(tag_data, tag_len, session_pin);
has_session_pin = true;
}
}
else if (tag == 0x82) { //sopin pin
if (file_sopin && file_sopin->data) {
uint8_t dhash[33];
dhash[0] = (uint8_t)tag_len;
double_hash_pin(tag_data, tag_len, dhash + 1);
file_put_data(file_sopin, dhash, sizeof(dhash));
hash_multi(tag_data, tag_len, session_sopin);
uint8_t pin_data[34];
pin_data[0] = (uint8_t)tag_len;
pin_data[1] = 1; // Format
pin_derive_verifier(tag_data, tag_len, pin_data + 2);
file_put_data(file_sopin, pin_data, sizeof(pin_data));
pin_derive_session(tag_data, tag_len, session_sopin);
has_session_sopin = true;
}
}

View file

@ -55,10 +55,6 @@ int cmd_reset_retry(void) {
}
newpin_len = (uint8_t)apdu.nc;
}
uint8_t dhash[33];
dhash[0] = newpin_len;
double_hash_pin(apdu.data + (apdu.nc - newpin_len), newpin_len, dhash + 1);
file_put_data(file_pin1, dhash, sizeof(dhash));
if (pin_reset_retries(file_pin1, true) != PICOKEY_OK) {
return SW_MEMORY_FAILURE();
}
@ -67,13 +63,18 @@ int cmd_reset_retry(void) {
if (r != PICOKEY_OK) {
return SW_EXEC_ERROR();
}
hash_multi(apdu.data + (apdu.nc - newpin_len), newpin_len, session_pin);
pin_derive_session(apdu.data + (apdu.nc - newpin_len), newpin_len, session_pin);
has_session_pin = true;
r = store_mkek(mkek);
release_mkek(mkek);
if (r != PICOKEY_OK) {
return SW_EXEC_ERROR();
}
uint8_t dhash[34];
dhash[0] = newpin_len;
dhash[1] = 1; // Format
pin_derive_verifier(apdu.data + (apdu.nc - newpin_len), newpin_len, dhash + 2);
file_put_data(file_pin1, dhash, sizeof(dhash));
low_flash_available();
return SW_OK();
}

View file

@ -47,18 +47,19 @@ int load_mkek(uint8_t *mkek) {
if (has_session_pin == false && has_session_sopin == false) {
return PICOKEY_NO_LOGIN;
}
file_t *ef = NULL;
const uint8_t *pin = NULL;
if (pin == NULL && has_session_pin == true) {
file_t *tf = search_file(EF_MKEK);
if (file_has_data(tf)) {
memcpy(mkek, file_get_data(tf), MKEK_SIZE);
ef = tf;
pin = session_pin;
}
}
if (pin == NULL && has_session_sopin == true) {
file_t *tf = search_file(EF_MKEK_SO);
if (file_has_data(tf)) {
memcpy(mkek, file_get_data(tf), MKEK_SIZE);
ef = tf;
pin = session_sopin;
}
}
@ -66,21 +67,42 @@ int load_mkek(uint8_t *mkek) {
return PICOKEY_EXEC_ERROR;
}
if (has_mkek_mask) {
mkek_masked(mkek, mkek_mask);
uint16_t fid_size = file_get_size(ef);
if (fid_size == MKEK_SIZE_OLD) {
memcpy(mkek, file_get_data(ef), MKEK_SIZE_OLD);
if (has_mkek_mask) {
mkek_masked(mkek, mkek_mask);
}
int ret = aes_decrypt_cfb_256(pin, MKEK_IV(mkek), MKEK_KEY(mkek), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
if (ret != 0) {
return PICOKEY_EXEC_ERROR;
}
uint32_t mkek_checksum = 0;
memcpy(&mkek_checksum, MKEK_CHECKSUM(mkek), sizeof(mkek_checksum));
if (crc32c(MKEK_KEY(mkek), MKEK_KEY_SIZE) != mkek_checksum) {
return PICOKEY_WRONG_DKEK;
}
if (otp_key_1) {
mkek_masked(mkek, otp_key_1);
}
}
int ret = aes_decrypt_cfb_256(pin, MKEK_IV(mkek), MKEK_KEY(mkek), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
if (ret != 0) {
return PICOKEY_EXEC_ERROR;
else if (fid_size == MKEK_FILE_SIZE) {
uint8_t format = *file_get_data(ef);
if (format == 0x03) { // Format indicator
uint8_t tmp_key[MKEK_FILE_SIZE];
memcpy(tmp_key, file_get_data(ef), sizeof(tmp_key));
int ret = decrypt_with_aad(pin, tmp_key + 1, MKEK_FILE_SIZE - 1, 2, mkek);
mbedtls_platform_zeroize(tmp_key, sizeof(tmp_key));
if (ret != PICOKEY_OK) {
return PICOKEY_EXEC_ERROR;
}
}
else {
return PICOKEY_EXEC_ERROR;
}
}
uint32_t mkek_checksum = 0;
memcpy(&mkek_checksum, MKEK_CHECKSUM(mkek), sizeof(mkek_checksum));
if (crc32c(MKEK_KEY(mkek), MKEK_KEY_SIZE) != mkek_checksum) {
return PICOKEY_WRONG_DKEK;
}
if (otp_key_1) {
mkek_masked(mkek, otp_key_1);
else {
return PICOKEY_ERR_FILE_NOT_FOUND;
}
return PICOKEY_OK;
}
@ -113,47 +135,30 @@ int store_mkek(const uint8_t *mkek) {
if (has_session_pin == false && has_session_sopin == false) {
return PICOKEY_NO_LOGIN;
}
uint8_t tmp_mkek[MKEK_SIZE];
uint8_t tmp_mkek[MKEK_FILE_SIZE];
tmp_mkek[0] = 0x03; // Format indicator
if (mkek == NULL) {
const uint8_t *rd = random_bytes_get(MKEK_IV_SIZE + MKEK_KEY_SIZE);
memcpy(tmp_mkek, rd, MKEK_IV_SIZE + MKEK_KEY_SIZE);
mkek = random_bytes_get(MKEK_SIZE);
}
else {
memcpy(tmp_mkek, mkek, MKEK_SIZE);
}
if (otp_key_1) {
mkek_masked(tmp_mkek, otp_key_1);
}
uint32_t mkek_checksum = crc32c(MKEK_KEY(tmp_mkek), MKEK_KEY_SIZE);
memcpy(MKEK_CHECKSUM(tmp_mkek), &mkek_checksum, sizeof(mkek_checksum));
if (has_session_pin) {
uint8_t tmp_mkek_pin[MKEK_SIZE];
memcpy(tmp_mkek_pin, tmp_mkek, MKEK_SIZE);
file_t *tf = search_file(EF_MKEK);
if (!tf) {
release_mkek(tmp_mkek);
release_mkek(tmp_mkek_pin);
file_t *ef = search_file(EF_MKEK);
if (!ef) {
return PICOKEY_ERR_FILE_NOT_FOUND;
}
aes_encrypt_cfb_256(session_pin, MKEK_IV(tmp_mkek_pin), MKEK_KEY(tmp_mkek_pin), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
file_put_data(tf, tmp_mkek_pin, MKEK_SIZE);
release_mkek(tmp_mkek_pin);
encrypt_with_aad(session_pin, mkek, MKEK_SIZE, 2, tmp_mkek + 1);
file_put_data(ef, tmp_mkek, sizeof(tmp_mkek));
}
if (has_session_sopin) {
uint8_t tmp_mkek_sopin[MKEK_SIZE];
memcpy(tmp_mkek_sopin, tmp_mkek, MKEK_SIZE);
file_t *tf = search_file(EF_MKEK_SO);
if (!tf) {
release_mkek(tmp_mkek);
release_mkek(tmp_mkek_sopin);
file_t *ef = search_file(EF_MKEK_SO);
if (!ef) {
return PICOKEY_ERR_FILE_NOT_FOUND;
}
aes_encrypt_cfb_256(session_sopin, MKEK_IV(tmp_mkek_sopin), MKEK_KEY(tmp_mkek_sopin), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
file_put_data(tf, tmp_mkek_sopin, MKEK_SIZE);
release_mkek(tmp_mkek_sopin);
encrypt_with_aad(session_sopin, mkek, MKEK_SIZE, 2, tmp_mkek + 1);
file_put_data(ef, tmp_mkek, sizeof(tmp_mkek));
}
low_flash_available();
release_mkek(tmp_mkek);
mbedtls_platform_zeroize(tmp_mkek, sizeof(tmp_mkek));
return PICOKEY_OK;
}
@ -246,7 +251,7 @@ static int dkek_kmac(uint8_t id, uint8_t *kmac) { //kmac 32 bytes
int mkek_encrypt(uint8_t *data, uint16_t len) {
int r;
uint8_t mkek[MKEK_SIZE + 4];
uint8_t mkek[MKEK_SIZE];
if ((r = load_mkek(mkek)) != PICOKEY_OK) {
return r;
}
@ -257,7 +262,7 @@ int mkek_encrypt(uint8_t *data, uint16_t len) {
int mkek_decrypt(uint8_t *data, uint16_t len) {
int r;
uint8_t mkek[MKEK_SIZE + 4];
uint8_t mkek[MKEK_SIZE];
if ((r = load_mkek(mkek)) != PICOKEY_OK) {
return r;
}

View file

@ -34,21 +34,9 @@ extern int import_dkek_share(uint8_t, const uint8_t *share);
extern int dkek_kcv(uint8_t, uint8_t *kcv);
extern int mkek_encrypt(uint8_t *data, uint16_t len);
extern int mkek_decrypt(uint8_t *data, uint16_t len);
extern int dkek_encode_key(uint8_t,
void *key_ctx,
int key_type,
uint8_t *out,
uint16_t *out_len,
const uint8_t *,
uint16_t);
extern int dkek_encode_key(uint8_t, void *key_ctx, int key_type, uint8_t *out, uint16_t *out_len, const uint8_t *, uint16_t);
extern int dkek_type_key(const uint8_t *in);
extern int dkek_decode_key(uint8_t,
void *key_ctx,
const uint8_t *in,
uint16_t in_len,
int *key_size_out,
uint8_t **,
uint16_t *);
extern int dkek_decode_key(uint8_t, void *key_ctx, const uint8_t *in, uint16_t in_len, int *key_size_out, uint8_t **, uint16_t *);
#define MAX_DKEK_ENCODE_KEY_BUFFER (8 + 1 + 12 + 6 + (8 + 2 * 4 + 2 * 4096 / 8 + 3 + 13) + 16)
@ -57,12 +45,15 @@ extern int dkek_decode_key(uint8_t,
#define MKEK_IV_SIZE (IV_SIZE)
#define MKEK_KEY_SIZE (32)
#define MKEK_KEY_CS_SIZE (4)
#define MKEK_SIZE (MKEK_IV_SIZE + MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE)
#define MKEK_SIZE_OLD (MKEK_IV_SIZE + MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE)
#define MKEK_IV(p) (p)
#define MKEK_KEY(p) (MKEK_IV(p) + MKEK_IV_SIZE)
#define MKEK_CHECKSUM(p) (MKEK_KEY(p) + MKEK_KEY_SIZE)
#define DKEK_KEY_SIZE (32)
#define MKEK_SIZE (MKEK_IV_SIZE + MKEK_KEY_SIZE)
#define MKEK_FILE_SIZE (1 + (12 + MKEK_SIZE + 16))
extern uint8_t mkek_mask[MKEK_KEY_SIZE];
extern bool has_mkek_mask;

View file

@ -209,8 +209,10 @@ void reset_puk_store(void) {
uint16_t fterm_data_len = file_get_size(fterm);
asn1_ctx_t ctxi;
asn1_ctx_init(fterm_data, fterm_data_len, &ctxi);
DEBUG_DATA(fterm_data,fterm_data_len);
while (walk_tlv(&ctxi, &p, NULL, NULL, NULL)) {
add_cert_puk_store(pq, (uint16_t)(p - pq), false);
DEBUG_PAYLOAD(pq, (p - pq));
pq = p;
}
}
@ -362,12 +364,18 @@ uint16_t check_pin(const file_t *pin, const uint8_t *data, uint16_t len) {
isUserAuthenticated = false;
}
has_session_pin = has_session_sopin = false;
uint8_t dhash[32];
double_hash_pin(data, len, dhash);
if (sizeof(dhash) != file_get_size(pin) - 1) { // 1 byte for pin len
return SW_CONDITIONS_NOT_SATISFIED();
uint8_t dhash[32], off = 2;
if (sizeof(dhash) == file_get_size(pin) - 1) { // Old style
off = 1;
double_hash_pin(data, len, dhash);
}
if (memcmp(file_get_data(pin) + 1, dhash, sizeof(dhash)) != 0) {
else if (sizeof(dhash) == file_get_size(pin) - 2) {
pin_derive_verifier(data, len, dhash);
}
else {
return SW_WRONG_DATA();
}
if (memcmp(file_get_data(pin) + off, dhash, sizeof(dhash)) != 0) {
int retries;
if ((retries = pin_wrong_retry(pin)) < PICOKEY_OK) {
return SW_PIN_BLOCKED();
@ -381,15 +389,54 @@ uint16_t check_pin(const file_t *pin, const uint8_t *data, uint16_t len) {
if (r != PICOKEY_OK) {
return SW_MEMORY_FAILURE();
}
if (off == 1) { // Upgrade PIN format
if (r != PICOKEY_OK) {
return SW_MEMORY_FAILURE();
}
if (pin == file_pin1) {
hash_multi(data, len, session_pin);
has_session_pin = true;
}
else if (pin == file_sopin) {
hash_multi(data, len, session_sopin);
has_session_sopin = true;
}
uint8_t mkek[MKEK_SIZE_OLD]; // Old MKEK size, as it is encrypted with old PIN format
r = load_mkek(mkek); //loads the MKEK with old format
if (r != PICOKEY_OK) {
return SW_MEMORY_FAILURE();
}
if (pin == file_pin1) {
pin_derive_session(data, len, session_pin);
}
else if (pin == file_sopin) {
pin_derive_session(data, len, session_sopin);
}
r = store_mkek(mkek); //stores the MKEK with new format
mbedtls_platform_zeroize(mkek, sizeof(mkek));
if (r != PICOKEY_OK) {
return SW_MEMORY_FAILURE();
}
uint8_t pin_data[34];
pin_data[0] = len;
pin_data[1] = 1; // new format indicator
pin_derive_verifier(data, len, pin_data + 2);
r = file_put_data((file_t *) pin, pin_data, sizeof(pin_data));
if (r != PICOKEY_OK) {
return SW_MEMORY_FAILURE();
}
low_flash_available();
}
if (pka_enabled() == false) {
isUserAuthenticated = true;
}
if (pin == file_pin1) {
hash_multi(data, len, session_pin);
pin_derive_session(data, len, session_pin);
has_session_pin = true;
}
else if (pin == file_sopin) {
hash_multi(data, len, session_sopin);
pin_derive_session(data, len, session_sopin);
has_session_sopin = true;
}
if (pending_save_dkek != 0xff) {