From df2cf0a9484258a855c610087aeda60659bfe5e5 Mon Sep 17 00:00:00 2001 From: Adam Jon Foster Date: Tue, 7 Jan 2025 06:17:37 +0800 Subject: [PATCH] Added SEOS Support Added generic SEOS Support with the following commands; - `hf seos info` (slightly improved from the original - `hf seos pacs` (return the SIO value from the default ADF and default tag) - `hf seos adf ` (send a get data request to an ADF with a custom tag list) - `hf seos gdf` (make a request for a GDF based upon a command found in their docs) - `hf seos managekeys` (manage your own keys used for SEOS commands) You still need keys to use this commandset, this does not have any keys and is theoretical at this stage until someone tests it with the keys. Signed-off-by: Adam Jon Foster --- client/src/cmdhfseos.c | 1588 +++++++++++++++++++++++++++++++++++++++- client/src/cmdhfseos.h | 3 +- 2 files changed, 1559 insertions(+), 32 deletions(-) diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 4ab7c1b15..fe57b7c76 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -23,85 +23,1303 @@ #include "cmdparser.h" // command_t #include "comms.h" // clearCommandBuffer #include "cmdtrace.h" +#include +#include +#include "fileutils.h" #include "crc16.h" #include "ui.h" #include "cmdhf14a.h" // manufacture #include "protocols.h" // definitions of ISO14A/7816 protocol #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "crypto/asn1utils.h" // ASN1 decode / print +#include "crypto/libpcrypto.h" // AES decrypt #include "commonutil.h" // get_sw #include "protocols.h" // ISO7816 APDU return codes +static uint8_t zeros[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + static int CmdHelp(const char *Cmd); -static int seos_select(void) { - bool activate_field = true; - bool keep_field_on = true; +typedef struct { + uint8_t nonce[8]; + uint8_t privEncKey[16]; + uint8_t privMacKey[16]; + uint8_t readKey[16]; + uint8_t writeKey[16]; + uint8_t adminKey[16]; +} keyset_t; + +keyset_t keys[] = { + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Nonce + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // privEncKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // privMacKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // readKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // writeKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } // adminKey + }, + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Nonce + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // privEncKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // privMacKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // readKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // writeKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } // adminKey + }, + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Nonce + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // privEncKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // privMacKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // readKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // writeKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } // adminKey + }, + { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Nonce + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // privEncKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // privMacKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // readKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // writeKey + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } // adminKey + }, +}; + +typedef struct { + const int value; + const char* name; +} known_algo_t; + +static const known_algo_t known_algorithm_map[] = { + {2, "2K3DES_CBC_MODE"}, + {4, "3K3DES_CBC_MODE"}, + {6, "SHA-1"}, + {7, "SHA-256"}, + {9, "AES-128_CBC_MODE"}, +}; + +static int create_cmac (uint8_t* key, uint8_t* input, uint8_t* out, int input_len, int encryption_algorithm) { + uint8_t iv[16] = {0x00}; + + if (encryption_algorithm == 0x09) { + // Working as expected + aes_cmac(iv, key, input, out, input_len); + } + else if (encryption_algorithm == 0x02) { + // CMAC Requires a 24 byte key, but the 2k3DES uses the 1st part for the 3rd part of the key + memcpy(&key[16], &key[0], 8); + + const mbedtls_cipher_info_t* ctx; + ctx = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_EDE3_ECB); + mbedtls_cipher_cmac(ctx, key, 192, input, input_len, out); + } else { + PrintAndLogEx(ERR, _RED_("Unknown Encryption Algorithm")); + return PM3_ESOFT; + } + return PM3_SUCCESS; +} + +static int create_cryptogram (uint8_t* key, uint8_t* input, uint8_t* out, int input_len, int encryption_algorithm) { + uint8_t iv[16] = {}; + + if (encryption_algorithm == 0x09) { + aes_encode(iv, key, input, out, input_len); + } else if (encryption_algorithm == 0x02) { + mbedtls_des3_context ctx3; + mbedtls_des3_set2key_enc(&ctx3, key); + mbedtls_des3_crypt_cbc(&ctx3, MBEDTLS_DES_ENCRYPT, input_len, iv, input, out); + mbedtls_des3_free(&ctx3); + } else { + PrintAndLogEx(ERR, _RED_("Unknown Encryption Algorithm")); + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static int decrypt_cryptogram (uint8_t* key, uint8_t* input, uint8_t* out, int input_len, int encryption_algorithm) { + uint8_t iv[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + if (encryption_algorithm == 0x09) { + aes_decode(iv, key, input, out, input_len); + } else if (encryption_algorithm == 0x02) { + mbedtls_des3_context ctx3; + mbedtls_des3_set2key_dec(&ctx3, key); + mbedtls_des3_crypt_cbc(&ctx3, MBEDTLS_DES_DECRYPT, input_len, iv, input, out); + mbedtls_des3_free(&ctx3); + } else { + PrintAndLogEx(ERR, "Unknown Encryption Algorithm"); + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static void increment_command_wrapper(uint8_t* input, int input_len) { + input[input_len-1]++; // Increment the last element of the header by 1 +} + +static void padToBlockSize(const uint8_t* input, int inputSize, int blockSize, uint8_t* output) { + int paddingSize = blockSize - (inputSize % blockSize); + memcpy(output, input, inputSize); + memset(output + inputSize, 0x80, 1); + memset(output + inputSize + 1, 0x00, paddingSize - 1); +} + +static void generate_command_wrapping(uint8_t *command_Header, int command_header_len, uint8_t *unencrypted_Command, int unencrypted_command_len ,uint8_t *rndICC, uint8_t *rndIFD, uint8_t *diversified_enc_key, uint8_t *diversified_mac_key, int encryption_algorithm, uint8_t *command, int *command_len) { + int block_size = 0; + + if (encryption_algorithm == 0x02) { + block_size = 8; + } + else if (encryption_algorithm == 0x09) { + block_size = 16; + } + else { + PrintAndLogEx(ERR, _RED_("Unknown Encryption Algorithm")); + return; + } + + uint8_t rndCounter[block_size]; + memcpy (rndCounter, rndICC, block_size / 2); + memcpy (rndCounter + block_size / 2, rndIFD, block_size/2); + increment_command_wrapper(rndCounter, block_size); + + // Command Header is for the APDU Command to be sent + uint8_t padded_Command_Header[block_size]; + padToBlockSize(command_Header, command_header_len, block_size, padded_Command_Header); + + // Unencrypted Command is our actual command data + uint8_t padded_unencrypted_Command[block_size]; + padToBlockSize(unencrypted_Command, unencrypted_command_len, block_size, padded_unencrypted_Command); + + uint8_t padded_encrypted_Command[block_size]; + create_cryptogram(diversified_enc_key, padded_unencrypted_Command, padded_encrypted_Command, sizeof(padded_unencrypted_Command), encryption_algorithm); + + uint8_t asn1_tag_cryptograph[2] = {0x85,ARRAYLEN(padded_encrypted_Command)}; + uint8_t asn1_tag_mac[2] = {0x8e,0x08}; + uint8_t command_trailer[2] = {0x97,0x00}; + uint8_t padded_command_trailer[block_size - ARRAYLEN(command_trailer)]; + padToBlockSize(command_trailer, sizeof(command_trailer), block_size, padded_command_trailer); + + uint8_t toEncrypt[ARRAYLEN(rndCounter) + ARRAYLEN(padded_Command_Header) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(padded_command_trailer)]; + + memcpy(toEncrypt, rndCounter, ARRAYLEN(rndCounter)); + memcpy(toEncrypt + ARRAYLEN(rndCounter), padded_Command_Header, ARRAYLEN(padded_Command_Header)); + memcpy(toEncrypt + ARRAYLEN(rndCounter) + ARRAYLEN(padded_Command_Header), asn1_tag_cryptograph, ARRAYLEN(asn1_tag_cryptograph)); + memcpy(toEncrypt + ARRAYLEN(rndCounter) + ARRAYLEN(padded_Command_Header) + ARRAYLEN(asn1_tag_cryptograph), padded_encrypted_Command, ARRAYLEN(padded_encrypted_Command)); + memcpy(toEncrypt + ARRAYLEN(rndCounter) + ARRAYLEN(padded_Command_Header) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command), padded_command_trailer, ARRAYLEN(padded_command_trailer)); + + // Breakdown + // 0181e43801010201 + 0000000000000001 + 0CCB3FFF800000000000000000000000 + 8510EB54DA90CB43AEE7FBFE816ECA25A10D + 9700 + 800000000000000000000000 + + uint8_t mac[8]; + create_cmac(diversified_mac_key, toEncrypt, mac, sizeof(toEncrypt), encryption_algorithm); + + // PrintAndLogEx(SUCCESS, "Encryption Key................... " _YELLOW_("%s"), sprint_hex_inrow(diversified_enc_key, 24)); + // PrintAndLogEx(SUCCESS, "MAC Key.......................... " _YELLOW_("%s"), sprint_hex_inrow(diversified_mac_key, 24)); + // PrintAndLogEx(SUCCESS, "rndCounter....................... " _YELLOW_("%s"), sprint_hex_inrow(rndCounter,sizeof(rndCounter))); + // PrintAndLogEx(SUCCESS, "padded_encrypted_Command......... " _YELLOW_("%s"), sprint_hex_inrow(padded_encrypted_Command,sizeof(padded_encrypted_Command))); + // PrintAndLogEx(SUCCESS, "toEncrypt........................ " _YELLOW_("%s"), sprint_hex_inrow(toEncrypt,sizeof(toEncrypt))); + // PrintAndLogEx(SUCCESS, "MAC.............................. " _YELLOW_("%s"), sprint_hex_inrow(mac,sizeof(mac))); + + uint8_t sizeofcommand[1] = {ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer) + ARRAYLEN(asn1_tag_mac)+ ARRAYLEN(mac)}; + uint8_t respondTo[1] = {0x00}; + + uint8_t completedCommand[command_header_len + 1 + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer) + ARRAYLEN(asn1_tag_mac)+ ARRAYLEN(mac) + 1]; + memcpy(completedCommand, command_Header, command_header_len); + memcpy(completedCommand + command_header_len, sizeofcommand, ARRAYLEN(sizeofcommand)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand), asn1_tag_cryptograph, ARRAYLEN(asn1_tag_cryptograph)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph), padded_encrypted_Command, ARRAYLEN(padded_encrypted_Command)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command), command_trailer, ARRAYLEN(command_trailer)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer), asn1_tag_mac, ARRAYLEN(asn1_tag_mac)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer) + ARRAYLEN(asn1_tag_mac), mac, ARRAYLEN(mac)); + memcpy(completedCommand + command_header_len + ARRAYLEN(sizeofcommand) + ARRAYLEN(asn1_tag_cryptograph) + ARRAYLEN(padded_encrypted_Command) + ARRAYLEN(command_trailer) + ARRAYLEN(asn1_tag_mac) + ARRAYLEN(mac), respondTo, 1); + + // PrintAndLogEx(INFO, "--- " _CYAN_("Command Generation") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Command Header................... " _YELLOW_("%s"), sprint_hex_inrow(command_Header,sizeof(command_Header))); + // PrintAndLogEx(SUCCESS, "Payload.......................... " _YELLOW_("%s"), sprint_hex_inrow(unencrypted_Command,sizeof(unencrypted_Command))); + // PrintAndLogEx(SUCCESS, "completedCommand................. " _YELLOW_("%s"), sprint_hex_inrow(completedCommand,sizeof(completedCommand))); + + memcpy(command, completedCommand, ARRAYLEN(completedCommand)); + *command_len = ARRAYLEN(completedCommand); + //return; +} + +static int seos_get_data(uint8_t *rndICC, uint8_t *rndIFD, uint8_t *diversified_enc_key, uint8_t *diversified_mac_key, uint8_t *sioOutput, int* sio_size, int encryption_algorithm, uint8_t* get_data_tlv, int get_data_tlv_len) { + // Intergrating our command generation with the GetData request to make my life easier in the future + + // Command Header is for the Get Data Command using + // `0C` - Secure messaging – ISO/IEC 7816 standard, command header authenticated (C-MAC) + // `CB` - GET DATA + // uint8_t command_header[4] = {0x0c,0xcb,0x3f,0xff}; + uint8_t cla[1] = {0x0c}; // Secure Messaging Command Header + uint8_t ins[1] = {0xcb}; // GET DATA Instruction + uint8_t p1[1] = {0x3f}; // High order tag value (accoring to NIST.SP.800-73pt2-5.pdf, this is the hardcoded tag value) + uint8_t p2[1] = {0xff}; // Low order tag value + + // command builder + uint8_t command_header[ARRAYLEN(cla) + ARRAYLEN(ins) + ARRAYLEN(p1) + ARRAYLEN(p2)]; + memcpy(command_header, cla, ARRAYLEN(cla)); + memcpy(command_header + ARRAYLEN(cla), ins, ARRAYLEN(ins)); + memcpy(command_header + ARRAYLEN(cla) + ARRAYLEN(ins), p1, ARRAYLEN(p1)); + memcpy(command_header + ARRAYLEN(cla) + ARRAYLEN(ins) + ARRAYLEN(p1), p2, ARRAYLEN(p2)); + + int command_header_len = ARRAYLEN(command_header); + + // Command to be sent + // 5c [02] ff 00 + // 5c = tag list data object + // BER-TLV tag of the data object to be retrieved + // uint8_t unencrypted_command[4] = {0x5c,0x02,0xff,0x00}; + // Modification of the tags 2nd place from 00 can return other data + + uint8_t unencrypted_command[get_data_tlv_len]; + memcpy(unencrypted_command, get_data_tlv, get_data_tlv_len); + + int unencrypted_command_len = ARRAYLEN(unencrypted_command); + + uint8_t command_buffer[254]; + int command_len = 0; + + // PrintAndLogEx(SUCCESS, "Raw Command...................... " _YELLOW_("%s"), sprint_hex_inrow(unencrypted_command, get_data_tlv_len)); + generate_command_wrapping(command_header, command_header_len, unencrypted_command, unencrypted_command_len, rndICC, rndIFD, diversified_enc_key, diversified_mac_key, encryption_algorithm, command_buffer, &command_len); + + // Convert command from buffer to stream + uint8_t command_convert[command_len]; + memcpy(command_convert, command_buffer, command_len); + char completedCommandChar[sizeof(command_len) * 2 + 1]; + for (int i = 0; i < sizeof(command_convert); i++) { + snprintf(&completedCommandChar[i * 2], 3, "%02X", command_convert[i]); + } + // PrintAndLogEx(SUCCESS, "Command.......................... " _YELLOW_("%s"), completedCommandChar); + + // ------------------- Send Command ------------------- uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; - // --------------- Select SEOS applet ---------------- - uint8_t aSELECT_AID[80]; - int aSELECT_AID_n = 0; - param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); - int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + bool activate_field = false; + bool keep_field_on = true; + + uint8_t aGET_CHALLENGE[100]; + int aGET_CHALLENGE_n = command_len; + param_gethex_to_eol(completedCommandChar, 0, aGET_CHALLENGE, sizeof(aGET_CHALLENGE), &aGET_CHALLENGE_n); + int res = ExchangeAPDU14a(aGET_CHALLENGE, aGET_CHALLENGE_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Get Data Failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "--- " _CYAN_("Get Data") " ---------------------------"); + // Raw response contains a few values + // 85 is our cryptogram response (64 bytes) + // 99 is our status word response (2 bytes) + // 8E is our MAC response (8 bytes) + // PrintAndLogEx(SUCCESS, "Raw Response..................... " _YELLOW_("%s"), sprint_hex_inrow(response, (resplen - 2))); + + uint8_t cryptogram[64]; + uint8_t responseCode[2]; + uint8_t tag[2] = {0x00,0x00}; + int getDataSize = 0; + + // ------------------- Cryptogram Response ------------------- + if (resplen >= 2 && response[0] == 0x85 && response[1] == 0x40) { + uint8_t decrypted[64]; + memcpy(cryptogram, response + 2, 64); + memcpy(responseCode, response + 68, 2); + + // Decrypt the response + decrypt_cryptogram(diversified_enc_key, cryptogram, decrypted, sizeof(cryptogram), encryption_algorithm); + + // Response Format + // FF0038302F8102578CA5020500A6088101010403030008A7178515D65ED945996AB9107CD6D3E6011F56FFDD9CFFC780A9020500050000000000008000000000 + + // FF00 is our inputed tag value + // 38 is our len + + // PrintAndLogEx(SUCCESS, "Cryptogram....................... " _YELLOW_("%s"), sprint_hex_inrow(cryptogram, sizeof(cryptogram))); + // PrintAndLogEx(SUCCESS, "Decrypted........................ " _YELLOW_("%s"), sprint_hex_inrow(decrypted, sizeof(decrypted))); + + getDataSize = decrypted[2]; + memcpy(tag, decrypted, ARRAYLEN(tag)); + memmove(decrypted, decrypted + 1, sizeof(decrypted) - 1); + memmove(sioOutput, decrypted + 2, getDataSize); + *sio_size = getDataSize; + memcpy(responseCode, response + 68, 2); + + PrintAndLogEx(SUCCESS, "Response Code.................... " _YELLOW_("%s"), sprint_hex_inrow(responseCode, (ARRAYLEN(responseCode)))); + PrintAndLogEx(SUCCESS, "Output........................... " _YELLOW_("%s"), sprint_hex_inrow(sioOutput, getDataSize)); + } + else if (resplen >= 2 && response[0] == 0x99) { + memcpy(responseCode, response + 2, 2); + // PrintAndLogEx(SUCCESS, "Raw Response..................... " _YELLOW_("%s"), sprint_hex_inrow(response, (resplen - 2))); + PrintAndLogEx(SUCCESS, "Response Code.................... " _YELLOW_("%s"), sprint_hex_inrow(responseCode, (ARRAYLEN(responseCode)))); + } + + return PM3_SUCCESS; +}; + +static void set_counter_big_endian(uint8_t *buffer, uint32_t counter) { + buffer[0] = (counter >> 24) & 0xFF; + buffer[1] = (counter >> 16) & 0xFF; + buffer[2] = (counter >> 8) & 0xFF; + buffer[3] = counter & 0xFF; +} + +static void create_mutual_auth_key(uint8_t* KEYIFD, uint8_t* KEYICC, uint8_t* RNDICC, uint8_t* RNDIFD, uint8_t* EncryptionKey, uint8_t* MACKey, int encryptionAlgorithm ,int HashingAlgorithm) { + // Creating Mutual Authentication Keys + // Structure + // Prefix = 00000000 + // keyIFD.substring(16) = 0000000000000000 IFD = Interface Device + // keyICC.substring(16) = 0000000000000000 ICC = Integrated Circuit Card + // hashing algorithm x2 = 09 09 + // randomICC = 0000000000000000 ICC = Integrated Circuit Card + // RandomIFD = 0000000000000000 IFD = Interface Device + // Will always be 38 bytes + // + // 00000000 E0EC1F2D7B000000 F0EC1F2D7B000000 09 09 B0EC1F2D7B000000B8EC1F2D7B000000 + + uint8_t prefix[4] = {0x00,0x00,0x00,0x00}; + uint8_t aHashingAlgorithm[2] = {encryptionAlgorithm,encryptionAlgorithm}; + uint8_t hash_in[38]; + + memcpy(hash_in, prefix, 4); + memcpy(hash_in + 4, KEYIFD, 8); + memcpy(hash_in + 12, KEYICC, 8); + memcpy(hash_in + 20, aHashingAlgorithm, 2); + memcpy(hash_in + 22, RNDICC, 8); + memcpy(hash_in + 30, RNDIFD, 8); + + // PrintAndLogEx(INFO, "--- " _CYAN_("Mutual Auth Keys") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Prefix........................... " _YELLOW_("%s"), sprint_hex_inrow(prefix, ARRAYLEN(prefix))); + // PrintAndLogEx(SUCCESS, "KeyIFD........................... " _YELLOW_("%s"), sprint_hex_inrow(KEYIFD, 8)); + // PrintAndLogEx(SUCCESS, "KeyICC........................... " _YELLOW_("%s"), sprint_hex_inrow(KEYICC, 8)); + // PrintAndLogEx(SUCCESS, "HashingAlgo...................... " _YELLOW_("%s"), sprint_hex_inrow(aHashingAlgorithm, ARRAYLEN(aHashingAlgorithm))); + // PrintAndLogEx(SUCCESS, "RandomICC........................ " _YELLOW_("%s"), sprint_hex_inrow(RNDICC, 8)); + // PrintAndLogEx(SUCCESS, "RandomIFD........................ " _YELLOW_("%s"), sprint_hex_inrow(RNDIFD, 8)); + // PrintAndLogEx(SUCCESS, "hash Input....................... " _YELLOW_("%s"), sprint_hex_inrow(hash_in,ARRAYLEN(hash_in))); + + uint8_t output[128]; // Buffer to store the two 32-byte keys + uint8_t hashedOutput[128]; + uint32_t counter = 1; + + // Generate the first key + set_counter_big_endian(hash_in, counter); // Set the counter in big-endian format + // PrintAndLogEx(SUCCESS, "key_out_temp..................... " _YELLOW_("%s"), sprint_hex_inrow(hash_in,ARRAYLEN(hash_in))); + + if (HashingAlgorithm == 0x06) { + sha1hash(hash_in, sizeof(hash_in), hashedOutput); + //PrintAndLogEx(SUCCESS, "key_out_temp..................... " _YELLOW_("%s"), sprint_hex_inrow(hash_in,ARRAYLEN(hash_in))); + memcpy(output, hashedOutput, 20); + counter++; + set_counter_big_endian(hash_in, counter); + sha1hash(hash_in, sizeof(hash_in), hashedOutput); + memcpy(output + 20, hashedOutput, 20); + //PrintAndLogEx(SUCCESS, "key_out_temp..................... " _YELLOW_("%s"), sprint_hex_inrow(hash_in,ARRAYLEN(hash_in))); + } + else if (HashingAlgorithm == 0x07) { + sha256hash(hash_in, sizeof(hash_in), hashedOutput); + memcpy(output, hashedOutput, 32); + } + else { + // Yes they generate their encryption keys and mac keys in a weird way for no fucking reason, the 2nd cycle isn't required. + PrintAndLogEx(ERR, _RED_("Unknown Hashing Algorithm")); + return; + } + + + memcpy(EncryptionKey, output, 16); + memcpy(MACKey, output + 16, 16); + + + // PrintAndLogEx(INFO, "--- " _CYAN_("New Key Generation") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Hash Output...................... " _YELLOW_("%s"), sprint_hex_inrow(output,ARRAYLEN(output))); + // PrintAndLogEx(SUCCESS, "Encryption Key................... " _YELLOW_("%s"), sprint_hex_inrow(EncryptionKey, 16)); + // PrintAndLogEx(SUCCESS, "MAC Key.......................... " _YELLOW_("%s"), sprint_hex_inrow(MACKey, 16)); +} + +static int seos_challenge_get(uint8_t* RNDICC, uint8_t RNDICC_len) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + bool activate_field = false; + bool keep_field_on = true; + + // The Get Challenge seems to be static across all tested cards + // 00870001047c02810000 + + char getChallengePre[21]; + strcpy(getChallengePre, "008700"); + const char keyslot_str[3] = "01"; + //snprintf(keyslot_str, sizeof(keyslot_str), "%02X", keyslot); + strcat(getChallengePre, keyslot_str); + strcat(getChallengePre, "047c02810000"); + + uint8_t aGET_CHALLENGE[12]; + int aGET_CHALLENGE_n = 0; + param_gethex_to_eol(getChallengePre, 0, aGET_CHALLENGE, sizeof(aGET_CHALLENGE), &aGET_CHALLENGE_n); + int res = ExchangeAPDU14a(aGET_CHALLENGE, aGET_CHALLENGE_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); return res; } - if (resplen < 2) { + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Get Challenge Failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + memcpy(RNDICC, &response[4], 8); + + // Response looks like + // 7C0A810897460AC7535731F8 + // |----------|------------------| + // | 7C0A8108 | 97460AC7535731F8 | + // | Static | RND.ICC | + // |----------|------------------| + // 7C 0A 81 08 18 1E 43 80 10 10 20 11 + // 81 is the ASN.1 tag + + uint8_t staticResponse[8] = {0x01,0x81,0xE4,0x38,0x01,0x01,0x02,0x01}; + + PrintAndLogEx(INFO, "--- " _CYAN_("Get Challenge") " ---------------------------"); + //PrintAndLogEx(SUCCESS, "Challenge Input: " _YELLOW_("%s"), getChallengePre); + if (memcmp(RNDICC, staticResponse, 8) == 0) { + PrintAndLogEx(SUCCESS, "Static Response Detected......... " _GREEN_("%s"), sprint_hex_inrow(RNDICC, sizeof(RNDICC))); + } else { + PrintAndLogEx(SUCCESS, "RND.ICC.......................... " _YELLOW_("%s"), sprint_hex_inrow(RNDICC, sizeof(RNDICC))); + } + + return PM3_SUCCESS; +}; + +int seos_kdf(bool encryption, uint8_t* masterKey, uint8_t keyslot, + 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 + // KEK Encryption key = 05 + // MAC key = 06 + // KEK MAC key = 07 + + uint8_t typeOfKey = 0x06; + if (encryption == true) { + 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); + + // PrintAndLogEx(SUCCESS, "adfOid: " _YELLOW_("%s"), sprint_hex_inrow(adfOid, 16)); + // PrintAndLogEx(SUCCESS, "diversifier: " _YELLOW_("%s"), sprint_hex_inrow(diversifier, 7)); + // PrintAndLogEx(SUCCESS, "Input: " _YELLOW_("%s"), sprint_hex_inrow(input, (sizeof(input)))); + + // ----------------- AES Key Generation ----------------- + uint8_t AES_iv[16] = {}; + + aes_cmac(AES_iv, masterKey, input, out, sizeof(input)); + return PM3_SUCCESS; +}; + +static int select_DF_verify(uint8_t* response, uint8_t response_length, uint8_t* MAC_value, size_t MAC_value_len, int encryption_algorithm, int key_index) { + uint8_t input[response_length - 10]; + // Response is an ASN.1 encoded structure + // Extract everything before the 8E tag + for (int i = 0; i < response_length; i++) { + // extract MAC + if (response[i] == 0x8E) { + memcpy(input, response, i); + memcpy(MAC_value, response + (i + 2), MAC_value_len); + } + } + + // ----------------- MAC Key Generation ----------------- + uint8_t cmac[8]; + uint8_t MAC_key[24] = {0x00}; + memcpy(MAC_key, keys[key_index].privMacKey, 16); + create_cmac(MAC_key, input, cmac, sizeof(input), encryption_algorithm); + + // PrintAndLogEx(INFO, "--- " _CYAN_("MAC") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "MAC Key: "_YELLOW_("%s"), sprint_hex_inrow(MAC_key,sizeof(MAC_key))); + // PrintAndLogEx(SUCCESS, "Message: " _YELLOW_("%s"), sprint_hex_inrow(input,sizeof(input))); + + if (memcmp(cmac, MAC_value, MAC_value_len) == 0) { + // PrintAndLogEx(SUCCESS, _GREEN_("MAC Verification successful")); + return PM3_SUCCESS; + } + // PrintAndLogEx(INFO, "MAC Type......................... " _YELLOW_("%s"), algorithm_name1); + // PrintAndLogEx(INFO, "Supp MAC......................... " _YELLOW_("%s"), sprint_hex_inrow(MAC_value, MAC_value_len)); + // PrintAndLogEx(INFO, "Calc MAC......................... " _YELLOW_("%s"), sprint_hex_inrow(cmac, sizeof(cmac))); + PrintAndLogEx(INFO, "--- " _CYAN_("MAC") " ---------------------------"); + PrintAndLogEx(ERR, _RED_("MAC Verification Failed")); + + return PM3_ESOFT; +} + +static int select_df_decode(uint8_t* response, uint8_t response_length, int* ALGORITHM_INFO_value1, int* ALGORITHM_INFO_value2, uint8_t* CRYPTOGRAM_encrypted_data, uint8_t* MAC_value) { + // Response is an ASN.1 encoded structure + // ASN.1 Information + // CF = Diversifier + // + // CD = ALGORITHM_INFO + // 02 = 3DES 2K (TWO_KEY_3DES_CBC_MODE) + // 04 = 3K3DES (THREE_KEY_3DES_CBC_MODE) + // 06 = SHA-1 (hash assigned to RSA-1024) + // 07 = SHA-256 + // 09 = AES-128 (AES_128_CBC) + // 85 = CRYPTOGRAM + // First 16 bytes what I guess is a nonce + // Followed with the ADF selected, then the diversifier + // 8E = MAC + + /* + [+] Raw ADF Response: CD0209078540EAA1E1966D666D1FBA14098700071D1DEE24B74CAC87D182EF1700B9946D697E60F87B0FB703C12AE0F83F579A9BF4888DF6B7691BBA6A404C797356F8457E488E088149C86A535EF86A + [=] -- CD [02] 'elem' + [=] 00: 09 07 | .. + [=] -- 85 [40] 'elem' + [=] 00: EA A1 E1 96 6D 66 6D 1F BA 14 09 87 00 07 1D 1D | ....mfm......... + [=] 10: EE 24 B7 4C AC 87 D1 82 EF 17 00 B9 94 6D 69 7E | .$.L.........mi~ + [=] 20: 60 F8 7B 0F B7 03 C1 2A E0 F8 3F 57 9A 9B F4 88 | `.{....*..?W.... + [=] 30: 8D F6 B7 69 1B BA 6A 40 4C 79 73 56 F8 45 7E 48 | ...i..j@LysV.E~H + [=] -- 8E [08] 'elem' + [=] 00: 81 49 C8 6A 53 5E F8 6A | .I.jS^.j + */ + int ALGORITHM_INFO_value1_n = 0; + int ALGORITHM_INFO_value2_n = 0; + int bufferPoint = 0; + + for (int i = 0; i < response_length; i++) { + // ALGORITHM_INFO + if (response[i] == 0xCD) { + *ALGORITHM_INFO_value1 = (int)response[i + 2]; + ALGORITHM_INFO_value1_n = response[i + 2]; + *ALGORITHM_INFO_value2 = (int)response[i + 3]; + ALGORITHM_INFO_value2_n = response[i + 3]; + bufferPoint = i + (i + 1); + break; + } + } + + for (int i = bufferPoint ; i < response_length; i++) { + // CRYPTOGRAM + if (response[i] == 0x85) { + memcpy(CRYPTOGRAM_encrypted_data, &response[i + 2], 64); + bufferPoint = i + (i + 1); + break; + } + } + + for (int i = bufferPoint; i < response_length; i++) { + // MAC + if (response[i] == 0x8E) { + memcpy(MAC_value, &response[i + 2], 8); + } + } + + const char* algorithm_name1 = NULL; + for (int i = 0; i < ARRAYLEN(known_algorithm_map); i++) { + if ((known_algorithm_map[i].value) == ALGORITHM_INFO_value1_n) { + algorithm_name1 = known_algorithm_map[i].name; + break; + } + } + + const char* algorithm_name2 = NULL; + for (int i = 0; i < ARRAYLEN(known_algorithm_map); i++) { + if (known_algorithm_map[i].value == ALGORITHM_INFO_value2_n) { + algorithm_name2 = known_algorithm_map[i].name; + break; + } + } + + PrintAndLogEx(INFO, "--- " _CYAN_("Raw ADF Information") " ---------------------------"); + if (algorithm_name1 != NULL) { + PrintAndLogEx(SUCCESS, "algoIdCipher (Encryption)........ "_YELLOW_("%i (%s)"), ALGORITHM_INFO_value1_n, algorithm_name1); + } else { + PrintAndLogEx(ERR, "algoIdCipher (Encryption)........ %d (Unknown)", ALGORITHM_INFO_value1_n); + } + + if (algorithm_name2 != NULL) { + PrintAndLogEx(SUCCESS, "algoIdHash (MAC)................. "_YELLOW_("%i (%s)"),ALGORITHM_INFO_value2_n, algorithm_name2); + } else { + PrintAndLogEx(ERR, "algoIdHash (MAC)............... %d (Unknown)", ALGORITHM_INFO_value2_n); + } + + // PrintAndLogEx(SUCCESS, "Raw Data......................... " _YELLOW_("%s"), sprint_hex_inrow(response, 80)); + PrintAndLogEx(SUCCESS, "CRYPTOGRAM Encrypted Data........ " _YELLOW_("%s"), sprint_hex_inrow(CRYPTOGRAM_encrypted_data, 64)); + // PrintAndLogEx(SUCCESS, "MAC.............................. " _YELLOW_("%s"), sprint_hex_inrow(MAC_value, 8)); + + return PM3_SUCCESS; +} + +static int select_ADF_decrypt(const char* selectADFOID, uint8_t* CRYPTOGRAM_encrypted_data_raw, uint8_t* CRYPTOGRAM_Diversifier, int encryption_algorithm, int key_index) { + // --------------- Decrypt ---------------- + + // 1. MAC Verify - AES/CBC-decrypt (IV || cryptogram || 16 bytes after 8e 08) with the MAC key & keep the last block + // 2. Decrypt the CRYPTOGRAM_encrypted_data - AES/CBC-decrypt with the encryption key & IV (the previous 16 bytes) + // 3. Verify the Decryption + // 3.1 - CF tag for diversifier at 44 chars in + // 4. Extract the data + // 4.1 Selected ADF + // 4.2 Diversifier + // 4.3 Nonce + uint8_t privEncKey[16] = {}; + memcpy(privEncKey, keys[key_index].privEncKey, 16); + uint8_t CRYPTOGRAM_decrypted_data[64]; + + decrypt_cryptogram(privEncKey, CRYPTOGRAM_encrypted_data_raw, CRYPTOGRAM_decrypted_data, ARRAYLEN(CRYPTOGRAM_decrypted_data), encryption_algorithm); + + // PrintAndLogEx(SUCCESS, "CRYPTOGRAM_encrypted_data_raw: " _YELLOW_("%s"), sprint_hex_inrow(CRYPTOGRAM_encrypted_data_raw, 64)); + // PrintAndLogEx(SUCCESS, "Raw Decrypted Data............... "_YELLOW_("%s"), sprint_hex_inrow(CRYPTOGRAM_decrypted_data,sizeof(CRYPTOGRAM_decrypted_data))); + + // Rough Output + // 06112B0601040181E438010102011801010202 CF 07 EFB08A28B0529F 5282752803B485BABF8CD88F3DA5515DF7712CF3 + + + // Extract the data + int diversifier_length = 0; + int adf_length = 0; + + int CRYPTOGRAM_decrypted_data_length = sizeof(CRYPTOGRAM_decrypted_data); + + for (int i = 0; i < CRYPTOGRAM_decrypted_data_length; i++) { + // ADF OID tag + if (CRYPTOGRAM_decrypted_data[i] == 0x06 && CRYPTOGRAM_decrypted_data[i + 1] < 20) { + adf_length = ((CRYPTOGRAM_decrypted_data[i + 1])); + diversifier_length = CRYPTOGRAM_decrypted_data[i + adf_length + 3]; + + uint8_t CRYPTOGRAM_ADF[strlen(selectADFOID)/2]; + + memcpy(CRYPTOGRAM_ADF, &CRYPTOGRAM_decrypted_data[i], strlen(selectADFOID)/2); + memcpy(CRYPTOGRAM_Diversifier, &CRYPTOGRAM_decrypted_data[i + adf_length + 4], diversifier_length); + + const char* CRYPTOGRAM_ADF_CMP = (sprint_hex_inrow(CRYPTOGRAM_ADF,ARRAYLEN(CRYPTOGRAM_ADF))); + + char* CRYPTOGRAM_ADF_UPPER = strdup(CRYPTOGRAM_ADF_CMP); + char* selectADFOID_UPPER = strdup(selectADFOID); + + // Convert both strings to uppercase + for (int x = 0; CRYPTOGRAM_ADF_UPPER[x]; x++) { + CRYPTOGRAM_ADF_UPPER[x] = toupper(CRYPTOGRAM_ADF_UPPER[x]); + } + for (int x = 0; selectADFOID_UPPER[x]; x++) { + selectADFOID_UPPER[x] = toupper(selectADFOID_UPPER[x]); + } + + + // Compare the 2 ADF responses, if they don't match then the decryption is wrong + // We do the + 4 to remove the first 4 bytes of the ADF OID ASN.1 Tag (0611) + if (strcmp(CRYPTOGRAM_ADF_UPPER + 4, selectADFOID_UPPER + 4) != 0) { + PrintAndLogEx(ERR, "ADF does not match decrypted ADF"); + PrintAndLogEx(ERR, "Likely wrong Key or IV"); + // PrintAndLogEx(SUCCESS, "Decoded ADF....................... "_YELLOW_("%s"), CRYPTOGRAM_ADF_UPPER); // ADF Selected + // PrintAndLogEx(SUCCESS, "Supplied ADF...................... "_YELLOW_("%s"), selectADFOID_UPPER); // ADF Selected + return PM3_ESOFT; + } + + // PrintAndLogEx(INFO, "--- " _CYAN_("Decrypted Response") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Decoded ADF...................... "_YELLOW_("%s"), sprint_hex_inrow(&CRYPTOGRAM_ADF[2],adf_length)); // ADF Selected + // PrintAndLogEx(SUCCESS, "Diversifier...................... "_YELLOW_("%s"), sprint_hex_inrow(CRYPTOGRAM_Diversifier,diversifier_length)); // ADF Diversifier + return PM3_SUCCESS; + + } + } + return PM3_SUCCESS; +}; + +static int seos_mutual_auth(uint8_t* randomICC, uint8_t* CRYPTOGRAM_Diversifier, uint8_t diversifier_len, uint8_t* mutual_auth_randomIFD, uint8_t* mutual_auth_keyICC, uint8_t* randomIFD, uint8_t randomIFD_len, uint8_t* keyIFD, uint8_t keyIFD_len, int encryption_algorithm, int hash_algorithm, int key_index) { + uint8_t response[PM3_CMD_DATA_SIZE]; + + // ---------------- Diversify Keys ---------------- + uint8_t undiversified_key[16] = { 0x00 }; + memcpy(undiversified_key, keys[key_index].readKey, 16); + + uint8_t keyslot = 0x01; // up to 0x0F + uint8_t AES_key[24] = {0x00}; + uint8_t MAC_key[24] = {0x00}; + uint8_t adfOID[17] = {0x2b,0x06,0x01,0x04,0x01,0x81,0xe4,0x38,0x01,0x01,0x02,0x01,0x18,0x01,0x01,0x02,0x02}; + + // Null AES IV + uint8_t nullDiversifier[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + if (memcmp(CRYPTOGRAM_Diversifier, nullDiversifier, sizeof(nullDiversifier)) == 0) { + PrintAndLogEx(ERR, "No Diversifier found"); + return PM3_ESOFT; + } + + seos_kdf(true, undiversified_key, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, AES_key, encryption_algorithm, hash_algorithm); + seos_kdf(false, undiversified_key, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, MAC_key, encryption_algorithm, hash_algorithm); + + memcpy(&MAC_key[16], &MAC_key[0], 8); + memcpy(&AES_key[16], &AES_key[0], 8); + + // PrintAndLogEx(INFO, "--- " _CYAN_("Diversified Keys") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Diversified Enc Key.............. " _YELLOW_("%s"), sprint_hex_inrow(AES_key, (sizeof(AES_key)))); + // PrintAndLogEx(SUCCESS, "Diversified Mac Key.............. " _YELLOW_("%s"), sprint_hex_inrow(MAC_key, (sizeof(MAC_key)))); + // PrintAndLogEx(INFO, "--- " _CYAN_("Mutual Auth") " ---------------------------"); + + // ----------------- Command Generation ----------------- + uint8_t mutual_auth_plain[32]; + memcpy(mutual_auth_plain, randomIFD, 8); + memcpy(mutual_auth_plain + 8, randomICC, 8); + memcpy(mutual_auth_plain + 8 + 8, keyIFD, 16); + + // ----------------- Encryption and MAC Generation ----------------- + uint8_t mac[8]; + uint8_t mutual_auth_enc[32]; + create_cryptogram(AES_key, mutual_auth_plain, mutual_auth_enc, sizeof(mutual_auth_plain), encryption_algorithm); + create_cmac(MAC_key, mutual_auth_enc, mac, sizeof(mutual_auth_enc), encryption_algorithm); + + uint8_t message_authenticated[40]; + memcpy(message_authenticated, mutual_auth_enc, sizeof(mutual_auth_enc)); + memcpy(message_authenticated + sizeof(mutual_auth_enc), mac, sizeof(mac)); + + // ----------------- Debugging ----------------- + // PrintAndLogEx(SUCCESS, "AES IV : "_YELLOW_("%s"), sprint_hex_inrow(AES_iv,sizeof(AES_iv))); + // PrintAndLogEx(SUCCESS, "AES Key: "_YELLOW_("%s"), sprint_hex_inrow(AES_key,sizeof(AES_key))); + // PrintAndLogEx(SUCCESS, "mutual_auth_plain... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_plain, sizeof(mutual_auth_plain))); + // PrintAndLogEx(SUCCESS, "mutual_auth_enc..... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_enc, sizeof(mutual_auth_enc))); + + // PrintAndLogEx(INFO, "--- " _CYAN_("MAC") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "AES IV: "_YELLOW_("%s"), sprint_hex_inrow(AES_iv,sizeof(AES_iv))); + // PrintAndLogEx(SUCCESS, "MAC Key: "_YELLOW_("%s"), sprint_hex_inrow(MAC_key,sizeof(MAC_key))); + // PrintAndLogEx(SUCCESS, "Message.......................... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_enc,sizeof(mutual_auth_enc))); + // PrintAndLogEx(SUCCESS, "MAC.............................. " _YELLOW_("%s"), sprint_hex_inrow(mac,sizeof(mac))); + + // ----------------- Command Generation ----------------- + + const char* prefixLenHex = "2c"; + const char* ASN1_tagAboveLenHex = "2a"; + const char* ASN1_auth_encryptedLenHex = "28"; + + const char* mutual_auth_message = sprint_hex_inrow(message_authenticated, sizeof(message_authenticated)); + + char keyslot_str[3]; + snprintf(keyslot_str, sizeof(keyslot_str), "%02X", keyslot); + + const char* prefix = "008700"; + const char* ASN1_tagAbove = "7c"; + const char* ASN1_auth_encrypted = "82"; + const char* suffix = "00"; + + char mutual_auth[102]; + snprintf(mutual_auth, sizeof(mutual_auth), "%s%s%s%s%s%s%s%s%s", prefix, keyslot_str, prefixLenHex, ASN1_tagAbove, ASN1_tagAboveLenHex,ASN1_auth_encrypted, ASN1_auth_encryptedLenHex, mutual_auth_message, suffix); + // PrintAndLogEx(SUCCESS, "Mutual Auth Encrypted Request.... " _YELLOW_("%s"), mutual_auth); + + // BLOCKS MUTUAL AUTH BEFORE REQUIRED + // return PM3_SUCCESS; + // + + int resplen = 0; + bool activate_field = false; + bool keep_field_on = true; + + uint8_t aMUTUAL_AUTH[102]; + int aMUTUAL_AUTH_n = 0; + param_gethex_to_eol(mutual_auth, 0, aMUTUAL_AUTH, sizeof(aMUTUAL_AUTH), &aMUTUAL_AUTH_n); + int res = ExchangeAPDU14a(aMUTUAL_AUTH, aMUTUAL_AUTH_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Mutual Auth Request Failed"); DropField(); return PM3_ESOFT; } uint16_t sw = get_sw(response, resplen); if (sw != ISO7816_OK) { - PrintAndLogEx(ERR, "Selecting SEOS applet aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(ERR, "Mutual Auth Request Failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; } - activate_field = false; - keep_field_on = false; - // --------------- CC file reading ---------------- + // PrintAndLogEx(INFO, "--- " _CYAN_("Get Challenge") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Raw Mutual Auth Response: " _YELLOW_("%s"), sprint_hex_inrow(response, (resplen - 2))); + // PrintAndLogEx(SUCCESS, "Mutual Auth Encrypted Response... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_response, sizeof(mutual_auth_response))); + // PrintAndLogEx(SUCCESS, "Mutual Auth MAC Response: " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_mac_response, sizeof(mutual_auth_mac_response))); + // PrintAndLogEx(SUCCESS, "Mutual Auth MAC Input: " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_validate, sizeof(mutual_auth_validate))); + // PrintAndLogEx(SUCCESS, "Mutual Auth MAC Calculated: " _YELLOW_("%s"), sprint_hex_inrow(mac_calculated, sizeof(mac_calculated))); - uint8_t aSELECT_FILE_ADF[30]; + // Process Response + uint8_t iv[16] = {}; + uint8_t mutual_auth_response[32]; + uint8_t mutual_auth_mac_response[8]; + memcpy(mutual_auth_response, &response[4], 32); + memcpy(mutual_auth_mac_response, &response[4 + 32], 8); + + // PrintAndLogEx(SUCCESS, "Mutual Auth Encrypted Response... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_response, sizeof(mutual_auth_response))); + // PrintAndLogEx(SUCCESS, "Mutual Auth MAC Response: " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_mac_response, sizeof(mutual_auth_mac_response))); + + uint8_t mutual_auth_response_decrypted[32]; + if (encryption_algorithm == 0x09) { + aes_decode(iv, AES_key, mutual_auth_response, mutual_auth_response_decrypted, sizeof(mutual_auth_response)); + } else if (encryption_algorithm == 0x02) { + mbedtls_des3_context ctx3; + mbedtls_des3_set2key_dec(&ctx3, AES_key); + mbedtls_des3_crypt_cbc(&ctx3, MBEDTLS_DES_DECRYPT, sizeof(mutual_auth_response), iv, mutual_auth_response, mutual_auth_response_decrypted); + mbedtls_des3_free(&ctx3); + } + + // Validate response with comparison between nonce and randomICC + uint8_t mutual_auth_RandomICC[8]; + memcpy(mutual_auth_RandomICC, &mutual_auth_response_decrypted, 8); + + // PrintAndLogEx(SUCCESS, "Mutual Auth Decrypted Response... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_response_decrypted, sizeof(mutual_auth_response_decrypted))); + + if (memcmp(randomICC, mutual_auth_RandomICC, 8) != 0) { + PrintAndLogEx(ERR, "RandomICC does not match decrypted RandomICC"); + PrintAndLogEx(ERR, "Likely wrong Key or IV"); + return PM3_ESOFT; + } + + memcpy(mutual_auth_randomIFD, &mutual_auth_response_decrypted[8], 8); + memcpy(mutual_auth_keyICC, &mutual_auth_response_decrypted[16], 16); + + // PrintAndLogEx(SUCCESS, _GREEN_("Mutual Auth Completed")); + // PrintAndLogEx(INFO, "--- " _CYAN_("Decrypted Response") " ---------------------------"); + // PrintAndLogEx(SUCCESS, "Mutual Auth Decrypted Response... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_response_decrypted, sizeof(mutual_auth_response_decrypted))); + // PrintAndLogEx(SUCCESS, "Mutual Auth RandomICC............ " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_RandomICC, sizeof(mutual_auth_RandomICC))); + // PrintAndLogEx(SUCCESS, "Mutual Auth RandomIFD............ " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_randomIFD, sizeof(mutual_auth_randomIFD))); + // PrintAndLogEx(SUCCESS, "Mutual Auth KeyICC............... " _YELLOW_("%s"), sprint_hex_inrow(mutual_auth_keyICC, sizeof(mutual_auth_keyICC))); + + return PM3_SUCCESS; +}; + +static int seos_aid_select(void) { + // Working 100%, pulls from live card + bool activate_field = true; + bool keep_field_on = true; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + // --------------- Select AID for SEOS Card ---------------- + typedef struct { + const char* name; + const char* value; + } seos_aid_t; + + static const seos_aid_t known_AID_map[] = { + {"STANDARD_SEOS", "A00000044000010100010"}, + {"MOBILE_SEOS_ADMIN_CARD", "A000000382002D0001010"}, + }; + + int i; + int res = PM3_ESOFT; + //PrintAndLogEx(INFO, "--- " _CYAN_("AID Selection") " ---------------------------"); + for (i = 0; i < ARRAYLEN(known_AID_map); i++) { + + const char* selectedAID = known_AID_map[i].value; + + // Select command prefixed with a 00 + const char* prefix = "00A404"; + uint16_t aidlen = strlen(selectedAID) >> 1; + + char aidlenHex[5]; + snprintf(aidlenHex, sizeof(aidlenHex), "%04X", aidlen); + + const char* suffix = "0"; + char combinedString[100]; + + snprintf(combinedString, sizeof(combinedString), "%s%s%s%s", prefix, aidlenHex, selectedAID, suffix); + //PrintAndLogEx(SUCCESS, "AID Selected: " _YELLOW_("%s"), known_AID_map[i].name); + //PrintAndLogEx(SUCCESS, "AID Select Command: " _YELLOW_("%s"), combinedString); + + // --------------- Select AID for SEOS Card ---------------- + uint8_t aSELECT_AID[80]; + int aSELECT_AID_n = 0; + param_gethex_to_eol(combinedString, 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + continue; + } + + if (resplen < 2) { + DropField(); + continue; + } + + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Selecting SEOS applet aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + continue; + } + + // if we made it here, its a success and we break :) + break; + } + + return res; +}; + +static int seos_pacs_adf_select(char* oid, int oid_len, uint8_t* get_data, int get_data_len, int key_index) { + int resplen = 0; + uint8_t response[PM3_CMD_DATA_SIZE]; + bool activate_field = false; + bool keep_field_on = true; + + // --------------- ADF file Selection ---------------- + + // breaks down to + // 06 = ASN.1 Tag + // 11 = Len + // 2b0601040181e438010102011801010202 = ADF-OID + + // --------------- OID Selection ---------------- + const char* ADFprefix = "06"; + char selectedOID[100]; + snprintf(selectedOID, sizeof(selectedOID), "%s", oid); + + uint16_t selectedOIDLen = strlen(selectedOID); + char selectedOIDLenHex[3]; + snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen)/2); + + char selectedADF[strlen(ADFprefix) + strlen(selectedOIDLenHex) + selectedOIDLen + 1]; + snprintf(selectedADF, sizeof(selectedADF), "%s%s%s", ADFprefix, selectedOIDLenHex, selectedOID); + + // --------------- Command Builder Selection ---------------- + // prefix is the APDU command we are sending + const char* prefix = "80A504"; + const char* suffix = "00"; + const char* keyReference = "00"; + + uint16_t selectedADFLen = strlen(selectedADF); + + char adflenHex[3]; + snprintf(adflenHex, sizeof(adflenHex), "%02X", (selectedADFLen >> 1) & 0xFF); + + char selectADF[strlen(prefix) + strlen(adflenHex) + selectedADFLen + strlen(suffix) + 1]; + + // 80 A5 04 00 13 06 11 2B 06 01 04 01 81 E4 38 01 01 02 01 18 01 01 02 02 00 + snprintf(selectADF, sizeof(selectADF), "%s%s%s%s%s", prefix, keyReference, adflenHex, selectedADF, suffix); + + + PrintAndLogEx(INFO, "--- " _CYAN_("Select ADF") " ---------------------------"); + PrintAndLogEx(SUCCESS, "Selected ADF..................... "_YELLOW_("%s"), selectedOID); + + // --------------- Send APDU Command ---------------- + + uint8_t aSELECT_FILE_ADF[124]; int aSELECT_FILE_ADF_n = 0; - param_gethex_to_eol("80a504001306112b0601040181e43801010201180101020200", 0, aSELECT_FILE_ADF, sizeof(aSELECT_FILE_ADF), &aSELECT_FILE_ADF_n); - res = ExchangeAPDU14a(aSELECT_FILE_ADF, aSELECT_FILE_ADF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + // Input into getHextoEOL is a Char string + param_gethex_to_eol(selectADF, 0, aSELECT_FILE_ADF, sizeof(aSELECT_FILE_ADF), &aSELECT_FILE_ADF_n); + int res = ExchangeAPDU14a(aSELECT_FILE_ADF, aSELECT_FILE_ADF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); return res; } - sw = get_sw(response, resplen); + uint16_t sw = get_sw(response, resplen); if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting ADF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; } - // remove the 2byte SW - asn1_print(response, resplen - 2, " "); + // --------------- Decrypt ADF Response ---------------- + // Information returned from the GetChallenge command + int ALGORITHM_INFO_value1 = 0; // Encryption Algorithm + int ALGORITHM_INFO_value2 = 0; // Hash Algorithm + uint8_t CRYPTOGRAM_encrypted_data[64]; // Encrypted Data + uint8_t MAC_value[8] = {0}; // MAC Value + + uint8_t diversifier[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t RNDICC[8] = {0}; + uint8_t KeyICC[16] = {0}; + uint8_t RNDIFD[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t KeyIFD[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + uint8_t Diversified_New_EncryptionKey[24] = {0}; + uint8_t Diversified_New_MACKey[24] = {0}; + + resplen -= 2; + + seos_challenge_get(RNDICC, sizeof(RNDICC)); + select_df_decode(response, resplen, &ALGORITHM_INFO_value1, &ALGORITHM_INFO_value2, CRYPTOGRAM_encrypted_data, MAC_value); + res = select_DF_verify(response, resplen, MAC_value, sizeof(MAC_value), ALGORITHM_INFO_value1, key_index); + + if (res != PM3_SUCCESS) { + return res; + } + + if (ALGORITHM_INFO_value1 == 0x09 || ALGORITHM_INFO_value1 == 0x02) { + + select_ADF_decrypt(selectedADF, CRYPTOGRAM_encrypted_data, diversifier, ALGORITHM_INFO_value1, key_index); + seos_mutual_auth(RNDICC, diversifier, sizeof(diversifier), RNDIFD, KeyICC, RNDIFD, sizeof(RNDIFD), KeyIFD, sizeof(KeyIFD), ALGORITHM_INFO_value1, ALGORITHM_INFO_value2, key_index); + create_mutual_auth_key(KeyIFD, KeyICC, RNDICC, RNDIFD, Diversified_New_EncryptionKey, Diversified_New_MACKey, ALGORITHM_INFO_value1, ALGORITHM_INFO_value2); + + uint8_t sio_buffer_out[PM3_CMD_DATA_SIZE]; + int sio_size = 0; + seos_get_data(RNDICC, RNDIFD, Diversified_New_EncryptionKey, Diversified_New_MACKey, sio_buffer_out, &sio_size, ALGORITHM_INFO_value1, get_data, get_data_len); + + if (sio_size == 0) { + return PM3_ESOFT; + } + + if (sio_buffer_out[0] == 0x30) { + uint8_t sioOutput[sio_size]; + memcpy(sioOutput, sio_buffer_out, sio_size); + + PrintAndLogEx(INFO, "--- " _CYAN_("Key Data") " ---------------------------"); + PrintAndLogEx(SUCCESS, "SIO.............................. "_YELLOW_("%s"), sprint_hex_inrow(sioOutput, sizeof(sioOutput))); // SIO + PrintAndLogEx(SUCCESS, "SIO Size......................... "_YELLOW_("%i"), sio_size); // SIO Size + PrintAndLogEx(SUCCESS, "Diversifier...................... "_YELLOW_("%s"), sprint_hex_inrow(diversifier,ARRAYLEN(diversifier))); // Diversifier + }; + + } else { + PrintAndLogEx(ERR, "Unknown encryption algorithm"); + return PM3_ESOFT; + }; + + return PM3_SUCCESS; +}; + +static int seos_adf_select(char* oid, int oid_len, int key_index) { + int resplen = 0; + uint8_t response[PM3_CMD_DATA_SIZE]; + bool activate_field = false; + bool keep_field_on = true; + + // --------------- OID Selection ---------------- + const char* ADFprefix = "06"; + char selectedOID[100]; + snprintf(selectedOID, sizeof(selectedOID), "%s", oid); + + uint16_t selectedOIDLen = strlen(selectedOID); + char selectedOIDLenHex[3]; + snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen)/2); + + char selectedADF[strlen(ADFprefix) + strlen(selectedOIDLenHex) + selectedOIDLen + 1]; + snprintf(selectedADF, sizeof(selectedADF), "%s%s%s", ADFprefix, selectedOIDLenHex, selectedOID); + + // --------------- Command Builder Selection ---------------- + // prefix is the APDU command we are sending + const char* prefix = "80A504"; + const char* suffix = "00"; + const char* keyReference = "00"; + + uint16_t selectedADFLen = strlen(selectedADF); + char adflenHex[3]; + snprintf(adflenHex, sizeof(adflenHex), "%02X", (selectedADFLen >> 1) & 0xFF); + char selectADF[strlen(prefix) + strlen(adflenHex) + selectedADFLen + strlen(suffix) + 1]; + + // 80 A5 04 00 13 06 11 2B 06 01 04 01 81 E4 38 01 01 02 01 18 01 01 02 02 00 + snprintf(selectADF, sizeof(selectADF), "%s%s%s%s%s", prefix, keyReference, adflenHex, selectedADF, suffix); + PrintAndLogEx(INFO, "--- " _CYAN_("Select ADF") " ---------------------------"); + PrintAndLogEx(SUCCESS, "Selected ADF..................... "_YELLOW_("%s"), selectedADF); + + // --------------- Send APDU Command ---------------- + uint8_t aSELECT_FILE_ADF[124]; + int aSELECT_FILE_ADF_n = 0; + + // Input into getHextoEOL is a Char string + param_gethex_to_eol(selectADF, 0, aSELECT_FILE_ADF, sizeof(aSELECT_FILE_ADF), &aSELECT_FILE_ADF_n); + + int res = ExchangeAPDU14a(aSELECT_FILE_ADF, aSELECT_FILE_ADF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Selecting ADF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + + // --------------- Decrypt ADF Response ---------------- + // Information returned from the GetChallenge command + int ALGORITHM_INFO_value1 = 0; // Encryption Algorithm + int ALGORITHM_INFO_value2 = 0; // Hash Algorithm + uint8_t CRYPTOGRAM_encrypted_data[64] = {0}; // Encrypted Data + uint8_t MAC_value[8] = {0}; // MAC Value + uint8_t RNDICC[8] = {0}; + + resplen -= 2; + + seos_challenge_get(RNDICC, sizeof(RNDICC)); + select_df_decode(response, resplen, &ALGORITHM_INFO_value1, &ALGORITHM_INFO_value2, CRYPTOGRAM_encrypted_data, MAC_value); + select_DF_verify(response, resplen, MAC_value, sizeof(MAC_value), ALGORITHM_INFO_value1, key_index); + return PM3_SUCCESS; +}; + +static int seos_gdf_select(int key_index) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + bool activate_field = false; + bool keep_field_on = true; + // --------------- Select Global_df for SEOS Card ---------------- + // SelectGDF = 00A507 + referenceDataQualifier + 00 + // 00A5070600 + // SelectGlobalDF = 00A50000 + + const char* getGDF = "00A5070600"; + + uint8_t agetGDF[10]; + int agetGDF_n = 0; + param_gethex_to_eol(getGDF, 0, agetGDF, sizeof(agetGDF), &agetGDF_n); + int res = ExchangeAPDU14a(agetGDF, agetGDF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "Get Global_df failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + + // --------------- Decrypt GDF Response ---------------- + // Information returned from the GetChallenge command + int ALGORITHM_INFO_value1 = 0; // Encryption Algorithm + int ALGORITHM_INFO_value2 = 0; // Hash Algorithm + uint8_t CRYPTOGRAM_encrypted_data[64] = {0}; // Encrypted Data + uint8_t MAC_value[8] = {0}; // MAC Value + uint8_t RNDICC[8] = {0}; + + seos_challenge_get(RNDICC, sizeof(RNDICC)); + select_df_decode(response, (resplen - 2), &ALGORITHM_INFO_value1, &ALGORITHM_INFO_value2, CRYPTOGRAM_encrypted_data, MAC_value); + select_DF_verify(response, resplen, MAC_value, sizeof(MAC_value), ALGORITHM_INFO_value1, key_index); + + return PM3_SUCCESS; +}; + +static int seos_select(void) { + int res = seos_aid_select(); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + const char* oid = "2B0601040181E438010102011801010202"; + int oid_len = strlen(oid); + res = seos_adf_select((char*)oid, oid_len, 0); + DropField(); + return res; +} + +static int seos_pacs(char* oid, int oid_len, uint8_t* get_data, int get_data_len, int key_index) { + int res = seos_aid_select(); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + res = seos_pacs_adf_select(oid, oid_len, get_data, get_data_len, key_index); + DropField(); + return res; +} + +static int seos_global_df(int key_index) { + int res = seos_aid_select(); + if (res == PM3_SUCCESS) { + res = seos_gdf_select(key_index); + } + DropField(); + return res; +} + +static int seos_print_keys(bool verbose) { + PrintAndLogEx(NORMAL, ""); + if (verbose) { + for (int i = 0; i < ARRAYLEN(keys); i++) { + PrintAndLogEx(INFO, "Key Index........................ " _YELLOW_("%u"), i); + PrintAndLogEx(INFO, "Nonce............................ " _YELLOW_("%s"), sprint_hex(keys[i].nonce, 8)); + PrintAndLogEx(INFO, "Privacy Encryption Key........... " _YELLOW_("%s"), sprint_hex(keys[i].privEncKey, 16)); + PrintAndLogEx(INFO, "Privacy MAC Key.................. " _YELLOW_("%s"), sprint_hex(keys[i].privMacKey, 16)); + PrintAndLogEx(INFO, "Read Key......................... " _YELLOW_("%s"), sprint_hex(keys[i].readKey, 16)); + PrintAndLogEx(INFO, "Write Key........................ " _YELLOW_("%s"), sprint_hex(keys[i].writeKey, 16)); + PrintAndLogEx(INFO, "Admin Key........................ " _YELLOW_("%s"), sprint_hex(keys[i].adminKey, 16)); + PrintAndLogEx(INFO, "----------------------------"); + } + } + else { + PrintAndLogEx(INFO, "idx| key"); + PrintAndLogEx(INFO, "---+------------------------"); + for (uint8_t i = 0; i < ARRAYLEN(keys); i++) { + if (memcmp(keys[i].privEncKey, zeros, sizeof(zeros)) == 0) + PrintAndLogEx(INFO, " %u |", i); + else + PrintAndLogEx(INFO, " %u | " _YELLOW_("%s"), i, sprint_hex(keys[i].nonce, 8)); + } + PrintAndLogEx(INFO, "---+------------------------"); + }; + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +static int seos_load_keys(char *filename) { + uint8_t *dump = NULL; + size_t bytes_read = 0; + if (loadFile_safe(filename, "", (void **)&dump, &bytes_read) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); + return PM3_EFILE; + } + + // 16 = max line size + // 8 = 8 items per keyset + // 4 = 4 keysets + if (bytes_read > 382) { + PrintAndLogEx(WARNING, "File is too long to load - exp: %zu got: %zu", sizeof(keys), bytes_read); + free(dump); + return PM3_EFILE; + } + + size_t kn = sizeof(keyset_t); + + size_t i = 0; + for (; i < bytes_read / kn; i++) { + memcpy(keys[i].nonce, dump + (i * kn), 8); + memcpy(keys[i].privEncKey, dump + ((i * kn) + 8), 16); + memcpy(keys[i].privMacKey, dump + ((i * kn) + 24), 16); + memcpy(keys[i].readKey, dump + ((i * kn) + 40), 16); + memcpy(keys[i].writeKey, dump + ((i * kn) + 56), 16); + memcpy(keys[i].adminKey, dump + ((i * kn) + 72), 16); + } + + free(dump); + PrintAndLogEx(SUCCESS, "Loaded" _GREEN_("%2zd") " keys from %s", i, filename); return PM3_SUCCESS; } int infoSeos(bool verbose) { - int res = seos_select(); - if (res == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - } - return PM3_SUCCESS; + return seos_select(); } static int CmdHfSeosInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf seos info", - "Get info from SEOS tags", - "hf seos info"); - + "Requests the unauthenticated information from the default ADF of a SEOS card\n" + "- If the card is a SEOS card\n" + "- Are static RND.ICC keys used (can detect SEOS default keyset)\n" + "- What encryption and hashing algorithm is use\n", + "hf seos info" + ); void *argtable[] = { arg_param_begin, arg_param_end @@ -111,14 +1329,321 @@ static int CmdHfSeosInfo(const char *Cmd) { return infoSeos(true); } +static int CmdHfSeosGDF(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos gdf", + "Get Global Data File (GDF) from SEOS card\n\n" + "By default:\n" + " - Key Index: 0\n", + "hf seos gdf" + "hf seos gdf --ki 0" + ); + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int key_index = arg_get_int_def(ctx, 1, -1); + + CLIParserFree(ctx); + return seos_global_df(key_index); +} + +static int CmdHfSeosPACS(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos pacs", + "Make a GET DATA request to an ADF of a SEOS card\n\n" + "By default:\n" + " - ADF OID : 2B0601040181E438010102011801010202\n" + " - Key Index: 0\n", + "hf seos pacs\n" + "hf seos pacs --ki 1\n" + "hf seos pacs -o 2B0601040181E438010102011801010202 --ki 0\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("o", "oid", "", "<0-100> hex bytes for OID (Default: 2B0601040181E438010102011801010202)"), + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int get_data_len = 4; + uint8_t get_data[] = {0x5c,0x02,0xff,0x00}; + + int oid_len = 0; + uint8_t oid_hex[256] = {0x2B,0x06,0x01,0x04,0x01,0x81,0xE4,0x38,0x01,0x01,0x02,0x01,0x18,0x01,0x01,0x02}; + CLIGetHexWithReturn(ctx, 1, oid_hex, &oid_len); + + int key_index = arg_get_int_def(ctx, 2, 0); + + CLIParserFree(ctx); + + // Fall back to default OID + if (oid_len == 0) { + oid_len = 16; + } + + // convert OID hex to literal string + + char oid_buffer[256] = ""; + for (int i = 0; i < oid_len; i++) { + sprintf(oid_buffer + (i * 2), "%02X", oid_hex[i]); + } + + const char* oid = oid_buffer; + + if (oid_len == 0) { + PrintAndLogEx(ERR, "OID value must be supplied"); + return PM3_ESOFT; + } + + return seos_pacs((char*)oid, oid_len, get_data, get_data_len, key_index); +} + +static int CmdHfSeosADF(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos adf", + "Make a GET DATA request to an Application Data File (ADF) of a SEOS Tag\n" + "The ADF is meant to be read by an application\n" + "You still need the valid authentication keys to read a card\n\n" + "By default:\n" + " - ADF OID : 2B0601040181E438010102011801010202\n" + " - Key Index: 0\n" + " - Tag List : 5c02ff00\n", + "hf seos adf\n" + "hf seos adf -o 2B0601040181E438010102011801010202\n" + "hf seos adf -o 2B0601040181E438010102011801010202 --ki 0\n" + "hf seos adf -o 2B0601040181E438010102011801010202 -c 5c02ff41\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("c", "getdata", "", "<0-100> hex bytes for the tag list to Get Data request (Default: 5c02ff00)"), + arg_str0("o", "oid", "", "<0-100> hex bytes for OID (Default: 2B0601040181E438010102011801010202)"), + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int get_data_len = 0; + uint8_t get_data[256] = {0x5c,0x02,0xff,0x00}; + CLIGetHexWithReturn(ctx, 1, get_data, &get_data_len); + + int oid_len = 0; + uint8_t oid_hex[256] = {0x2B,0x06,0x01,0x04,0x01,0x81,0xE4,0x38,0x01,0x01,0x02,0x01,0x18,0x01,0x01,0x02}; + CLIGetHexWithReturn(ctx, 2, oid_hex, &oid_len); + + int key_index = arg_get_int_def(ctx, 3, 0); + + CLIParserFree(ctx); + + if (get_data_len == 0) { + get_data_len = 4; + } + + // Catching when the OID value is not supplied + if (oid_len == 0) { + oid_len = 16; + } + + // convert OID hex to literal string + char oid_buffer[256] = ""; + for (int i = 0; i < oid_len; i++) { + sprintf(oid_buffer + (i * 2), "%02X", oid_hex[i]); + } + + const char* oid = oid_buffer; + + if (oid_len == 0) { + PrintAndLogEx(ERR, "OID value must be supplied"); + return PM3_ESOFT; + } + + return seos_pacs((char*)oid, oid_len, get_data, get_data_len, key_index); +} + +static int CmdHfSeosManageKeys(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos managekeys", + "Manage SEOS Keys in client memory, keys are required to authenticate with SEOS cards\n", + "hf seos managekeys -p\n" + "hf seos managekeys -p -v\n" + "hf seos managekeys --ki 0 --nonce 0102030405060708 -> Set nonce value at key index 0\n" + "hf seos managekeys --load -f mykeys.bin -p -> load from file and prints keys\n" + "hf seos managekeys --save -f mykeys.bin -> saves keys to file\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), + arg_str0(NULL, "nonce", "", "Nonce value as 8 hex bytes"), + arg_str0(NULL, "privenc", "", "Privacy Encryption key as 16 hex bytes"), + arg_str0(NULL, "privmac", "", "Privacy MAC key as 16 hex bytes"), + arg_str0(NULL, "read", "", "Undiversified Read key as 16 hex bytes"), + arg_str0(NULL, "write", "", "Undiversified Write key as 16 hex bytes"), + arg_str0(NULL, "admin", "", "Undiversified Admin key as 16 hex bytes"), + + arg_str0("f", "file", "", "Specify a filename for load / save operations"), + arg_lit0(NULL, "save", "Save keys in memory to file specified by filename"), + arg_lit0(NULL, "load", "Load keys to memory from file specified by filename"), + + arg_lit0("p", "print", "Print keys loaded into memory"), + arg_lit0("v", "verbose", "verbose (print all key info)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + uint8_t operation = 0; + + uint8_t nonce[8] = {0}; + uint8_t privenc[16] = {0}; + uint8_t privmac[16] = {0}; + uint8_t read[16] = {0}; + uint8_t write[16] = {0}; + uint8_t admin[16] = {0}; + int nonce_len = 0; + int privenc_len = 0; + int privmac_len = 0; + int read_len = 0; + int write_len = 0; + int admin_len = 0; + + int key_index = arg_get_int_def(ctx, 1, -1); + + CLIGetHexWithReturn(ctx, 2, nonce, &nonce_len); + CLIGetHexWithReturn(ctx, 3, privenc, &privenc_len); + CLIGetHexWithReturn(ctx, 4, privmac, &privmac_len); + CLIGetHexWithReturn(ctx, 5, read, &read_len); + CLIGetHexWithReturn(ctx, 6, write, &write_len); + CLIGetHexWithReturn(ctx, 7, admin, &admin_len); + + CLIParamStrToBuf(arg_get_str(ctx, 8), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + if (key_index >= 0) { + operation += 3; + if (key_index < 4) { + if (nonce_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for nonce[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].nonce, 8)); + } + if (privenc_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Priv Enc[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].privEncKey, 16)); + } + if (privmac_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Priv Mac[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].privMacKey, 16)); + } + if (read_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Read Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].readKey, 16)); + } + if (write_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Write Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].writeKey, 16)); + } + if (admin_len != 0) { + PrintAndLogEx(SUCCESS, "Current value for Admin Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].adminKey, 16)); + } + } + else { + PrintAndLogEx(ERR, "Key index is out-of-range"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + if (arg_get_lit(ctx, 9)) { //save + operation += 6; + } + if (arg_get_lit(ctx, 10)) { //load + operation += 5; + } + if (arg_get_lit(ctx, 11)) { //print + operation += 4; + } + + bool verbose = arg_get_lit(ctx, 12); + + CLIParserFree(ctx); + + if (operation == 0) { + PrintAndLogEx(ERR, "No operation specified (load, save, or print)\n"); + return PM3_EINVARG; + } + if (operation > 6) { + PrintAndLogEx(ERR, "Too many operations specified\n"); + return PM3_EINVARG; + } + if (operation > 4 && fnlen == 0) { + PrintAndLogEx(ERR, "You must enter a filename when loading or saving\n"); + return PM3_EINVARG; + } + if (((nonce_len > 0) || (privenc_len > 0) || (privmac_len > 0) || (read_len > 0) || (write_len > 0) || (admin_len > 0)) && key_index == -1) { + PrintAndLogEx(ERR, "Please specify key index when specifying key"); + return PM3_EINVARG; + } + + switch (operation) { + case 3: + if (nonce_len != 0) { + memcpy(keys[key_index].nonce, nonce, 8); + PrintAndLogEx(SUCCESS, "New value for nonce[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].nonce, 8)); + } + if (privenc_len != 0) { + memcpy(keys[key_index].privEncKey, privenc, 16); + PrintAndLogEx(SUCCESS, "New value for Priv Enc[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].privEncKey, 16)); + } + if (privmac_len != 0) { + memcpy(keys[key_index].privMacKey, privmac, 16); + PrintAndLogEx(SUCCESS, "New value for Priv Mac[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].privMacKey, 16)); + } + if (read_len != 0) { + memcpy(keys[key_index].readKey, read, 16); + PrintAndLogEx(SUCCESS, "New value for Read Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].readKey, 16)); + } + if (write_len != 0) { + memcpy(keys[key_index].writeKey, write, 16); + PrintAndLogEx(SUCCESS, "New value for Write Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].writeKey, 16)); + } + if (admin_len != 0) { + memcpy(keys[key_index].adminKey, admin, 16); + PrintAndLogEx(SUCCESS, "New value for Admin Key[%d] " _GREEN_("%s"), key_index, sprint_hex_inrow(keys[key_index].adminKey, 16)); + } + return PM3_SUCCESS; + case 4: + return seos_print_keys(verbose); + case 5: + return seos_load_keys(filename); + case 6: { + bool isOK = saveFile(filename, ".bin", keys, sizeof(keys)); + if (isOK == false) { + return PM3_EFILE; + } + return PM3_SUCCESS; + } + } + + return PM3_SUCCESS; +} + static int CmdHfSeosList(const char *Cmd) { return CmdTraceListAlias(Cmd, "hf seos", "seos -c"); } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"info", CmdHfSeosInfo, IfPm3NfcBarcode, "Tag information"}, - {"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"}, + {"info", CmdHfSeosInfo, IfPm3NfcBarcode, "Tag information"}, + {"pacs", CmdHfSeosPACS, AlwaysAvailable, "Extract PACS Information from card"}, + {"adf", CmdHfSeosADF, AlwaysAvailable, "Read an ADF from the card"}, + {"gdf", CmdHfSeosGDF, AlwaysAvailable, "Read an GDF from card"}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Utils") " -----------------------"}, + {"managekeys", CmdHfSeosManageKeys, AlwaysAvailable, "Manage keys to use with SEOS commands"}, + {NULL, NULL, NULL, NULL} }; @@ -132,3 +1657,4 @@ int CmdHFSeos(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } + diff --git a/client/src/cmdhfseos.h b/client/src/cmdhfseos.h index 32ffb6510..33e6d45f5 100644 --- a/client/src/cmdhfseos.h +++ b/client/src/cmdhfseos.h @@ -23,5 +23,6 @@ int infoSeos(bool verbose); int CmdHFSeos(const char *Cmd); - +int seos_kdf(bool encryption, uint8_t* masterKey, uint8_t keyslot, + uint8_t* adfOid, size_t adfoid_len, uint8_t* diversifier, uint8_t diversifier_len, uint8_t* out, int encryption_algorithm, int hash_algorithm); #endif