diff --git a/client/src/cmdhfntag424.c b/client/src/cmdhfntag424.c index b3b6c47c7..725d2cdc3 100644 --- a/client/src/cmdhfntag424.c +++ b/client/src/cmdhfntag424.c @@ -20,13 +20,16 @@ #include #include "cmdparser.h" #include "commonutil.h" +#include "comms.h" #include "protocols.h" #include "cliparser.h" #include "cmdmain.h" #include "fileutils.h" // saveFile #include "crypto/libpcrypto.h" // aes_decode #include "cmac.h" - +#include "cmdhf14a.h" +#include "util.h" +#include "crc32.h" #define NTAG424_MAX_BYTES 412 @@ -79,83 +82,679 @@ typedef struct sdm_picc_s { uint32_t cnt_int; } sdm_picc_t; -static int sdm_generator(void) { +// -------------- Encryption structs --------------------------- +typedef struct { + uint8_t ti[4]; + uint8_t rnd_a[16]; + uint8_t pd_cap2[6]; + uint8_t pcd_cap2[6]; +} ntag424_ev2_response_t; - // NXP Secure Dynamic Messaging (SDM) with Secure Unique NFC message (SUN) - // Where do they come up with these names? - // - // ref: - // https://www.nxp.com/docs/en/application-note/AN12196.pdf +typedef struct { + uint16_t command_counter; + uint8_t ti[4]; + uint8_t encryption[16]; + uint8_t mac[16]; +} ntag424_session_keys_t; - // SMD / CMAC - uint8_t iv[16] = {0}; - uint8_t aeskey[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // uint8_t enc_txt[16] = {0xEF, 0x96, 0x3F, 0xF7, 0x82, 0x86, 0x58, 0xA5, 0x99, 0xF3, 0x04, 0x15, 0x10, 0x67, 0x1E, 0x88}; - uint8_t enc_txt[16] = {0xe6, 0x45, 0xb6, 0x15, 0x4e, 0x8f, 0x32, 0x7d, 0xfb, 0xab, 0x93, 0x4d, 0x4c, 0x66, 0x46, 0x14}; - uint8_t dec_txt[16] = {0}; +// -------------- File settings structs ------------------------- +#define FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING (1 << 6) - aes_decode(iv, aeskey, enc_txt, dec_txt, sizeof(enc_txt)); +#define FILE_SETTINGS_SDM_OPTIONS_UID (1 << 7) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER (1 << 6) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER_LIMIT (1 << 5) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA (1 << 4) +// RFU stuff... +#define FILE_SETTINGS_SDM_OPTIONS_ENCODING_MODE_ASCII (1 << 0) - PrintAndLogEx(INFO, "Ntag424 SUN message validation and encryption"); - PrintAndLogEx(INFO, "Enc text... %s", sprint_hex(enc_txt, sizeof(enc_txt))); - PrintAndLogEx(INFO, "Dec text... %s", sprint_hex(dec_txt, sizeof(dec_txt))); +typedef struct { + uint8_t sdm_options; + uint8_t sdm_access[2]; + uint8_t offset[8][3]; +} ntag424_file_sdm_settings_t; - sdm_picc_t o = {0}; - o.tag = dec_txt[0]; - memcpy(o.uid, dec_txt + 1, sizeof(o.uid)); - memcpy(o.cnt, dec_txt + 8, sizeof(o.cnt)); - o.cnt_int = MemLeToUint3byte(o.cnt); +typedef struct { + uint8_t type; + uint8_t options; + uint8_t access[2]; + uint8_t size[3]; + ntag424_file_sdm_settings_t optional_sdm_settings; +} ntag424_file_settings_t; - PrintAndLogEx(INFO, "Decypted text"); - PrintAndLogEx(INFO, " Tag........... 0x%02X", o.tag); - PrintAndLogEx(INFO, " UID........... %s", sprint_hex(o.uid, sizeof(o.uid))); - PrintAndLogEx(INFO, " Count bytes... %s", sprint_hex(o.cnt, sizeof(o.cnt))); - PrintAndLogEx(INFO, " Count value... 0x%X ( %u )", o.cnt_int, o.cnt_int); +#define SETTINGS_WITHOUT_OFFSETS_SIZE (1+1+2+3+1+2) - // SV2 as per NXP DS465430 (NT4H2421Gx Data sheet) - uint8_t sv2data[16] = {0x3C, 0xC3, 0x00, 0x01, 0x00, 0x80}; +// A different struct is used when actually writing the settings back, +// since we obviously can't change the size or type of a static file. +typedef struct { + uint8_t options; + uint8_t access[2]; + ntag424_file_sdm_settings_t optional_sdm_settings; +} file_settings_write_t; - memcpy(sv2data + 6, o.uid, sizeof(o.uid)); - memcpy(sv2data + 6 + sizeof(o.uid), o.cnt, sizeof(o.cnt)); +// Currently unused functions, commented out due to -Wunused-function +/*static void ntag424_file_settings_set_access_rights(ntag424_file_settings_t *settings, + uint8_t read_write_key, uint8_t change_key, + uint8_t read_key, uint8_t write_key) - uint8_t cmackey[16] = {0}; - mbedtls_aes_cmac_prf_128(aeskey, 16, sv2data, sizeof(sv2data), cmackey); +{ + settings->access[0] = read_write_key << 4 | change_key; + settings->access[1] = read_key << 4 | write_key; +}*/ - uint8_t zero[16] = {0}; - uint8_t full_cmac[16] = {0}; - mbedtls_aes_cmac_prf_128(cmackey, 16, zero, 0, full_cmac); +// Currently unused functions, commented out due to -Wunused-function +/*static void ntag424_file_settings_set_sdm_access_rights(ntag424_file_settings_t *settings, + uint8_t sdm_meta_read, uint8_t sdm_file_read, + uint8_t rfu, uint8_t sdm_ctr_ret) +{ + settings->optional_sdm_settings.sdm_access[1] = sdm_meta_read << 4 | sdm_file_read; + settings->optional_sdm_settings.sdm_access[0] = rfu << 4 | sdm_ctr_ret; +}*/ - uint8_t cmac[8] = {0}; - for (int i = 0, j = 1; i < 8; ++i, j += 2) { - cmac[i] = full_cmac[j]; + +static uint8_t ntag424_file_settings_get_sdm_meta_read(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[1] >> 4; +} + +static uint8_t ntag424_file_settings_get_sdm_file_read(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[1] & 0xf; +} + +// Currently unused functions, commented out due to -Wunused-function +/*static uint8_t ntag424_file_settings_get_sdm_rfu(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[0] >> 4; +} + +static uint8_t ntag424_file_settings_get_sdm_ctr_ret(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[0] & 4; +}*/ + +// Calculate the actual size of a file settings struct. A variable number of data is attached +// at the end depending on settings. +static int ntag424_calc_file_settings_size(const ntag424_file_settings_t *settings) { + int size = 7; + + if(settings->options & FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING) { + size += 3; // sdm_options and sdm_access must be present + + if(settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_UID && + ntag424_file_settings_get_sdm_meta_read(settings) == 0xe) + { + size += 3; // UIDOffset + } + + if(settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER && + ntag424_file_settings_get_sdm_meta_read(settings) == 0xe) + { + size += 3; // SDMReadCtrOffset + } + + if(ntag424_file_settings_get_sdm_meta_read(settings) <= 0x04) { + size += 3; // PICCDataOffset + } + + if(ntag424_file_settings_get_sdm_file_read(settings) != 0x0f) { + size += 3; // SDMMacInputOffset + } + + if(ntag424_file_settings_get_sdm_file_read(settings) != 0x0f && + settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA) + { + size += 3; // SDMEncOffset + size += 3; // SDMEncLength + } + + if(ntag424_file_settings_get_sdm_file_read(settings) != 0x0f) { + // Warning, this value has different offsets depending on + // FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA + size += 3; // SDMMacOffset + } + + if(settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER_LIMIT) + { + size += 3; // SDMReadCtrLimit + } } - //uint8_t veri[] = {0x94, 0xee, 0xd9, 0xee, 0x65, 0x33, 0x70, 0x86}; - uint8_t veri[] = {0x8b, 0xa1, 0xfb, 0x47, 0x0d, 0x63, 0x39, 0xe8 }; - uint8_t is_ok = (memcmp(cmac, veri, 8) == 0); + return size; +} + +static int ntag424_read_file_settings(uint8_t fileno, ntag424_file_settings_t *settings_out) { + const size_t RESPONSE_LENGTH = sizeof(ntag424_file_settings_t) + 2; + uint8_t cmd[] = { 0x90, 0xF5, 0x00, 0x00, 0x01, fileno, 0x00}; + uint8_t resp[RESPONSE_LENGTH]; + int outlen = 0; + int res; - PrintAndLogEx(INFO, "SDM cmac... %s ( %s )", - sprint_hex(cmac, sizeof(cmac)), - (is_ok) ? _GREEN_("ok") : _RED_("fail") - ); + res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if(outlen < 9) { + PrintAndLogEx(ERR, "Incorrect response asdaslength: %d", outlen); + return PM3_ESOFT; + } + + if(resp[outlen-2] != 0x91 || resp[outlen-1] != 0x00) + { + PrintAndLogEx(ERR, "Failed to get file settings"); + return PM3_ESOFT; + } + + if(settings_out) + { + memcpy(settings_out, resp, outlen-2); + } return PM3_SUCCESS; } -static int CmdHF_ntag424_sdm(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf ntag424 sdm", - "Validate a SDM message", - "hf ntag424 sdm" - ); - void *argtable[] = { - arg_param_begin, - arg_param_end +// Write file settings is done with full communication mode. This can probably be broken out +// and used for read/write of file when full communication mode is needed. +static int ntag424_write_file_settings(uint8_t fileno, ntag424_file_settings_t *settings, ntag424_session_keys_t *session_keys) { + + // ------- Convert file settings to the format for writing + file_settings_write_t write_settings = { + .options = settings->options, + .access[0] = settings->access[0], + .access[1] = settings->access[1], + .optional_sdm_settings = settings->optional_sdm_settings, }; - CLIExecWithReturn(ctx, Cmd, argtable, false); - CLIParserFree(ctx); - return sdm_generator(); + + // ------- Calculate IV + uint8_t iv_clear[] = { 0xa5, 0x5a, + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3], + (uint8_t)(session_keys->command_counter), (uint8_t)(session_keys->command_counter >> 8), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + uint8_t zero_iv[16] = {0}; + uint8_t ivc[16] = {0}; + aes_encode(zero_iv, session_keys->encryption, iv_clear, ivc, 16); + + // ------- Encrypt file settings + uint8_t padded_cmddata_buffer[256] = {0}; + uint8_t encrypted_cmddata[256] = {0}; + size_t settings_size = ntag424_calc_file_settings_size(settings) - 4; // This is weird, but since the write settings are the same as + // the settings read out, but minus file type and file size, we subtract 4 here. + + size_t total_size = settings_size + 16 - (settings_size % 16); // pad up to 16 byte blocks + memcpy(padded_cmddata_buffer, (void*)&write_settings, settings_size); + if(total_size > settings_size) { + padded_cmddata_buffer[settings_size] = 0x80; + } + aes_encode(ivc, session_keys->encryption, padded_cmddata_buffer, encrypted_cmddata, total_size); + + // ------- Calculate MAC + uint8_t mac_input_header[] = { 0x5f, + (uint8_t)session_keys->command_counter, (uint8_t)(session_keys->command_counter >> 8), + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3], + fileno // CmdHeader + }; + + uint8_t mac_input[256] = {0}; + memcpy(mac_input, mac_input_header, sizeof(mac_input_header)); + memcpy(&mac_input[sizeof(mac_input_header)], encrypted_cmddata, total_size); + uint8_t mac[16] = {0}; + mbedtls_aes_cmac_prf_128(session_keys->mac, 16, mac_input, sizeof(mac_input_header) + total_size, mac); + uint8_t mact[8] = { mac[1], mac[3], mac[5], mac[7], mac[9], mac[11], mac[13], mac[15] }; + + // ------- Assemble the actual command + uint8_t lc = 1 + total_size + 8; // CmdHeader + size + mac*/ + + uint8_t cmd_header[] = { + 0x90, 0x5f, 0x00, 0x00, + lc, + fileno + }; + + uint8_t cmd[256] = {0}; + + memcpy(cmd, cmd_header, sizeof(cmd_header)); + memcpy(&cmd[sizeof(cmd_header)], encrypted_cmddata, total_size); + memcpy(&cmd[sizeof(cmd_header) + total_size], mact, sizeof(mact)); + cmd[sizeof(cmd_header) + total_size + sizeof(mact)] = 0x00; + + size_t apdu_size = sizeof(cmd_header) + total_size + sizeof(mact) + 1; + + // ------- Actually send the APDU + const size_t RESPONSE_LENGTH = 8 + 2; + int outlen; + uint8_t resp[RESPONSE_LENGTH]; + int res = ExchangeAPDU14a(cmd, apdu_size, false, true, resp, RESPONSE_LENGTH, &outlen); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if(outlen != RESPONSE_LENGTH) { + PrintAndLogEx(ERR, "Incorrect response asdaslength: %d, %02X%02X", outlen, resp[outlen-2], resp[outlen-1]); + return PM3_ESOFT; + } + + if(resp[outlen-2] != 0x91 || resp[outlen-1] != 0x00) + { + PrintAndLogEx(ERR, "Failed to get file settings"); + return PM3_ESOFT; + } + + session_keys->command_counter++; // Should this be incremented only on success? + + return PM3_SUCCESS; +} + +static void ntag424_print_file_settings(uint8_t fileno, const ntag424_file_settings_t *settings) { + + int num_offsets = (ntag424_calc_file_settings_size(settings) - SETTINGS_WITHOUT_OFFSETS_SIZE) / 3; + + PrintAndLogEx(SUCCESS, "--- " _CYAN_("File %d settings:") , fileno); + + PrintAndLogEx(SUCCESS, " type: " _GREEN_("%02X"), settings->type); + PrintAndLogEx(SUCCESS, " options: " _GREEN_("%02X"), settings->options); + PrintAndLogEx(SUCCESS, " access: " _GREEN_("%02X%02X (RW, C, R, W)"), settings->access[0], settings->access[1]); + PrintAndLogEx(SUCCESS, " size: " _GREEN_("%02X%02X%02X"), settings->size[2], settings->size[1], settings->size[0]); + + if(settings->options & FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING) + { + PrintAndLogEx(SUCCESS, "--- " _CYAN_("SDM settings: ")); + PrintAndLogEx(SUCCESS, " options: " _GREEN_("%02X"), settings->optional_sdm_settings.sdm_options); + PrintAndLogEx(SUCCESS, " sdm access: " _GREEN_("%02X%02X"), settings->optional_sdm_settings.sdm_access[0], settings->optional_sdm_settings.sdm_access[1]); + + if(num_offsets > 0) + { + PrintAndLogEx(SUCCESS, "--- " _CYAN_("SDM offsets: ")); + for(int i = 0; i < num_offsets; i++) + { + PrintAndLogEx(SUCCESS, " offset %d: %02X%02X%02X", i, + settings->optional_sdm_settings.offset[i][2], + settings->optional_sdm_settings.offset[i][1], + settings->optional_sdm_settings.offset[i][0]); + } + } + } + + + // Add printing of variable number of offsets here... +} + +// NTAG424 only have one static application, so we select it here +static int ntag424_select_application(void) { + const size_t RESPONSE_LENGTH = 2; + uint8_t cmd[] = {0x00, 0xA4, 0x04, 0x0C, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00 }; + uint8_t resp[RESPONSE_LENGTH]; + int outlen = 0; + int res; + + res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if(outlen != RESPONSE_LENGTH || resp[RESPONSE_LENGTH-2] != 0x90 || resp[RESPONSE_LENGTH-1] != 0x00) + { + PrintAndLogEx(ERR, "Failed to select application"); + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static int ntag424_auth_first_step(uint8_t keyno, uint8_t *key, uint8_t *out) +{ + const size_t RESPONSE_LENGTH = 16 + 2; + uint8_t cmd[] = {0x90, 0x71, 0x00, 0x00, 0x02, keyno, 0x00, 0x00}; + uint8_t resp[RESPONSE_LENGTH]; + int outlen = 0; + int res; + + res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if(outlen != RESPONSE_LENGTH || resp[RESPONSE_LENGTH-2] != 0x91 || resp[RESPONSE_LENGTH-1] != 0xAF) + { + PrintAndLogEx(ERR, "Failed to get RndB (invalid key number?)"); + return PM3_ESOFT; + } + + uint8_t iv[16] = {0}; + aes_decode(iv, key, resp, out, 16); + + return PM3_SUCCESS; +} + +static int ntag424_auth_second_step(uint8_t *challenge, uint8_t *response) +{ + uint8_t cmd_header[] = { 0x90, 0xAF, 0x00, 0x00, 0x20 }; + + uint8_t cmd[sizeof(cmd_header) + 32 + 1] = {0}; + + memcpy(cmd, cmd_header, sizeof(cmd_header)); + memcpy(&cmd[sizeof(cmd_header)], challenge, 32); + + const size_t RESPONSE_LENGTH = 256; + uint8_t resp[RESPONSE_LENGTH]; + int outlen = 0; + int res; + + res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + if(res != PM3_SUCCESS) + { + return res; + } + + if(resp[outlen-2] != 0x91 || resp[outlen-1] != 0x00) + { + PrintAndLogEx(ERR, "Challenge failed: wrong key?"); + return PM3_ESOFT; + } + + memcpy(response, resp, outlen-2); + + return PM3_SUCCESS; +} + +// Authenticate against a key number and optionally get session keys out +static int ntag424_authenticate_ev2_first(uint8_t keyno, uint8_t *key, ntag424_session_keys_t *session_keys_out) +{ + // -------- Get first challenge from card + uint8_t rnd_b_clear[16] = {0}; + + int res = ntag424_auth_first_step(keyno, key, rnd_b_clear); + if(res != PM3_SUCCESS) + { + return res; + } + + // -------- Concatenate RndA and RndB and encrypt it with the key + uint8_t concat_clear[32] = {0}; + uint8_t concat_enc[32] = {0}; + // This should of course be completely random, if we cared + // about security + uint8_t rnd_a_clear[16] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf + }; + + uint8_t iv[16] = {0}; + memcpy(&concat_clear[16], &rnd_b_clear[1], 15); + concat_clear[31] = rnd_b_clear[0]; + memcpy(concat_clear, rnd_a_clear, 16); + + aes_encode(iv, key, concat_clear, concat_enc, 32); + + // -------- Do second step with our concatenated encrypted RndA || RndB + uint8_t resp[4+16+6+6]; + res = ntag424_auth_second_step(concat_enc, resp); + if(res != PM3_SUCCESS) + { + return res; + } + + ntag424_ev2_response_t response; + aes_decode(iv, key, resp, (uint8_t*)&response, sizeof(ntag424_ev2_response_t)); + + // -------- Verify that the response we got contains the RndA that we supplied (rotated one byte) + if(memcmp(response.rnd_a, &rnd_a_clear[1], 15) != 0 || + response.rnd_a[15] != rnd_a_clear[0]) + { + PrintAndLogEx(ERR, "Incorrect response from card\n" + "expected: %s\n" + "got: %s" + , sprint_hex(rnd_a_clear, 16), + sprint_hex(response.rnd_a, 16)); + return PM3_ESOFT; + } + + // -------- Optionally calculate session keys + if(session_keys_out) + { + memset(session_keys_out, 0, sizeof(ntag424_session_keys_t)); + memcpy(session_keys_out->ti, response.ti, sizeof(response.ti)); + + // SV 1 = [0xA5][0x5A][0x00][0x01] + // [0x00][0x80][RndA[15:14] || + // [ (RndA[13:8] ⊕ RndB[15:10]) ] || + // [RndB[9:0] || RndA[7:0] + + uint8_t sv1[] = { 0xa5, 0x5a, 0x00, 0x01, 0x00, 0x80, rnd_a_clear[0], rnd_a_clear[1], + rnd_a_clear[2] ^ rnd_b_clear[0], + rnd_a_clear[3] ^ rnd_b_clear[1], + rnd_a_clear[4] ^ rnd_b_clear[2], + rnd_a_clear[5] ^ rnd_b_clear[3], + rnd_a_clear[6] ^ rnd_b_clear[4], + rnd_a_clear[7] ^ rnd_b_clear[5], + rnd_b_clear[6], rnd_b_clear[7], rnd_b_clear[8], rnd_b_clear[9], rnd_b_clear[10], + rnd_b_clear[11], rnd_b_clear[12], rnd_b_clear[13], rnd_b_clear[14], rnd_b_clear[15], + rnd_a_clear[8], rnd_a_clear[9], rnd_a_clear[10], + rnd_a_clear[11], rnd_a_clear[12], rnd_a_clear[13], rnd_a_clear[14], rnd_a_clear[15] + }; + + // SV 2 = [0x5A][0xA5][0x00][0x01] + // [0x00][0x80][RndA[15:14] || + // [ (RndA[13:8] ⊕ RndB[15:10]) ] || + // [RndB[9:0] || RndA[7:0] + + uint8_t sv2[] = { 0x5a, 0xa5, 0x00, 0x01, 0x00, 0x80, rnd_a_clear[0], rnd_a_clear[1], + rnd_a_clear[2] ^ rnd_b_clear[0], + rnd_a_clear[3] ^ rnd_b_clear[1], + rnd_a_clear[4] ^ rnd_b_clear[2], + rnd_a_clear[5] ^ rnd_b_clear[3], + rnd_a_clear[6] ^ rnd_b_clear[4], + rnd_a_clear[7] ^ rnd_b_clear[5], + rnd_b_clear[6], rnd_b_clear[7], rnd_b_clear[8], rnd_b_clear[9], rnd_b_clear[10], + rnd_b_clear[11], rnd_b_clear[12], rnd_b_clear[13], rnd_b_clear[14], rnd_b_clear[15], + rnd_a_clear[8], rnd_a_clear[9], rnd_a_clear[10], + rnd_a_clear[11], rnd_a_clear[12], rnd_a_clear[13], rnd_a_clear[14], rnd_a_clear[15] + }; + + mbedtls_aes_cmac_prf_128(key, 16, sv1, sizeof(sv1), session_keys_out->encryption); + mbedtls_aes_cmac_prf_128(key, 16, sv2, sizeof(sv2), session_keys_out->mac); + } + + return PM3_SUCCESS; +} + +#define MAX_WRITE_APDU 248 + +// Write file to card. Only supports plain communications mode. Authentication must be done +// first unless file has free write access. +static int ntag424_write_file(uint8_t fileno, uint16_t offset, uint16_t num_bytes, uint8_t *in) { + const size_t RESPONSE_LENGTH = 2; + size_t remainder = 0; + + if(num_bytes > MAX_WRITE_APDU) + { + remainder = num_bytes - MAX_WRITE_APDU; + num_bytes = MAX_WRITE_APDU; + } + + // 248 + + uint8_t cmd_header[] = { 0x90, 0x8d, 0x00, 0x00, 0x07 + num_bytes, fileno, + (uint8_t)offset, (uint8_t)(offset << 8), (uint8_t)(offset << 16), // offset + (uint8_t)num_bytes, (uint8_t)(num_bytes >> 8), (uint8_t)(num_bytes >> 16) //size + }; + + uint8_t cmd[512] = {0}; + + memcpy(cmd, cmd_header, sizeof(cmd_header)); + memcpy(&cmd[sizeof(cmd_header)], in, num_bytes); + + size_t total_size = sizeof(cmd_header) + num_bytes + 1; //(Le) + + uint8_t resp[RESPONSE_LENGTH]; + int outlen = 0; + int res; + + res = ExchangeAPDU14a(cmd, total_size, false, true, resp, RESPONSE_LENGTH, &outlen); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if(outlen != RESPONSE_LENGTH) { + PrintAndLogEx(ERR, "Incorrect response length: %d, %s", outlen, sprint_hex(resp, 2)); + return PM3_ESOFT; + } + + if(resp[outlen-2] != 0x91 || resp[outlen-1] != 0x00) + { + PrintAndLogEx(ERR, "Failed to write file"); + return PM3_ESOFT; + } + + if(remainder > 0) + { + return ntag424_write_file(fileno, offset + num_bytes, remainder, &in[num_bytes]); + } + + return PM3_SUCCESS; +} + +// Read file from card. Only supports plain communications mode. Authentication must be done +// first unless file has free read access. +static int ntag424_read_file(uint8_t fileno, uint16_t offset, uint16_t num_bytes, uint8_t *out) { + const size_t RESPONSE_LENGTH = num_bytes + 2; + + uint8_t cmd[] = { 0x90, 0xad, 0x00, 0x00, 0x07, fileno, + (uint8_t)offset, (uint8_t)(offset << 8), (uint8_t)(offset << 16), // offset + (uint8_t)num_bytes, (uint8_t)(num_bytes >> 8), 0x00, //size + 0x00 }; + uint8_t resp[RESPONSE_LENGTH]; + int outlen = 0; + int res; + + res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if(outlen != RESPONSE_LENGTH) { + PrintAndLogEx(ERR, "Incorrect response length: %d, %s", outlen, sprint_hex(resp, 2)); + return PM3_ESOFT; + } + + if(resp[outlen-2] != 0x91 || resp[outlen-1] != 0x00) + { + PrintAndLogEx(ERR, "Failed to read file"); + return PM3_ESOFT; + } + + memcpy(out, resp, num_bytes); + return PM3_SUCCESS; +} + +static int ntag424_change_key(uint8_t keyno, uint8_t *new_key, uint8_t *old_key, uint8_t version, ntag424_session_keys_t *session_keys) { + // -------- Calculate xor and crc + uint8_t key[16] = {0}; + uint8_t crc[4] = {0}; + if(keyno != 0) + { + for(int i = 0; i < 16; i++) + { + key[i] = old_key[i] ^ new_key[i]; + } + crc32_ex(new_key, 16, crc); + } + else + { + memcpy(key, new_key, 16); + } + + + // ------- Calculate IV + uint8_t iv_clear[] = { 0xa5, 0x5a, + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3], + (uint8_t)(session_keys->command_counter), (uint8_t)(session_keys->command_counter >> 8), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + uint8_t zero_iv[16] = {0}; + uint8_t ive[16] = {0}; + aes_encode(zero_iv, session_keys->encryption, iv_clear, ive, 16); + + // ------- Calculate KeyData + uint8_t keydata[32] = {0}; + memcpy(keydata, key, 16); + keydata[16] = version; + if(keyno != 0) + { + memcpy(&keydata[17], crc, 4); + keydata[21] = 0x80; + } + else + { + keydata[17] = 0x80; + } + + uint8_t enc_keydata[32] = {0}; + aes_encode(ive, session_keys->encryption, keydata, enc_keydata, 32); + + // -------- Calculate MAC + uint8_t mac_input_header[] = { 0xC4, + (uint8_t)session_keys->command_counter, (uint8_t)(session_keys->command_counter >> 8), + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3], + keyno, + }; + + uint8_t mac_input[256] = {0}; + memcpy(mac_input, mac_input_header, sizeof(mac_input_header)); + memcpy(&mac_input[sizeof(mac_input_header)], enc_keydata, 32); + uint8_t mac[16] = {0}; + mbedtls_aes_cmac_prf_128(session_keys->mac, 16, mac_input, sizeof(mac_input_header) + 32, mac); + uint8_t mact[8] = { mac[1], mac[3], mac[5], mac[7], mac[9], mac[11], mac[13], mac[15] }; + + + // ------- Assemble APDU + uint8_t cmd_header[] = { + 0x90, 0xC4, 0x00, 0x00, 0x29, keyno + }; + + uint8_t cmd[512] = {0}; + memcpy(cmd, cmd_header, sizeof(cmd_header)); + memcpy(&cmd[sizeof(cmd_header)], enc_keydata, 32); + memcpy(&cmd[sizeof(cmd_header) + 32], mact, 8); + int apdu_size = sizeof(cmd_header) + 32 + 8 + 1; + + + // ------- Actually send the APDU + const size_t RESPONSE_LENGTH = 8 + 2; + int outlen; + uint8_t resp[RESPONSE_LENGTH]; + int res = ExchangeAPDU14a(cmd, apdu_size, false, true, resp, RESPONSE_LENGTH, &outlen); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if(outlen < 2) { + PrintAndLogEx(ERR, "Incorrect response length: %d", outlen); + return PM3_ESOFT; + } + + if(resp[outlen-2] != 0x91 || resp[outlen-1] != 0x00) + { + PrintAndLogEx(ERR, "Failed to get file settings"); + return PM3_ESOFT; + } + + session_keys->command_counter++; // Should this be incremented only on success? + + return PM3_SUCCESS; + } static int CmdHF_ntag424_info(const char *Cmd) { @@ -169,27 +768,613 @@ static int CmdHF_ntag424_info(const char *Cmd) { arg_param_begin, arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); PrintAndLogEx(INFO, "not implemented yet"); PrintAndLogEx(INFO, "Feel free to contribute!"); + return PM3_SUCCESS; +} +static int ntag424_cli_get_auth_information(CLIParserContext *ctx, int key_no_index, int key_index, int *keyno, uint8_t *key_out) +{ + uint8_t key[16]; + int keylen = 16; + if(keyno) + { + *keyno = arg_get_int(ctx, key_no_index); + } + CLIGetHexWithReturn(ctx, key_index, key, &keylen); - // has hardcoded application and three files. + if(keylen != 16) + { + PrintAndLogEx(ERR, "Key must be 16 bytes"); + return PM3_ESOFT; + } + memcpy(key_out, key, 16); + + return PM3_SUCCESS; +} - /* - // Check if the tag reponds to APDUs. - PrintAndLogEx(INFO, "Sending a test APDU (select file command) to check if the tag is responding to APDU"); - param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); - int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, true, false, response, sizeof(response), &response_n); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Tag did not respond to a test APDU (select file command). Aborting..."); +static int CmdHF_ntag424_auth(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 auth", + "Authenticate with selected key against NTAG424.", + "hf ntag424 auth -n 0 -k 00000000000000000000000000000000"); + + void *argtable[] = { + arg_param_begin, + arg_int1("n", "keyno", "", "Key number"), + arg_str1("k", "key", "", "Key for authenticate (HEX 16 bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int keyno; + uint8_t key[16] = {0}; + + if(ntag424_cli_get_auth_information(ctx, 1, 2, &keyno, key) != PM3_SUCCESS) + { + CLIParserFree(ctx); + return PM3_ESOFT; + } + + CLIParserFree(ctx); + + int res = SelectCard14443A_4(false, true, NULL); + if (res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to select card"); + DropField(); + return res; + } + + res = ntag424_select_application(); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + res = ntag424_authenticate_ev2_first(keyno, key, NULL); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to authenticate with key %d", keyno); + } + else + { + PrintAndLogEx(SUCCESS, "Successfully authenticated with key %d", keyno); + } + + DropField(); + + return res; +} + +// Read can only read files with plain communication mode! +static int CmdHF_ntag424_read(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 read", + "Read and print data from file on NTAG424 tag. Will authenticate if key information is provided.", + "hf ntag424 read -f 2 -n 0 -k 00000000000000000000000000000000 -o 0 -l 256"); + + void *argtable[] = { + arg_param_begin, + arg_int1("f", "fileno", "", "File number (1-3), (default 2)"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_int0("o", "offset", "", "Offset to read in file (default 0)"), + arg_int1("l", "length", "", "Number of bytes to read"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int keyno; + uint8_t key[16] = {0}; + int auth = 1; + + int fileno = arg_get_int(ctx, 1); + + if(ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) != PM3_SUCCESS) + { + PrintAndLogEx(INFO, "Reading unauthenticated"); + auth = 0; + } + else + { + PrintAndLogEx(INFO, "Reading authenticated"); + } + + int offset = arg_get_int_def(ctx, 4, 0); + int read_length = arg_get_int(ctx, 5); + + CLIParserFree(ctx); + + int res = SelectCard14443A_4(false, true, NULL); + if (res != PM3_SUCCESS) + { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return res; + } + + res = ntag424_select_application(); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + if(auth) + { + res = ntag424_authenticate_ev2_first(keyno, key, NULL); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to authenticate with key %d", keyno); + DropField(); return res; } - */ + else + { + PrintAndLogEx(SUCCESS, "Successfully authenticated with key %d", keyno); + } + } + uint8_t data[512]; + + res = ntag424_read_file(fileno, offset, read_length, data); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + PrintAndLogEx(SUCCESS, " -------- Read file %d contents ------------ ", fileno); + print_hex_break(data, read_length, 16); + + DropField(); + + return res; +} + +static int CmdHF_ntag424_write(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 write", + "Write data to file on NTAG424 tag. Will authenticate if key information is provided.", + "hf ntag424 write -f 2 -n 0 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788"); + + void *argtable[] = { + arg_param_begin, + arg_int1("f", "fileno", "", "File number (1-3), (default 2)"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_int0("o", "offset", "", "Offset to write in file (default 0)"), + arg_str1("d", "data", "", "Data to write"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int keyno; + uint8_t key[16] = {0}; + int auth = 1; + + int fileno = arg_get_int(ctx, 1); + + if(ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) != PM3_SUCCESS) + { + PrintAndLogEx(INFO, "Will write unauthenticated"); + auth = 0; + } + else + { + PrintAndLogEx(INFO, "Will write authenticated"); + } + + int offset = arg_get_int_def(ctx, 4, 0); + + uint8_t data[512] = {0}; + int datalen = 512; + CLIGetHexWithReturn(ctx, 5, data, &datalen); + + CLIParserFree(ctx); + + int res = SelectCard14443A_4(false, true, NULL); + if (res != PM3_SUCCESS) + { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return res; + } + + res = ntag424_select_application(); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + if(auth) + { + res = ntag424_authenticate_ev2_first(keyno, key, NULL); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to authenticate with key %d", keyno); + DropField(); + return res; + } + else + { + PrintAndLogEx(SUCCESS, "Successfully authenticated with key %d", keyno); + } + } + + res = ntag424_write_file(fileno, offset, datalen, data); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + PrintAndLogEx(SUCCESS, "Wrote %d bytes", datalen); + + DropField(); + + return res; +} + +static int CmdHF_ntag424_getfilesettings(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 getfilesettings", + "Read and print file settings for file", + "hf ntag424 getfilesettings -f 2"); + + void *argtable[] = { + arg_param_begin, + arg_int1("f", "file", "", "File number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fileno = arg_get_int(ctx, 1); + + CLIParserFree(ctx); + + int res = SelectCard14443A_4(false, true, NULL); + if (res != PM3_SUCCESS) + { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return res; + } + + res = ntag424_select_application(); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + ntag424_file_settings_t settings; + res = ntag424_read_file_settings(fileno, &settings); + DropField(); + if(res != PM3_SUCCESS) + { + return res; + } + + ntag424_print_file_settings(fileno, &settings); + + + return res; +} + +static int CmdHF_ntag424_changefilesettings(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 changefilesettings", + "Updates file settings for file, must be authenticated.\n" + "This is a short explanation of the settings. See AN12196 for more information:\n" + "options: byte with bit flags\n" + " Bit: Setting:\n" + " 6 Enable SDM and mirroring\n\n" + + "access: two byte access rights.\n" + "Each nibble is a key number, or E for free access.\n" + "Order is key for readwrite, change, read and write\n\n" + + "sdmoptions: byte with bit flags\n" + " Bit: Setting:\n" + " 0 ASCII encoding\n" + " 4 SDMEncFileData\n" + " 5 SDMReadCtrLimit\n" + " 6 SDMReadCtr\n" + " 7 SDMOptionsUID\n\n" + + "sdmaccess: two byte access rights.\n" + "Each nibble is a key, or E for plain mirror and F for no mirroring\n" + "Order is SDMMetaRead, SDMFileRead, RFU and SDMCtrRet\n\n" + + "offset: A three byte offset to the data to be mirrored to/from. Can be specified multiple times.\n\n" + + "Note: Not all of these settings will be written. It depends on the option byte, and the keys set. See AN12196 for more information.\n" + "You must also start with offset1, then offset2, up to the number of offsets you want to write", + + + "hf ntag424 changefilesettings -f 2 -n 0 -k 00000000000000000000000000000000 -o 40 -a 00E0 -s C1 -c F000 --offset1 000020 --offset2 000043 --offset3 000043"); + + void *argtable[] = { + arg_param_begin, + arg_int1("f", "file", "", "File number"), + arg_int1("n", "keyno", "", "Key number"), + arg_str1("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_str0("o", "options", "", "File options byte (HEX 1 byte)"), + arg_str0("a", "access", "", "File access settings (HEX 2 bytes)"), + arg_str0("s", "sdmoptions", "", "SDM options (HEX 1 byte)"), + arg_str0("c", "sdmaccess", "", "SDM access settings (HEX 2 bytes)"), + arg_str0(NULL, "offset1", "", "SDM offset (HEX 3 bytes)"), + arg_str0(NULL, "offset2", "", "SDM offset (HEX 3 bytes)"), + arg_str0(NULL, "offset3", "", "SDM offset (HEX 3 bytes)"), + arg_str0(NULL, "offset4", "", "SDM offset (HEX 3 bytes)"), + arg_str0(NULL, "offset5", "", "SDM offset (HEX 3 bytes)"), + arg_str0(NULL, "offset6", "", "SDM offset (HEX 3 bytes)"), + arg_str0(NULL, "offset7", "", "SDM offset (HEX 3 bytes)"), + // Sorry, couldn't figure out how to work with arg_strn... + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fileno = arg_get_int(ctx, 1); + + int keyno; + uint8_t key[16] = {0}; + + uint8_t has_options = 0; + uint8_t options[1]; + uint8_t has_access = 0; + uint8_t access[2]; + uint8_t has_sdmoptions = 0; + uint8_t sdmoptions[1]; + uint8_t has_sdmaccess = 0; + uint8_t sdmaccess[2]; + uint8_t num_offsets = 0; + uint8_t offsets[8][3]; + + + if(ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Could not get key settings"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + int len = 1; + if(arg_get_str(ctx,4)->count == 1) + { + has_options = 1; + CLIGetHexWithReturn(ctx, 4, options, &len); + if(len != 1) + { + PrintAndLogEx(ERR, "Options must be 1 byte"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 2; + if(arg_get_str(ctx,5)->count == 1) + { + has_access = 1; + CLIGetHexWithReturn(ctx, 5, access, &len); + if(len != 2) + { + PrintAndLogEx(ERR, "access must be 2 bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 1; + if(arg_get_str(ctx,6)->count == 1) + { + has_sdmoptions = 1; + CLIGetHexWithReturn(ctx, 6, sdmoptions, &len); + if(len != 1) + { + PrintAndLogEx(ERR, "SDM Options must be 1 byte"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 2; + if(arg_get_str(ctx,7)->count == 1) + { + has_sdmaccess = 1; + CLIGetHexWithReturn(ctx, 7, sdmaccess, &len); + if(len != 2) + { + PrintAndLogEx(ERR, "SDM access must be 2 bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + for(int i = 0; i < 8; i++) + { + if(arg_get_str(ctx,8+i)->count == 1) + { + len = 3; + num_offsets++; + CLIGetHexWithReturn(ctx, 8+i, offsets[i], &len); + if(len != 3) + { + PrintAndLogEx(ERR, "Offset must be 3 bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + else + { + break; + } + } + + + CLIParserFree(ctx); + + int res = SelectCard14443A_4(false, true, NULL); + if (res != PM3_SUCCESS) + { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return res; + } + + res = ntag424_select_application(); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + ntag424_file_settings_t settings; + res = ntag424_read_file_settings(fileno, &settings); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + ntag424_session_keys_t session = {0}; + res = ntag424_authenticate_ev2_first(keyno, key, &session); + if(res != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to authenticate with key %d", keyno); + DropField(); + return res; + } + + if(has_options) + { + settings.options = options[0]; + } + if(has_access) + { + memcpy(settings.access, access, 2); + } + if(has_sdmoptions) + { + settings.optional_sdm_settings.sdm_options = sdmoptions[0]; + } + if(has_sdmaccess) + { + memcpy(settings.optional_sdm_settings.sdm_access, sdmaccess, 2); + } + + for(int i = 0; i < num_offsets; i++) + { + settings.optional_sdm_settings.offset[i][2] = offsets[i][0]; + settings.optional_sdm_settings.offset[i][1] = offsets[i][1]; + settings.optional_sdm_settings.offset[i][0] = offsets[i][2]; + } + + if(ntag424_write_file_settings(fileno, &settings, &session) != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Failed to write settings"); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(SUCCESS, "Wrote settings successfully"); + ntag424_print_file_settings(fileno, &settings); + + DropField(); + return res; +} + +static int CmdHF_ntag424_changekey(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 changekey", + "Change a key.\n" + "Authentication key must currently be different to the one we want to change.\n", + "hf ntag424 changekey -n 1 --oldkey 00000000000000000000000000000000 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 -v 1" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("n", "keyno", "", "Key number to change"), + arg_str0(NULL, "oldkey", "", "Old key (only needed when changing key 1-4, HEX 16 bytes)"), + arg_str1(NULL, "newkey", "", "New key (HEX 16 bytes)"), + arg_str1(NULL, "key0", "", "Authentication key (must be key 0, HEX 16 bytes)"), + arg_int1("v", "version", "", "Version of the new key"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t version = arg_get_int(ctx, 6); + int keyno = arg_get_int(ctx, 1); + uint8_t oldkey[16]; + uint8_t newkey[16]; + uint8_t authkey[16]; + + if(keyno != 0) + { + if(ntag424_cli_get_auth_information(ctx, 0, 2, NULL, oldkey) != PM3_SUCCESS) + { + + PrintAndLogEx(ERR, "Could not get keyno or old key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + if(ntag424_cli_get_auth_information(ctx, 0, 3, NULL, newkey) != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Could not get new key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + if(ntag424_cli_get_auth_information(ctx, 0, 4, NULL, authkey) != PM3_SUCCESS) + { + PrintAndLogEx(ERR, "Could not get authentication key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + int res = SelectCard14443A_4(false, true, NULL); + if (res != PM3_SUCCESS) + { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return res; + } + + res = ntag424_select_application(); + if(res != PM3_SUCCESS) + { + DropField(); + return res; + } + + ntag424_session_keys_t session = {0}; + res = ntag424_authenticate_ev2_first(0, authkey, &session); + if(res != PM3_SUCCESS) + { + DropField(); + PrintAndLogEx(ERR, "Failed to authenticate"); + return PM3_ESOFT; + } + else + { + PrintAndLogEx(SUCCESS, "Successfully authenticated"); + } + + res = ntag424_change_key(keyno, newkey, oldkey, version, &session); + if(res != PM3_SUCCESS) + { + DropField(); + PrintAndLogEx(ERR, "Failed to change key"); + DropField(); + return PM3_ESOFT; + } + else + { + PrintAndLogEx(SUCCESS, "Successfully changed key %d", keyno); + } + + DropField(); return PM3_SUCCESS; } @@ -197,12 +1382,16 @@ static int CmdHF_ntag424_info(const char *Cmd) { // Menu Stuff //------------------------------------ static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, - {"info", CmdHF_ntag424_info, IfPm3Iso14443a, "Tag information"}, -// {"ndefread", CmdHF_ntag424_sdm, IfPm3Iso14443a, "Prints NDEF records from card"}, - {"sdm", CmdHF_ntag424_sdm, IfPm3Iso14443a, "Prints NDEF records from card"}, - {"view", CmdHF_ntag424_view, AlwaysAvailable, "Display content from tag dump file"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, + {"info", CmdHF_ntag424_info, IfPm3Iso14443a, "Tag information (not implemented yet)"}, + {"view", CmdHF_ntag424_view, AlwaysAvailable, "Display content from tag dump file"}, + {"auth", CmdHF_ntag424_auth, IfPm3Iso14443a, "Test authentication with key"}, + {"read", CmdHF_ntag424_read, IfPm3Iso14443a, "Read file"}, + {"write", CmdHF_ntag424_write, IfPm3Iso14443a, "Write file"}, + {"getfilesettings", CmdHF_ntag424_getfilesettings, IfPm3Iso14443a, "Get file settings"}, + {"changefilesettings", CmdHF_ntag424_changefilesettings, IfPm3Iso14443a, "Change file settings"}, + {"changekey", CmdHF_ntag424_changekey, IfPm3Iso14443a, "Change key"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index cbc0632d7..e4fab934d 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -455,8 +455,13 @@ const static vocabulary_t vocabulary[] = { { 1, "hf mfdes test" }, { 1, "hf ntag424 help" }, { 0, "hf ntag424 info" }, - { 0, "hf ntag424 sdm" }, { 1, "hf ntag424 view" }, + { 0, "hf ntag424 auth" }, + { 0, "hf ntag424 read" }, + { 0, "hf ntag424 write" }, + { 0, "hf ntag424 getfilesettings" }, + { 0, "hf ntag424 changefilesettings" }, + { 0, "hf ntag424 changekey" }, { 1, "hf seos help" }, { 0, "hf seos info" }, { 1, "hf seos list" }, @@ -817,4 +822,4 @@ const static vocabulary_t vocabulary[] = { } #endif -#endif \ No newline at end of file +#endif