From 53306abd5e1a5fd5d76212878e200cb5bd864b68 Mon Sep 17 00:00:00 2001 From: "Aaron Tulino (Aaronjamt)" Date: Mon, 22 Dec 2025 15:55:37 -0700 Subject: [PATCH] Reduce array usages in Seos simulation --- armsrc/seos.c | 161 ++++++++++++++++++++++---------------------------- 1 file changed, 72 insertions(+), 89 deletions(-) diff --git a/armsrc/seos.c b/armsrc/seos.c index 1e37605b5..e19d2dfc3 100644 --- a/armsrc/seos.c +++ b/armsrc/seos.c @@ -63,97 +63,66 @@ static uint8_t round_to_next(uint8_t value, uint8_t step) { } } -static bool generate_cryptogram(const uint8_t *key, const uint8_t *opt_iv, const uint8_t *input, size_t length, uint8_t *output, uint8_t algorithm) { - // IV is optional, only add if provided. Zeros by default. - uint8_t iv[16] = {0x00}; - if (opt_iv != NULL) { - memcpy(iv, opt_iv, 16); - } - - uint8_t bs = block_size(algorithm); - uint8_t padded_length = round_to_next(length, bs); - - // Allocate enough room to store any additional padding - uint8_t cleartext[padded_length]; - uint8_t cryptogram[padded_length]; - - memset(cleartext, 0, padded_length); - memcpy(cleartext, input, length); - // ISO7816 padding: add 0x80 after data, then follow with all zeros - if (padded_length != length) { - cleartext[length] = 0x80; +static uint8_t cryptogram_iv[16] = {0x00}; +static bool generate_cryptogram(const uint8_t *key, bool use_iv, const uint8_t *input, size_t length, uint8_t *output, uint8_t algorithm) { + if (!use_iv) { + memset(cryptogram_iv, 0x00, 16); } if (algorithm == SEOS_ENCRYPTION_AES) { mbedtls_aes_context ctx; mbedtls_aes_setkey_enc(&ctx, key, 128); - mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, length, iv, input, cryptogram); + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, length, cryptogram_iv, input, output); mbedtls_aes_free(&ctx); } else if (algorithm == SEOS_ENCRYPTION_2K3DES) { mbedtls_des3_context ctx; mbedtls_des3_set2key_enc(&ctx, key); - mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, length, iv, input, cryptogram); + mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, length, cryptogram_iv, input, output); mbedtls_des3_free(&ctx); } - // Add generated cryptogram to output buffer - memcpy(output, cryptogram, length); - return true; } static bool decrypt_cryptogram(const uint8_t *key, const uint8_t *input, size_t length, uint8_t *output, uint8_t algorithm) { - uint8_t iv[16] = {0x00}; - - // Allocate enough room to store any additional padding - uint8_t cleartext[length + block_size(algorithm)]; + memset(cryptogram_iv, 0x00, 16); if (algorithm == SEOS_ENCRYPTION_AES) { mbedtls_aes_context ctx; mbedtls_aes_init(&ctx); mbedtls_aes_setkey_dec(&ctx, key, 128); - mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, length, iv, input, cleartext); + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, length, cryptogram_iv, input, output); mbedtls_aes_free(&ctx); } else if (algorithm == SEOS_ENCRYPTION_2K3DES) { mbedtls_des3_context ctx; mbedtls_des3_set2key_dec(&ctx, key); - mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, length, iv, input, cleartext); + mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, length, cryptogram_iv, input, output); mbedtls_des3_free(&ctx); } else { Dbprintf(_RED_("Unknown Encryption Algorithm")); return false; } - // Add decrypted cleartext to output buffer - memcpy(output, cleartext, length); - return true; } // Returns length of generated CMAC -static uint8_t generate_cmac(const uint8_t *key, const uint8_t *input, size_t length, uint8_t *output, uint8_t max_output_len, uint8_t encryption_algorithm) { - uint8_t size = block_size(encryption_algorithm); - uint8_t mac[size]; - +static bool generate_cmac(const uint8_t *key, const uint8_t *input, size_t length, uint8_t *output, uint8_t encryption_algorithm) { if (encryption_algorithm == SEOS_ENCRYPTION_AES) { - ulaes_cmac(key, 16, input, length, mac); + ulaes_cmac(key, 16, input, length, output); } else if (encryption_algorithm == SEOS_ENCRYPTION_2K3DES || encryption_algorithm == SEOS_ENCRYPTION_3K3DES) { uint8_t keylen = 16; if (encryption_algorithm == SEOS_ENCRYPTION_3K3DES) keylen = 24; - des3_cmac(key, keylen, input, length, mac); + des3_cmac(key, keylen, input, length, output); } else { Dbprintf(_RED_("Unknown Encryption Algorithm")); return false; } - // Add generated CMAC to output buffer - size = MIN(size, max_output_len); - memcpy(output, mac, size); - - return size; + return true; } -static void seos_kdf(bool forEncryption, uint8_t *masterKey, uint8_t keyslot, +static void seos_kdf(bool forEncryption, uint8_t *masterKey, uint8_t keyslot, uint8_t *work_buffer, uint8_t *adfOid, size_t adfoid_len, uint8_t *diversifier, uint8_t diversifier_len, uint8_t *out, int encryption_algorithm, int hash_algorithm) { // Encryption key = 04 @@ -166,28 +135,18 @@ static void seos_kdf(bool forEncryption, uint8_t *masterKey, uint8_t keyslot, typeOfKey = 0x04; } - uint8_t inputPre[] = { - // Padding - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, typeOfKey, 0x00, 0x00, 0x80, 0x01, - encryption_algorithm, hash_algorithm, keyslot - }; - - // 00000000000000000000000600008001 09 07 00 06112B0601040181E438010102011801010202 EFB08A28B0529F - // 00000000000000000000000400008001 09 07 00 06112B0601040181E438010102011801010202 EFB08A28B0529F - // 06112B0601040181E438010102011801010202 CF 07 EFB08A28B0529F DBA240413B0969B7111F4B6133A3DEFAD934B6DC - - - uint8_t input[sizeof(inputPre) + adfoid_len + diversifier_len]; - - memset(input, 0, sizeof(input)); - - memcpy(input, inputPre, sizeof(inputPre)); - memcpy(input + sizeof(inputPre), adfOid, adfoid_len); - memcpy(input + sizeof(inputPre) + adfoid_len, diversifier, diversifier_len); + memset(work_buffer, 0x00, 16); + work_buffer[11] = typeOfKey; + work_buffer[14] = 0x80; + work_buffer[15] = 0x01; + work_buffer[16] = encryption_algorithm; + work_buffer[17] = hash_algorithm; + work_buffer[18] = keyslot; + memcpy(work_buffer+19, adfOid, adfoid_len); + memcpy(work_buffer+19+adfoid_len, diversifier, diversifier_len); // This CMAC always uses AES, regardless of the main encryption algorithm in use. - generate_cmac(masterKey, input, sizeof(input), out, 16, SEOS_ENCRYPTION_AES); + generate_cmac(masterKey, work_buffer, 19 + adfoid_len + diversifier_len, out, SEOS_ENCRYPTION_AES); } // turn off afterwards @@ -208,7 +167,6 @@ void SimulateSeos(seos_emulate_req_t *msg) { uint8_t diver_cmac_key[16]; // Calculated block size - const uint8_t max_bs = 16; const uint8_t bs = block_size(msg->encr_alg); const uint8_t half_bs = bs >> 1; if (bs == 0) { @@ -220,9 +178,9 @@ void SimulateSeos(seos_emulate_req_t *msg) { // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it + // Allocate 1024 bytes for the dynamic modulation, created when the reader queries for it // Such a response is less time critical, so we can prepare them on the fly -#define DYNAMIC_RESPONSE_BUFFER_SIZE 64 +#define DYNAMIC_RESPONSE_BUFFER_SIZE 192 #define DYNAMIC_MODULATION_BUFFER_SIZE 1024 uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE); @@ -259,6 +217,14 @@ void SimulateSeos(seos_emulate_req_t *msg) { return; } + // The RND_* counter is exactly one block size + uint8_t *rndCounter = BigBuf_calloc(bs); + if (rndCounter == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } + uint16_t flags = 0; uint8_t data[PM3_CMD_DATA_SIZE] = { 0 }; memcpy(data, msg->uid, msg->uid_len); @@ -416,9 +382,11 @@ void SimulateSeos(seos_emulate_req_t *msg) { if (selected_oid) { // Synthesized IV: half a block of random data followed by half of the CMAC of that data - uint8_t synthesized_iv[max_bs]; - memset(synthesized_iv, 0, bs>>1); // TODO: Maybe actually use random data? - generate_cmac(msg->privmac, synthesized_iv, bs>>1, synthesized_iv+(bs>>1), bs>>1, msg->encr_alg); + memset(cryptogram_iv, 0, half_bs); // TODO: Maybe actually use random data? + if (!generate_cmac(msg->privmac, cryptogram_iv, half_bs, cryptogram_iv+half_bs, msg->encr_alg)) { + Dbprintf(_RED_("Select ADF failed") ": Failed to create IV CMAC."); + break; + } // Always exactly 0x30 bytes in length const uint8_t reply_len = 0x30; @@ -436,12 +404,6 @@ void SimulateSeos(seos_emulate_req_t *msg) { memcpy(reply+reply_idx, msg->diversifier, msg->diversifier_len); reply_idx += msg->diversifier_len; - uint8_t cryptogram[reply_len]; - if (!generate_cryptogram(msg->privenc, synthesized_iv, reply, reply_len, cryptogram, msg->encr_alg)) { - Dbprintf(_RED_("Select ADF failed") ": Failed to create reply cryptogram."); - break; - } - uint8_t tlv_base = 1 + offset; uint8_t tlv_idx = tlv_base; @@ -452,14 +414,23 @@ void SimulateSeos(seos_emulate_req_t *msg) { dynamic_response_info.response[tlv_idx++] = 0x85; // Tag: cryptogram dynamic_response_info.response[tlv_idx++] = reply_len + bs; // Length - memcpy(dynamic_response_info.response+tlv_idx, synthesized_iv, bs); + memcpy(dynamic_response_info.response+tlv_idx, cryptogram_iv, bs); tlv_idx += bs; - memcpy(dynamic_response_info.response+tlv_idx, cryptogram, reply_len); + + // Generate cryptogram directly into response buffer + if (!generate_cryptogram(msg->privenc, true, reply, reply_len, dynamic_response_info.response+tlv_idx, msg->encr_alg)) { + Dbprintf(_RED_("Select ADF failed") ": Failed to create reply cryptogram."); + break; + } tlv_idx += reply_len; // Always an 8-byte CMAC + const uint8_t cmac_size = 8; uint8_t *cmac = work_buffer_a; - uint8_t cmac_size = generate_cmac(msg->privmac, dynamic_response_info.response+tlv_base, tlv_idx-tlv_base, cmac, 8, msg->encr_alg); + if (!generate_cmac(msg->privmac, dynamic_response_info.response+tlv_base, tlv_idx-tlv_base, cmac, msg->encr_alg)) { + Dbprintf(_RED_("Select ADF failed") ": Failed to create reply CMAC."); + break; + } dynamic_response_info.response[tlv_idx++] = 0x8E; // Tag: CMAC dynamic_response_info.response[tlv_idx++] = cmac_size; // Length @@ -520,13 +491,16 @@ void SimulateSeos(seos_emulate_req_t *msg) { uint8_t keyslot = receivedCmd[4 + offset]; // APDU P2 byte - seos_kdf(true, msg->authkey, keyslot, msg->oid, msg->oid_len, msg->diversifier, msg->diversifier_len, diver_encr_key, msg->encr_alg, msg->hash_alg); - seos_kdf(false, msg->authkey, keyslot, msg->oid, msg->oid_len, msg->diversifier, msg->diversifier_len, diver_cmac_key, msg->encr_alg, msg->hash_alg); + seos_kdf(true, msg->authkey, keyslot, work_buffer_a, msg->oid, msg->oid_len, msg->diversifier, msg->diversifier_len, diver_encr_key, msg->encr_alg, msg->hash_alg); + seos_kdf(false, msg->authkey, keyslot, work_buffer_a, msg->oid, msg->oid_len, msg->diversifier, msg->diversifier_len, diver_cmac_key, msg->encr_alg, msg->hash_alg); // Verify CMAC (last 8 bytes) uint8_t request_len = received_tlv_len - 8; uint8_t *cmac = work_buffer_a; - generate_cmac(diver_cmac_key, received_tlv, request_len, cmac, 8, msg->encr_alg); + if (!generate_cmac(diver_cmac_key, received_tlv, request_len, cmac, msg->encr_alg)) { + Dbprintf(_RED_("Mutual auth failed") ": Failed to create CMAC."); + break; + } if (memcmp(cmac, received_tlv + request_len, 8) != 0) { Dbprintf(_RED_("Mutual auth failed") ": Invalid CMAC:"); Dbhexdump(8, received_tlv + request_len, false); @@ -559,8 +533,11 @@ void SimulateSeos(seos_emulate_req_t *msg) { // Generate cryptogram + 8-byte CMAC const uint8_t reply_len = reply_plain_len + 8; uint8_t *reply = work_buffer_b; - generate_cryptogram(diver_encr_key, NULL, reply_plain, reply_plain_len, reply, msg->encr_alg); - generate_cmac(diver_cmac_key, reply, reply_plain_len, reply+reply_plain_len, 8, msg->encr_alg); + generate_cryptogram(diver_encr_key, false, reply_plain, reply_plain_len, reply, msg->encr_alg); + if (!generate_cmac(diver_cmac_key, reply, reply_plain_len, reply+reply_plain_len, msg->encr_alg)) { + Dbprintf(_RED_("Mutual auth failed") ": Failed to create reply CMAC."); + break; + } uint8_t tlv_idx = 1 + offset; @@ -662,7 +639,6 @@ void SimulateSeos(seos_emulate_req_t *msg) { // Combine the first half_bs each of RND_ICC and RND_IFD, // then increment as a single counter - uint8_t rndCounter[bs]; memcpy(rndCounter, RND_ICC, half_bs); memcpy(rndCounter + half_bs, RND_IFD, half_bs); @@ -697,7 +673,10 @@ void SimulateSeos(seos_emulate_req_t *msg) { } uint8_t *cmac = work_buffer_b; - generate_cmac(diver_cmac_key, mac_input, mac_input_idx, cmac, recvd_cmac_length, msg->encr_alg); + if (!generate_cmac(diver_cmac_key, mac_input, mac_input_idx, cmac, msg->encr_alg)) { + Dbprintf(_RED_("Get Data failed") ": Failed to create CMAC."); + break; + } if (memcmp(cmac, recvd_cmac, recvd_cmac_length) != 0) { Dbprintf( _RED_("Get Data failed") ": Invalid CMAC."); break; @@ -748,7 +727,7 @@ void SimulateSeos(seos_emulate_req_t *msg) { } uint8_t *reply_cryptogram = work_buffer_b; - if (!generate_cryptogram(diver_encr_key, NULL, reply, reply_len, reply_cryptogram, msg->encr_alg)) { + if (!generate_cryptogram(diver_encr_key, false, reply, reply_len, reply_cryptogram, msg->encr_alg)) { Dbprintf(_RED_("Get Data failed") ": Failed to create reply cryptogram."); break; } @@ -787,7 +766,11 @@ void SimulateSeos(seos_emulate_req_t *msg) { mac_input_idx += bs - (mac_input_idx % bs); } - uint8_t cmac_size = generate_cmac(diver_cmac_key, mac_input, mac_input_idx, cmac, recvd_cmac_length, msg->encr_alg); + uint8_t cmac_size = recvd_cmac_length; + if (!generate_cmac(diver_cmac_key, mac_input, mac_input_idx, cmac, msg->encr_alg)) { + Dbprintf(_RED_("Get Data failed") ": Failed to create reply CMAC."); + break; + } dynamic_response_info.response[tlv_idx++] = 0x8E; // Tag: CMAC dynamic_response_info.response[tlv_idx++] = cmac_size; // Length