diff --git a/armsrc/seos.c b/armsrc/seos.c index 3fc220422..20f57ccde 100644 --- a/armsrc/seos.c +++ b/armsrc/seos.c @@ -210,6 +210,7 @@ void SimulateSeos(seos_emulate_req_t *msg) { // 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) { // Can't continue, invalid encryption algorithm reply_ng(CMD_HF_SEOS_SIMULATE, PM3_EINVARG, NULL, 0); @@ -221,7 +222,7 @@ void SimulateSeos(seos_emulate_req_t *msg) { // Allocate 512 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 192 +#define DYNAMIC_RESPONSE_BUFFER_SIZE 64 #define DYNAMIC_MODULATION_BUFFER_SIZE 1024 uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE); @@ -243,6 +244,21 @@ void SimulateSeos(seos_emulate_req_t *msg) { .modulation_n = 0 }; + // General-purpose shared buffers +#define WORK_BUFFER_SIZE 0x80 + uint8_t *work_buffer_a = BigBuf_calloc(WORK_BUFFER_SIZE); + if (work_buffer_a == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } + uint8_t *work_buffer_b = BigBuf_calloc(WORK_BUFFER_SIZE); + if (work_buffer_b == 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); @@ -356,7 +372,6 @@ void SimulateSeos(seos_emulate_req_t *msg) { uint8_t aid_len = receivedCmd[5 + offset]; uint8_t *aid = &receivedCmd[6 + offset]; - // TODO: See if this actually matches exactly (if possible) if ((aid_len == sizeof(SEOS_AID)) && (memcmp(SEOS_AID, aid, sizeof(SEOS_AID)) == 0)) { // Evaluate the AID sent by the Reader to the AID supplied // Format as TLV and acknowledge /* @@ -412,7 +427,7 @@ void SimulateSeos(seos_emulate_req_t *msg) { // Always exactly 0x30 bytes in length const uint8_t reply_len = 0x30; uint8_t reply_idx = 0; - uint8_t reply[reply_len]; + uint8_t *reply = work_buffer_a; memset(reply, 0, reply_len); reply[reply_idx++] = 0x06; // Tag: selected OID @@ -447,8 +462,8 @@ void SimulateSeos(seos_emulate_req_t *msg) { tlv_idx += reply_len; // Always an 8-byte CMAC - uint8_t cmac[8]; - uint8_t cmac_size = generate_cmac(msg->privmac, dynamic_response_info.response+tlv_base, tlv_idx-tlv_base, cmac, sizeof(cmac), msg->encr_alg); + 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); dynamic_response_info.response[tlv_idx++] = 0x8E; // Tag: CMAC dynamic_response_info.response[tlv_idx++] = cmac_size; // Length @@ -497,6 +512,16 @@ void SimulateSeos(seos_emulate_req_t *msg) { uint8_t received_tlv_len = received_tlv[1]; received_tlv += 2; + if (received_tlv_len > WORK_BUFFER_SIZE) { + Dbprintf(_RED_("Mutual auth failed") ": Recieved cryptogram too long."); + break; + } + + if (received_tlv_len < 32) { + Dbprintf(_RED_("Mutual auth failed") ": Recieved cryptogram too short."); + break; + } + 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); @@ -504,7 +529,7 @@ void SimulateSeos(seos_emulate_req_t *msg) { // Verify CMAC (last 8 bytes) uint8_t request_len = received_tlv_len - 8; - uint8_t cmac[8]; + uint8_t *cmac = work_buffer_a; generate_cmac(diver_cmac_key, received_tlv, request_len, cmac, 8, msg->encr_alg); if (memcmp(cmac, received_tlv + request_len, 8) != 0) { Dbprintf(_RED_("Mutual auth failed") ": Invalid CMAC:"); @@ -514,7 +539,7 @@ void SimulateSeos(seos_emulate_req_t *msg) { break; } - uint8_t request[received_tlv_len]; + uint8_t *request = work_buffer_a; if (!decrypt_cryptogram(diver_encr_key, received_tlv, request_len, request, msg->encr_alg)) { Dbprintf(_RED_("Mutual auth failed") ": Failed to decrypt cryptogram."); break; @@ -529,24 +554,26 @@ void SimulateSeos(seos_emulate_req_t *msg) { memcpy(KEY_IFD, request + 16, 16); // reply = RND_ICC | RND_IFD | KEY_ICC - uint8_t reply_plain[32]; + const uint8_t reply_plain_len = 32; + uint8_t *reply_plain = work_buffer_a; memcpy(reply_plain + 0, RND_ICC, 8); memcpy(reply_plain + 8, RND_IFD, 8); memcpy(reply_plain + 16, KEY_ICC, 16); - // Generate cryptogram + CMAC - uint8_t reply[sizeof(reply_plain)+8]; - generate_cryptogram(diver_encr_key, NULL, reply_plain, sizeof(reply_plain), reply, msg->encr_alg); - generate_cmac(diver_cmac_key, reply, sizeof(reply_plain), reply+sizeof(reply_plain), 8, msg->encr_alg); + // 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); uint8_t tlv_idx = 1 + offset; dynamic_response_info.response[tlv_idx++] = 0x7C; // Tag: mutual auth - dynamic_response_info.response[tlv_idx++] = sizeof(reply)+2; // Length + dynamic_response_info.response[tlv_idx++] = reply_len+2; // Length dynamic_response_info.response[tlv_idx++] = 0x82; // Tag: request for challenge - dynamic_response_info.response[tlv_idx++] = sizeof(reply); // Length - memcpy(dynamic_response_info.response+tlv_idx, reply, sizeof(reply)); - tlv_idx += sizeof(reply); + dynamic_response_info.response[tlv_idx++] = reply_len; // Length + memcpy(dynamic_response_info.response+tlv_idx, reply, reply_len); + tlv_idx += reply_len; dynamic_response_info.response_n = tlv_idx; @@ -556,7 +583,7 @@ void SimulateSeos(seos_emulate_req_t *msg) { // IMPORTANT: before sending reply, calculate final diversified keys - uint8_t hash_input[38]; + uint8_t *hash_input = work_buffer_a; uint8_t hash_idx = 0; // Counter hash_input[hash_idx++] = 0x00; @@ -577,16 +604,16 @@ void SimulateSeos(seos_emulate_req_t *msg) { memcpy(hash_input+hash_idx, RND_IFD, 8); hash_idx += 8; - uint8_t hash_output[40]; + uint8_t *hash_output = work_buffer_b; if (msg->hash_alg == SEOS_HASHING_SHA1) { - mbedtls_sha1(hash_input, 38, hash_output); + mbedtls_sha1(hash_input, hash_idx, hash_output); // Increment LSB of counter for second hash hash_input[3]++; - mbedtls_sha1(hash_input, 38, hash_output + 20); + mbedtls_sha1(hash_input, hash_idx, hash_output + 20); } else if (msg->hash_alg == SEOS_HASHING_SHA256) { - mbedtls_sha256(hash_input, 38, hash_output, 0); + mbedtls_sha256(hash_input, hash_idx, hash_output, 0); } else { Dbprintf(_RED_("Unknown Hashing Algorithm")); break; @@ -632,131 +659,150 @@ void SimulateSeos(seos_emulate_req_t *msg) { } if (cryptogram != NULL && recvd_cmac != NULL) { - uint8_t rndCounter[bs]; - memcpy(rndCounter, RND_ICC, bs / 2); - memcpy(rndCounter + bs / 2, RND_IFD, bs / 2); - for (int8_t i=bs-1; i>=0; i--) { - rndCounter[i]++; - if (rndCounter[i] != 0x00) break; - } + if (cryptogram_length > WORK_BUFFER_SIZE) { + Dbprintf(_RED_("Get Data failed") ": Recieved cryptogram too long."); + break; + } - uint8_t cryptogram_padding = recvd_cmac_offset % bs; - if (cryptogram_padding) cryptogram_padding = bs - cryptogram_padding; + // 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); - uint8_t padded_apdu_header[bs]; - memset(padded_apdu_header, 0, bs); - memcpy(padded_apdu_header, &receivedCmd[1 + offset], 4); - padded_apdu_header[4] = 0x80; + for (int8_t i=bs-1; i>=0; i--) { + rndCounter[i]++; + if (rndCounter[i] != 0x00) break; + } - uint8_t mac_input[sizeof(rndCounter) + sizeof(padded_apdu_header) + recvd_cmac_offset + cryptogram_padding]; - memset(mac_input, 0, sizeof(mac_input)); - memcpy(mac_input, rndCounter, sizeof(rndCounter)); - memcpy(mac_input+sizeof(rndCounter), padded_apdu_header, sizeof(padded_apdu_header)); - memcpy(mac_input+sizeof(rndCounter)+sizeof(padded_apdu_header), received_tlv, recvd_cmac_offset); - if (cryptogram_padding) { - mac_input[sizeof(rndCounter)+sizeof(padded_apdu_header)+recvd_cmac_offset] = 0x80; - } + uint8_t *mac_input = work_buffer_a; + uint8_t mac_input_idx = 0; - uint8_t cmac[recvd_cmac_length]; - generate_cmac(diver_cmac_key, mac_input, sizeof(mac_input), cmac, recvd_cmac_length, msg->encr_alg); - if (memcmp(cmac, recvd_cmac, recvd_cmac_length) != 0) { - Dbprintf( _RED_("Get Data failed") ": Invalid CMAC:"); - Dbhexdump(recvd_cmac_length, cmac, false); - Dbprintf("for data:"); - Dbhexdump(sizeof(mac_input), mac_input, false); + // Add RND_* counter to mac_input + memcpy(mac_input + mac_input_idx, rndCounter, bs); + mac_input_idx += bs; + + // Add padded APDU header to mac_input + uint8_t *padded_apdu_header = mac_input + mac_input_idx; + memset(padded_apdu_header, 0, bs); + memcpy(padded_apdu_header, &receivedCmd[1 + offset], 4); + padded_apdu_header[4] = 0x80; + mac_input_idx += bs; + + // Add received TLV data to mac_input + memcpy(mac_input + mac_input_idx, received_tlv, recvd_cmac_offset); + mac_input_idx += recvd_cmac_offset; + + // Add padding (if needed) to mac_input + if (mac_input_idx % bs) { + memset(mac_input + mac_input_idx, 0, bs - (mac_input_idx % bs)); + mac_input[mac_input_idx] = 0x80; + mac_input_idx += bs - (mac_input_idx % bs); + } + + uint8_t *cmac = work_buffer_b; + generate_cmac(diver_cmac_key, mac_input, mac_input_idx, cmac, recvd_cmac_length, msg->encr_alg); + if (memcmp(cmac, recvd_cmac, recvd_cmac_length) != 0) { + Dbprintf( _RED_("Get Data failed") ": Invalid CMAC."); + break; + } + + uint8_t *request = work_buffer_a; + decrypt_cryptogram(diver_encr_key, cryptogram, cryptogram_length, request, msg->encr_alg); + + uint8_t tlv_base = 1 + offset; + uint8_t tlv_idx = tlv_base; + + if (is_put) { + // TODO: Add write support + Dbprintf(_RED_("Put Data failed") ": Not implemented"); + break; + } else { + //5c 02 ff 00 + if (request[0] != 0x5C) { + Dbprintf(_RED_("Get Data failed") ": Invalid request TLV. Expected tag 5C, but got %02X.", request[0]); break; } - uint8_t request[cryptogram_length]; - decrypt_cryptogram(diver_encr_key, cryptogram, cryptogram_length, request, msg->encr_alg); - - uint8_t tlv_base = 1 + offset; - uint8_t tlv_idx = tlv_base; - - if (is_put) { - - } else { - //5c 02 ff 00 - if (request[0] != 0x5C) { - Dbprintf(_RED_("Get Data failed") ": Invalid request TLV. Expected tag 5C, but got %02X.", request[0]); - break; - } - - if (request[1] != msg->data_tag_len || memcmp(request+2, msg->data_tag, msg->data_tag_len) != 0) { - Dbprintf(_RED_("Get Data failed") ": Requested invalid data tag."); - break; - } - - uint8_t reply_len = msg->data_tag_len + 1 + msg->data_len; - reply_len = round_to_next(reply_len, bs); - uint8_t reply[reply_len]; - memset(reply, 0, reply_len); - - uint8_t reply_idx = 0; - memcpy(reply+reply_idx, msg->data_tag, msg->data_tag_len); // Tag - reply_idx += msg->data_tag_len; - reply[reply_idx++] = msg->data_len; // Length - memcpy(reply+reply_idx, msg->data, msg->data_len); // Value - reply_idx += msg->data_len; - - if (reply_idx != reply_len) { - // Add 0x80 at first byte after data for start of padding - reply[reply_idx] = 0x80; - } - - uint8_t reply_cryptogram[reply_len]; - if (!generate_cryptogram(diver_encr_key, NULL, reply, reply_len, reply_cryptogram, msg->encr_alg)) { - Dbprintf(_RED_("Get Data failed") ": Failed to create reply cryptogram."); - break; - } - - // Only include a cryptogram for GET DATA - dynamic_response_info.response[tlv_idx++] = 0x85; // Tag: cryptogram - dynamic_response_info.response[tlv_idx++] = reply_len; // Length - memcpy(dynamic_response_info.response+tlv_idx, reply_cryptogram, reply_len); - tlv_idx += reply_len; + if (request[1] != msg->data_tag_len || memcmp(request+2, msg->data_tag, msg->data_tag_len) != 0) { + Dbprintf(_RED_("Get Data failed") ": Requested invalid data tag."); + break; } - // Whether we GET DATA or PUT DATA, add the response status code and CMAC - dynamic_response_info.response[tlv_idx++] = 0x99; // Tag: status code - dynamic_response_info.response[tlv_idx++] = 0x02; // Length - dynamic_response_info.response[tlv_idx++] = 0x90; - dynamic_response_info.response[tlv_idx++] = 0x00; - - - // Unlike every other CMAC, this time we need to increment - // the rndCounter from above again and CMAC with *that* - uint8_t mac_length = sizeof(rndCounter) + (tlv_idx - tlv_base); - mac_length = round_to_next(mac_length, bs); - uint8_t mac_input_2[mac_length]; - for (int8_t i=bs-1; i>=0; i--) { - rndCounter[i]++; - if (rndCounter[i] != 0x00) break; + uint8_t reply_len = msg->data_tag_len + 1 + msg->data_len; + reply_len = round_to_next(reply_len, bs); + if (reply_len > WORK_BUFFER_SIZE) { + Dbprintf(_RED_("Get Data failed") ": Unable to generate reply: too long."); + break; } - memset(mac_input_2, 0, mac_length); - uint8_t mac_idx = 0; - memcpy(mac_input_2, rndCounter, sizeof(rndCounter)); - mac_idx += sizeof(rndCounter); - memcpy(mac_input_2+sizeof(rndCounter), dynamic_response_info.response + tlv_base, tlv_idx - tlv_base); - mac_idx += tlv_idx - tlv_base; - if (mac_idx != mac_length) { + uint8_t *reply = work_buffer_a; + + uint8_t reply_idx = 0; + memcpy(reply+reply_idx, msg->data_tag, msg->data_tag_len); // Tag + reply_idx += msg->data_tag_len; + reply[reply_idx++] = msg->data_len; // Length + memcpy(reply+reply_idx, msg->data, msg->data_len); // Value + reply_idx += msg->data_len; + + if (reply_idx != reply_len) { + memset(reply + reply_idx, 0, reply_len - reply_idx); // Add 0x80 at first byte after data for start of padding - mac_input_2[mac_idx] = 0x80; + reply[reply_idx] = 0x80; } - uint8_t cmac_size = generate_cmac(diver_cmac_key, mac_input_2, mac_length, cmac, sizeof(cmac), msg->encr_alg); + uint8_t *reply_cryptogram = work_buffer_b; + if (!generate_cryptogram(diver_encr_key, NULL, reply, reply_len, reply_cryptogram, msg->encr_alg)) { + Dbprintf(_RED_("Get Data failed") ": Failed to create reply cryptogram."); + break; + } - dynamic_response_info.response[tlv_idx++] = 0x8E; // Tag: CMAC - dynamic_response_info.response[tlv_idx++] = cmac_size; // Length - memcpy(dynamic_response_info.response+tlv_idx, cmac, cmac_size); - tlv_idx += cmac_size; + // Only include a cryptogram for GET DATA + dynamic_response_info.response[tlv_idx++] = 0x85; // Tag: cryptogram + dynamic_response_info.response[tlv_idx++] = reply_len; // Length + memcpy(dynamic_response_info.response+tlv_idx, reply_cryptogram, reply_len); + tlv_idx += reply_len; + } - dynamic_response_info.response_n = tlv_idx; + // Whether we GET DATA or PUT DATA, add the response status code and CMAC + dynamic_response_info.response[tlv_idx++] = 0x99; // Tag: status code + dynamic_response_info.response[tlv_idx++] = 0x02; // Length + dynamic_response_info.response[tlv_idx++] = 0x90; + dynamic_response_info.response[tlv_idx++] = 0x00; - // Set status code to Success - apdu_status[0] = 0x90; - apdu_status[1] = 0x00; + // Unlike every other CMAC, this time we need to prepend + // the same counter from above, but increment it again + for (int8_t i=bs-1; i>=0; i--) { + rndCounter[i]++; + if (rndCounter[i] != 0x00) break; + } + + mac_input_idx = 0; + + memcpy(mac_input + mac_input_idx, rndCounter, sizeof(rndCounter)); + mac_input_idx += sizeof(rndCounter); + memcpy(mac_input + mac_input_idx, dynamic_response_info.response + tlv_base, tlv_idx - tlv_base); + mac_input_idx += tlv_idx - tlv_base; + + // Add padding (if needed) to mac_input + if (mac_input_idx % bs) { + memset(mac_input + mac_input_idx, 0, bs - (mac_input_idx % bs)); + mac_input[mac_input_idx] = 0x80; + 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); + + dynamic_response_info.response[tlv_idx++] = 0x8E; // Tag: CMAC + dynamic_response_info.response[tlv_idx++] = cmac_size; // Length + memcpy(dynamic_response_info.response+tlv_idx, cmac, cmac_size); + tlv_idx += cmac_size; + + dynamic_response_info.response_n = tlv_idx; + + // Set status code to Success + apdu_status[0] = 0x90; + apdu_status[1] = 0x00; } else { Dbprintf( _RED_("Get Data failed") ": No cryptogram or CMAC found in request."); }