diff --git a/client/src/cmdhffmcos.c b/client/src/cmdhffmcos.c index f8efe8380..44f1138bc 100644 --- a/client/src/cmdhffmcos.c +++ b/client/src/cmdhffmcos.c @@ -14,7 +14,6 @@ // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- // Commands for FMCOS CPU smart cards (Fudan Microelectronics) -// Reference implementation: client/pyscripts/fmcos/ //----------------------------------------------------------------------------- #include "cmdhffmcos.h" @@ -69,7 +68,10 @@ static const char *fmcos_print_sw(uint8_t sw1, uint8_t sw2) { switch (sw1) { case 0x62: - if (sw2 >= 0x02 && sw2 <= 0x80) { desc = "Triggering by the card"; break; } + if (sw2 >= 0x02 && sw2 <= 0x80) { + desc = "Triggering by the card"; + break; + } switch (sw2) { case 0x81: desc = "Part of returned data may be corrupted"; @@ -95,21 +97,39 @@ static const char *fmcos_print_sw(uint8_t sw1, uint8_t sw2) { break; case 0x63: - if (sw2 == 0x81) { desc = "File filled up by last write"; break; } - if ((sw2 & 0xF0) == 0xC0) { desc = "Counter (0-15) encoded in SW2 low nibble"; break; } + if (sw2 == 0x81) { + desc = "File filled up by last write"; + break; + } + if ((sw2 & 0xF0) == 0xC0) { + desc = "Counter (0-15) encoded in SW2 low nibble"; + break; + } break; case 0x64: - if (sw2 >= 0x02 && sw2 <= 0x80) { desc = "Triggering by the card"; break; } - if (sw2 == 0x01) { desc = "Immediate response required by card"; break; } + if (sw2 >= 0x02 && sw2 <= 0x80) { + desc = "Triggering by the card"; + break; + } + if (sw2 == 0x01) { + desc = "Immediate response required by card"; + break; + } break; case 0x65: - if (sw2 == 0x81) { desc = "Memory failure"; break; } + if (sw2 == 0x81) { + desc = "Memory failure"; + break; + } break; case 0x67: - if (sw2 == 0x00) { desc = "Invalid length"; break; } + if (sw2 == 0x00) { + desc = "Invalid length"; + break; + } break; case 0x68: @@ -203,15 +223,24 @@ static const char *fmcos_print_sw(uint8_t sw1, uint8_t sw2) { break; case 0x6D: - if (sw2 == 0x00) { desc = "Invalid INS"; break; } + if (sw2 == 0x00) { + desc = "Invalid INS"; + break; + } break; case 0x6E: - if (sw2 == 0x00) { desc = "Invalid CLA"; break; } + if (sw2 == 0x00) { + desc = "Invalid CLA"; + break; + } break; case 0x93: - if (sw2 == 0x02) { desc = "Invalid MAC"; break; } + if (sw2 == 0x02) { + desc = "Invalid MAC"; + break; + } break; case 0x94: @@ -228,7 +257,10 @@ static const char *fmcos_print_sw(uint8_t sw1, uint8_t sw2) { break; case 0x90: - if (sw2 == 0x00) { desc = "Success"; break; } + if (sw2 == 0x00) { + desc = "Success"; + break; + } break; default: @@ -310,8 +342,9 @@ static size_t fmcos_iso7816_pad(const uint8_t *in, size_t in_len, uint8_t *out) memcpy(out, in, in_len); out[in_len] = 0x80; size_t padded = in_len + 1; - while (padded % 8 != 0) + while (padded % 8 != 0) { out[padded++] = 0x00; + } return padded; } @@ -319,14 +352,16 @@ static size_t fmcos_iso7816_pad(const uint8_t *in, size_t in_len, uint8_t *out) // DES CBC-MAC with ISO7816 padding. key must be exactly 8 bytes. // iv is 8 bytes (typically the GET CHALLENGE response). // Writes mac_len bytes (<= 8) into mac_out. -static void fmcos_des_mac(const uint8_t *buf, size_t buf_len, +static bool fmcos_des_mac(const uint8_t *buf, size_t buf_len, const uint8_t key[8], const uint8_t iv[8], uint8_t *mac_out, size_t mac_len) { size_t max_padded = buf_len + 8; uint8_t *padded = calloc(max_padded, 1); - if (padded == NULL) - return; + if (padded == NULL) { + PrintAndLogEx(ERR, "calloc failed"); + return false; + } size_t padded_len = fmcos_iso7816_pad(buf, buf_len, padded); @@ -335,22 +370,25 @@ static void fmcos_des_mac(const uint8_t *buf, size_t buf_len, for (size_t i = 0; i < padded_len; i += 8) { uint8_t xored[8]; - for (int j = 0; j < 8; j++) + for (int j = 0; j < 8; j++) { xored[j] = val[j] ^ padded[i + j]; + } fmcos_des8_ecb_enc(key, xored, val); } free(padded); - if (mac_len > 8) + if (mac_len > 8) { mac_len = 8; + } memcpy(mac_out, val, mac_len); + return true; } // 3DES Retail MAC: DES-CBC-MAC with left-key-half (8 bytes), then decrypt // with right-key-half, then re-encrypt with left-key-half. // key must be exactly 16 bytes. Writes mac_len bytes (<= 8) into mac_out. -static void fmcos_3des_mac(const uint8_t *buf, size_t buf_len, +static bool fmcos_3des_mac(const uint8_t *buf, size_t buf_len, const uint8_t key[16], const uint8_t iv[8], uint8_t *mac_out, size_t mac_len) { @@ -358,15 +396,19 @@ static void fmcos_3des_mac(const uint8_t *buf, size_t buf_len, const uint8_t *key_r = key + 8; uint8_t val[8]; - fmcos_des_mac(buf, buf_len, key_l, iv, val, 8); + if (!fmcos_des_mac(buf, buf_len, key_l, iv, val, 8)) { + return false; + } uint8_t tmp[8]; fmcos_des8_ecb_dec(key_r, val, tmp); fmcos_des8_ecb_enc(key_l, tmp, val); - if (mac_len > 8) + if (mac_len > 8) { mac_len = 8; + } memcpy(mac_out, val, mac_len); + return true; } @@ -374,31 +416,36 @@ static void fmcos_3des_mac(const uint8_t *buf, size_t buf_len, // iv is the 8-byte GET CHALLENGE response used as the CBC IV. // key_len 8 -> fmcos_des_mac; key_len 16 -> fmcos_3des_mac. // Lc encodes payload_len + 4 (reserves space for the MAC itself). -static void fmcos_packet_mac(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, +static bool fmcos_packet_mac(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, const uint8_t *data, size_t data_len, const uint8_t *iv, const uint8_t *key, size_t key_len, uint8_t *mac_out) { size_t buf_len = 5 + data_len; uint8_t *mac_buf = calloc(buf_len, 1); - if (mac_buf == NULL) - return; + if (mac_buf == NULL) { + PrintAndLogEx(ERR, "calloc failed"); + return false; + } mac_buf[0] = cla; mac_buf[1] = ins; mac_buf[2] = p1; mac_buf[3] = p2; mac_buf[4] = (uint8_t)((data_len + 4) & 0xFF); - if (data_len > 0) + if (data_len > 0) { memcpy(&mac_buf[5], data, data_len); + } + bool ok; if (key_len == 8) { - fmcos_des_mac(mac_buf, buf_len, key, iv, mac_out, 4); + ok = fmcos_des_mac(mac_buf, buf_len, key, iv, mac_out, 4); } else { - fmcos_3des_mac(mac_buf, buf_len, key, iv, mac_out, 4); + ok = fmcos_3des_mac(mac_buf, buf_len, key, iv, mac_out, 4); } free(mac_buf); + return ok; } // --------------------------------------------------------------------------- @@ -407,11 +454,17 @@ static void fmcos_packet_mac(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, // Remove ISO7816 padding: scan backwards for 0x80 marker. static int fmcos_iso7816_unpad(const uint8_t *buf, size_t padded_len, size_t *out_len) { - if (padded_len == 0 || padded_len % 8 != 0) + if (padded_len == 0 || padded_len % 8 != 0) { return PM3_ESOFT; + } for (size_t i = padded_len; i > 0; i--) { - if (buf[i - 1] == 0x80) { *out_len = i - 1; return PM3_SUCCESS; } - if (buf[i - 1] != 0x00) break; + if (buf[i - 1] == 0x80) { + *out_len = i - 1; + return PM3_SUCCESS; + } + if (buf[i - 1] != 0x00) { + break; + } } PrintAndLogEx(ERR, "ISO7816 padding marker not found"); return PM3_ESOFT; @@ -422,9 +475,14 @@ static int fmcos_iso7816_unpad(const uint8_t *buf, size_t padded_len, size_t *ou static size_t fmcos_encrypt(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, uint8_t *out) { uint8_t padded[512] = {0}; + if (data_len + 8 > sizeof(padded)) { + PrintAndLogEx(ERR, "fmcos_encrypt: data too large (%zu bytes)", data_len); + return 0; + } size_t padded_len = fmcos_iso7816_pad(data, data_len, padded); - for (size_t off = 0; off < padded_len; off += 8) + for (size_t off = 0; off < padded_len; off += 8) { fmcos_ecb_encrypt(key, key_len, padded + off, out + off); + } return padded_len; } @@ -502,8 +560,9 @@ static int fmcos_get_challenge(uint8_t chal_len, bool activate, uint8_t *chal_ou int resp_len = 0; int res = fmcos_send_apdu(apdu, sizeof(apdu), activate, true, resp, &resp_len); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { return res; + } if (resp_len < 2) { PrintAndLogEx(ERR, "Empty response to GET CHALLENGE"); @@ -533,15 +592,16 @@ static const uint8_t *fmcos_tlv_find(const uint8_t *buf, size_t len, uint8_t tag, size_t *vlen) { size_t i = 0; while (i + 1 < len) { - uint8_t t = buf[i++]; - uint8_t l = buf[i++]; - if (i + l > len) + uint8_t cur_tag = buf[i++]; + uint8_t cur_len = buf[i++]; + if (i + cur_len > len) { break; - if (t == tag) { - *vlen = l; + } + if (cur_tag == tag) { + *vlen = cur_len; return &buf[i]; } - i += l; + i += cur_len; } return NULL; } @@ -707,13 +767,17 @@ static int CmdHFFmcosSelect(const char *Cmd) { } if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } if (resp_len < 2) { PrintAndLogEx(ERR, "Empty card response"); - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } @@ -728,10 +792,29 @@ static int CmdHFFmcosSelect(const char *Cmd) { } } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } +// Parse a required hex integer argument; returns false and prints an error on +// malformed input. Accepts bare hex digits (no "0x" prefix required). +static bool fmcos_parse_hex_int(const char *s, int *out) { + if (s == NULL || *s == '\0') { + PrintAndLogEx(ERR, "Empty hex value"); + return false; + } + char *end = NULL; + long v = strtol(s, &end, 16); + if (end == s || *end != '\0') { + PrintAndLogEx(ERR, "Invalid hex value: %s", s); + return false; + } + *out = (int)v; + return true; +} + // --------------------------------------------------------------------------- // hf fmcos auth external (Phase 2) // --------------------------------------------------------------------------- @@ -755,7 +838,11 @@ static int CmdHFFmcosAuthExternal(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int key_id = (int)strtol(arg_get_str(ctx, 1)->sval[0], NULL, 16); + int key_id; + if (!fmcos_parse_hex_int(arg_get_str(ctx, 1)->sval[0], &key_id)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t key[16] = {0}; int key_len = 0; @@ -780,7 +867,9 @@ static int CmdHFFmcosAuthExternal(const char *Cmd) { uint8_t challenge[8] = {0}; int res = fmcos_get_challenge(8, true, challenge); if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } @@ -790,7 +879,9 @@ static int CmdHFFmcosAuthExternal(const char *Cmd) { uint8_t encrypted[8] = {0}; res = fmcos_ecb_encrypt(key, (size_t)key_len, challenge, encrypted); if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } @@ -807,13 +898,17 @@ static int CmdHFFmcosAuthExternal(const char *Cmd) { int ea_resp_len = 0; res = fmcos_send_apdu(ea_apdu, sizeof(ea_apdu), false, keep, ea_resp, &ea_resp_len); if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } if (ea_resp_len < 2) { PrintAndLogEx(ERR, "Empty response to EXTERNAL AUTHENTICATE"); - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } @@ -827,7 +922,9 @@ static int CmdHFFmcosAuthExternal(const char *Cmd) { PrintAndLogEx(FAILED, "External authentication " _RED_("failed")); } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -884,13 +981,17 @@ static int CmdHFFmcosAuthInternal(const char *Cmd) { int resp_len = 0; int res = fmcos_send_apdu(apdu, (size_t)(5 + data_len), true, keep, resp, &resp_len); if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } if (resp_len < 2) { PrintAndLogEx(ERR, "Empty response to INTERNAL AUTHENTICATE"); - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } @@ -909,7 +1010,9 @@ static int CmdHFFmcosAuthInternal(const char *Cmd) { PrintAndLogEx(FAILED, "Internal authentication " _RED_("failed")); } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -963,26 +1066,37 @@ static int fmcos_write_cmd(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, if (prot == FMCOS_PROT_ENC) { uint8_t enc_in[256]; + if (data_len >= sizeof(enc_in)) { + PrintAndLogEx(ERR, "Write data too large for ENC mode (%zu bytes, max 255)", data_len); + return PM3_EINVARG; + } enc_in[0] = (uint8_t)(data_len & 0xFF); memcpy(enc_in + 1, data, data_len); payload_len = fmcos_encrypt(key, key_len, enc_in, 1 + data_len, payload); + if (payload_len == 0) { + return PM3_EMALLOC; + } cla |= 0x04; } else { memcpy(payload, data, data_len); payload_len = data_len; - if (prot == FMCOS_PROT_MAC) + if (prot == FMCOS_PROT_MAC) { cla |= 0x04; + } } if (prot != FMCOS_PROT_NONE) { uint8_t chal[8] = {0}; int res = fmcos_get_challenge(8, activate, chal); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { return res; + } activate = false; uint8_t mac[4] = {0}; - fmcos_packet_mac(cla, ins, p1, p2, payload, payload_len, chal, key, key_len, mac); + if (!fmcos_packet_mac(cla, ins, p1, p2, payload, payload_len, chal, key, key_len, mac)) { + return PM3_EMALLOC; + } memcpy(payload + payload_len, mac, 4); payload_len += 4; } @@ -1011,8 +1125,9 @@ static int fmcos_read_cmd(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t read_len, if (prot != FMCOS_PROT_NONE) { cla = 0x04; int res = fmcos_get_challenge(8, activate, mac_iv); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { return res; + } activate = false; } @@ -1021,7 +1136,9 @@ static int fmcos_read_cmd(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t read_len, if (prot != FMCOS_PROT_NONE) { // Case 4: CLA INS P1 P2 Lc=4 [MAC4] Le uint8_t mac[4] = {0}; - fmcos_packet_mac(cla, ins, p1, p2, NULL, 0, mac_iv, key, key_len, mac); + if (!fmcos_packet_mac(cla, ins, p1, p2, NULL, 0, mac_iv, key, key_len, mac)) { + return PM3_EMALLOC; + } apdu[0] = cla; apdu[1] = ins; apdu[2] = p1; @@ -1043,15 +1160,18 @@ static int fmcos_read_cmd(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t read_len, uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; int res = fmcos_send_apdu(apdu, apdu_len, activate, leave_on, resp, &resp_len); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { return res; - if (resp_len < 2) + } + if (resp_len < 2) { return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 != 0x90 || sw2 != 0x00) + if (sw1 != 0x90 || sw2 != 0x00) { return PM3_ESOFT; + } int data_len = resp_len - 2; @@ -1070,10 +1190,15 @@ static int fmcos_read_cmd(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t read_len, uint8_t *ret_mac = resp + msg_len; uint8_t calc_mac[4] = {0}; - if (key_len == 8) - fmcos_des_mac(resp, (size_t)msg_len, key, mac_iv, calc_mac, 4); - else - fmcos_3des_mac(resp, (size_t)msg_len, key, mac_iv, calc_mac, 4); + bool mac_ok; + if (key_len == 8) { + mac_ok = fmcos_des_mac(resp, (size_t)msg_len, key, mac_iv, calc_mac, 4); + } else { + mac_ok = fmcos_3des_mac(resp, (size_t)msg_len, key, mac_iv, calc_mac, 4); + } + if (!mac_ok) { + return PM3_EMALLOC; + } if (memcmp(calc_mac, ret_mac, 4) != 0) { PrintAndLogEx(ERR, "Response MAC " _RED_("mismatch")); @@ -1085,13 +1210,18 @@ static int fmcos_read_cmd(uint8_t ins, uint8_t p1, uint8_t p2, uint8_t read_len, uint8_t plain[256] = {0}; size_t plain_len = 0; res = fmcos_decrypt(key, key_len, resp, (size_t)msg_len, plain, &plain_len); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { return res; + } if (plain_len == 0) { PrintAndLogEx(ERR, "Decrypted length byte missing"); return PM3_ESOFT; } uint8_t actual_len = plain[0]; + if (actual_len > plain_len - 1) { + PrintAndLogEx(ERR, "Decrypted length byte %u exceeds payload %zu", actual_len, plain_len - 1); + return PM3_ESOFT; + } memcpy(data_out, plain + 1, actual_len); *data_out_len = (int)actual_len; } else { @@ -1124,17 +1254,30 @@ static int CmdHFFmcosErase(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; int res = fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "DF " _GREEN_("erased")); - else + } else { PrintAndLogEx(FAILED, "Erase " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -1160,7 +1303,11 @@ static int CmdHFFmcosCreateDir(const char *Cmd) { uint8_t id_buf[2] = {0}; int id_len = 0; CLIGetHexWithReturn(ctx, 1, id_buf, &id_len); - int space = (int)strtol(arg_get_str(ctx, 2)->sval[0], NULL, 16); + int space; + if (!fmcos_parse_hex_int(arg_get_str(ctx, 2)->sval[0], &space)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t cperm[1] = {0}; int cperm_len = 0; CLIGetHexWithReturn(ctx, 3, cperm, &cperm_len); @@ -1177,8 +1324,14 @@ static int CmdHFFmcosCreateDir(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 8); CLIParserFree(ctx); - if (id_len != 2) { PrintAndLogEx(ERR, "--id must be 2 bytes"); return PM3_EINVARG; } - if (space < 1 || space > 0xFFFF) { PrintAndLogEx(ERR, "--space out of range"); return PM3_EINVARG; } + if (id_len != 2) { + PrintAndLogEx(ERR, "--id must be 2 bytes"); + return PM3_EINVARG; + } + if (space < 1 || space > 0xFFFF) { + PrintAndLogEx(ERR, "--space out of range"); + return PM3_EINVARG; + } if (cperm_len != 1 || eperm_len != 1 || appid_len != 1) { PrintAndLogEx(ERR, "--cperm, --eperm, --appid must each be 1 byte"); return PM3_EINVARG; @@ -1195,7 +1348,10 @@ static int CmdHFFmcosCreateDir(const char *Cmd) { data[data_len++] = appid[0]; data[data_len++] = 0xFF; data[data_len++] = 0xFF; - if (name_len > 0) { memcpy(data + data_len, name, (size_t)name_len); data_len += (size_t)name_len; } + if (name_len > 0) { + memcpy(data + data_len, name, (size_t)name_len); + data_len += (size_t)name_len; + } uint8_t apdu[30]; apdu[0] = 0x80; @@ -1208,16 +1364,29 @@ static int CmdHFFmcosCreateDir(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; int res = fmcos_send_apdu(apdu, 5 + data_len, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "Directory " _GREEN_("created")); - else + } else { PrintAndLogEx(FAILED, "Create directory " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -1274,8 +1443,16 @@ static int CmdHFFmcosCreateFile(const char *Cmd) { int id_len = 0; CLIGetHexWithReturn(ctx, 1, id_buf, &id_len); int ftype = 0; - if (CLIGetOptionList(arg_get_str(ctx, 2), g_fmcos_filetype_opts, &ftype)) { CLIParserFree(ctx); return PM3_EINVARG; } - int size = (int)strtol(arg_get_str(ctx, 3)->sval[0], NULL, 16); + if (CLIGetOptionList(arg_get_str(ctx, 2), g_fmcos_filetype_opts, &ftype)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int size; + if (!fmcos_parse_hex_int(arg_get_str(ctx, 3)->sval[0], &size)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t rperm[1] = {0}; int rperm_len = 0; CLIGetHexWithReturn(ctx, 4, rperm, &rperm_len); @@ -1286,13 +1463,23 @@ static int CmdHFFmcosCreateFile(const char *Cmd) { int access_len = 0; CLIGetHexWithReturn(ctx, 6, access, &access_len); int prot = FMCOS_PROT_NONE; - if (CLIGetOptionList(arg_get_str(ctx, 7), g_fmcos_prot_opts, &prot)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 7), g_fmcos_prot_opts, &prot)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + bool keep = arg_get_lit(ctx, 8); bool apdu_log = arg_get_lit(ctx, 9); CLIParserFree(ctx); - if (id_len != 2) { PrintAndLogEx(ERR, "--id must be 2 bytes"); return PM3_EINVARG; } - if (size < 1 || size > 0xFFFF) { PrintAndLogEx(ERR, "--size out of range"); return PM3_EINVARG; } + if (id_len != 2) { + PrintAndLogEx(ERR, "--id must be 2 bytes"); + return PM3_EINVARG; + } + if (size < 1 || size > 0xFFFF) { + PrintAndLogEx(ERR, "--size out of range"); + return PM3_EINVARG; + } if (rperm_len != 1 || wperm_len != 1 || access_len != 1) { PrintAndLogEx(ERR, "--rperm, --wperm, --access must each be 1 byte"); return PM3_EINVARG; @@ -1322,16 +1509,29 @@ static int CmdHFFmcosCreateFile(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; int res = fmcos_send_apdu(apdu, 12, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "File " _GREEN_("created")); - else + } else { PrintAndLogEx(FAILED, "Create file " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -1355,7 +1555,11 @@ static int CmdHFFmcosCreateKeyfile(const char *Cmd) { uint8_t id_buf[2] = {0}; int id_len = 0; CLIGetHexWithReturn(ctx, 1, id_buf, &id_len); - int space = (int)strtol(arg_get_str(ctx, 2)->sval[0], NULL, 16); + int space; + if (!fmcos_parse_hex_int(arg_get_str(ctx, 2)->sval[0], &space)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t dfsid[1] = {0}; int dfsid_len = 0; CLIGetHexWithReturn(ctx, 3, dfsid, &dfsid_len); @@ -1366,8 +1570,14 @@ static int CmdHFFmcosCreateKeyfile(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 6); CLIParserFree(ctx); - if (id_len != 2) { PrintAndLogEx(ERR, "--id must be 2 bytes"); return PM3_EINVARG; } - if (space < 1 || space > 0xFFFF) { PrintAndLogEx(ERR, "--space out of range"); return PM3_EINVARG; } + if (id_len != 2) { + PrintAndLogEx(ERR, "--id must be 2 bytes"); + return PM3_EINVARG; + } + if (space < 1 || space > 0xFFFF) { + PrintAndLogEx(ERR, "--space out of range"); + return PM3_EINVARG; + } if (dfsid_len != 1 || perm_len != 1) { PrintAndLogEx(ERR, "--dfsid and --perm must each be 1 byte"); return PM3_EINVARG; @@ -1391,16 +1601,29 @@ static int CmdHFFmcosCreateKeyfile(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; int res = fmcos_send_apdu(apdu, 12, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "Keyfile " _GREEN_("created")); - else + } else { PrintAndLogEx(FAILED, "Create keyfile " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -1458,7 +1681,11 @@ static int CmdHFFmcosReadBinary(const char *Cmd) { CLIGetHexWithReturn(ctx, 2, p2b, &p2_len); int rlen = arg_get_int_def(ctx, 3, 0); int prot = FMCOS_PROT_NONE; - if (CLIGetOptionList(arg_get_str(ctx, 4), g_fmcos_prot_opts, &prot)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 4), g_fmcos_prot_opts, &prot)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + uint8_t key[16] = {0}; int key_len = 0; CLIGetHexWithReturn(ctx, 5, key, &key_len); @@ -1466,10 +1693,19 @@ static int CmdHFFmcosReadBinary(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 7); CLIParserFree(ctx); - if (p1_len != 1 || p2_len != 1) { PrintAndLogEx(ERR, "--p1 and --p2 must each be 1 byte"); return PM3_EINVARG; } - if (rlen < 0 || rlen > 255) { PrintAndLogEx(ERR, "--len must be 0-255"); return PM3_EINVARG; } + if (p1_len != 1 || p2_len != 1) { + PrintAndLogEx(ERR, "--p1 and --p2 must each be 1 byte"); + return PM3_EINVARG; + } + if (rlen < 0 || rlen > 255) { + PrintAndLogEx(ERR, "--len must be 0-255"); + return PM3_EINVARG; + } if (prot != FMCOS_PROT_NONE) { - if (key_len != 8 && key_len != 16) { PrintAndLogEx(ERR, "--key must be 8 or 16 bytes when --prot is set"); return PM3_EINVARG; } + if (key_len != 8 && key_len != 16) { + PrintAndLogEx(ERR, "--key must be 8 or 16 bytes when --prot is set"); + return PM3_EINVARG; + } } SetAPDULogging(apdu_log); @@ -1478,9 +1714,12 @@ static int CmdHFFmcosReadBinary(const char *Cmd) { int res = fmcos_read_cmd(0xB0, p1b[0], p2b[0], (uint8_t)rlen, prot, key, (size_t)key_len, true, keep, data_out, &data_out_len); - if (res == PM3_SUCCESS) + if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Data: " _GREEN_("%s"), sprint_hex(data_out, (size_t)data_out_len)); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return res; } @@ -1513,7 +1752,10 @@ static int CmdHFFmcosReadRecord(const char *Cmd) { CLIGetHexWithReturn(ctx, 2, fid_buf, &fid_len); int rlen = arg_get_int_def(ctx, 3, 0); int prot = FMCOS_PROT_NONE; - if (CLIGetOptionList(arg_get_str(ctx, 4), g_fmcos_prot_opts, &prot)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 4), g_fmcos_prot_opts, &prot)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t key[16] = {0}; int key_len = 0; CLIGetHexWithReturn(ctx, 5, key, &key_len); @@ -1522,11 +1764,23 @@ static int CmdHFFmcosReadRecord(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 8); CLIParserFree(ctx); - if (rec < 1 || rec > 255) { PrintAndLogEx(ERR, "--rec must be 1-255"); return PM3_EINVARG; } - if (fid_len != 1) { PrintAndLogEx(ERR, "--fid must be 1 byte"); return PM3_EINVARG; } - if (rlen < 0 || rlen > 253) { PrintAndLogEx(ERR, "--len must be 0-253"); return PM3_EINVARG; } + if (rec < 1 || rec > 255) { + PrintAndLogEx(ERR, "--rec must be 1-255"); + return PM3_EINVARG; + } + if (fid_len != 1) { + PrintAndLogEx(ERR, "--fid must be 1 byte"); + return PM3_EINVARG; + } + if (rlen < 0 || rlen > 253) { + PrintAndLogEx(ERR, "--len must be 0-253"); + return PM3_EINVARG; + } if (prot != FMCOS_PROT_NONE) { - if (key_len != 8 && key_len != 16) { PrintAndLogEx(ERR, "--key must be 8 or 16 bytes when --prot is set"); return PM3_EINVARG; } + if (key_len != 8 && key_len != 16) { + PrintAndLogEx(ERR, "--key must be 8 or 16 bytes when --prot is set"); + return PM3_EINVARG; + } } SetAPDULogging(apdu_log); @@ -1552,7 +1806,9 @@ static int CmdHFFmcosReadRecord(const char *Cmd) { } PrintAndLogEx(SUCCESS, "Record: " _GREEN_("%s"), sprint_hex(payload, (size_t)payload_len)); } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } @@ -1606,7 +1862,10 @@ static int CmdHFFmcosWriteBinary(const char *Cmd) { int wdata_len = 0; CLIGetHexWithReturn(ctx, 3, wdata, &wdata_len); int prot = FMCOS_PROT_NONE; - if (CLIGetOptionList(arg_get_str(ctx, 4), g_fmcos_prot_opts, &prot)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 4), g_fmcos_prot_opts, &prot)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t key[16] = {0}; int key_len = 0; CLIGetHexWithReturn(ctx, 5, key, &key_len); @@ -1614,8 +1873,14 @@ static int CmdHFFmcosWriteBinary(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 7); CLIParserFree(ctx); - if (p1_len != 1 || p2_len != 1) { PrintAndLogEx(ERR, "--p1 and --p2 must each be 1 byte"); return PM3_EINVARG; } - if (wdata_len < 1) { PrintAndLogEx(ERR, "--data required"); return PM3_EINVARG; } + if (p1_len != 1 || p2_len != 1) { + PrintAndLogEx(ERR, "--p1 and --p2 must each be 1 byte"); + return PM3_EINVARG; + } + if (wdata_len < 1) { + PrintAndLogEx(ERR, "--data required"); + return PM3_EINVARG; + } if (prot != FMCOS_PROT_NONE && key_len != 8 && key_len != 16) { PrintAndLogEx(ERR, "--key must be 8 or 16 bytes when --prot is set"); return PM3_EINVARG; @@ -1628,16 +1893,29 @@ static int CmdHFFmcosWriteBinary(const char *Cmd) { wdata, (size_t)wdata_len, prot, key, (size_t)key_len, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "Binary " _GREEN_("written")); - else + } else { PrintAndLogEx(FAILED, "Write binary " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -1672,7 +1950,10 @@ static int CmdHFFmcosWriteRecord(const char *Cmd) { int wdata_len = 0; CLIGetHexWithReturn(ctx, 3, wdata, &wdata_len); int prot = FMCOS_PROT_NONE; - if (CLIGetOptionList(arg_get_str(ctx, 4), g_fmcos_prot_opts, &prot)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 4), g_fmcos_prot_opts, &prot)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t key[16] = {0}; int key_len = 0; CLIGetHexWithReturn(ctx, 5, key, &key_len); @@ -1681,10 +1962,22 @@ static int CmdHFFmcosWriteRecord(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 8); CLIParserFree(ctx); - if (rec < 1 || rec > 255) { PrintAndLogEx(ERR, "--rec must be 1-255"); return PM3_EINVARG; } - if (fid_len != 1) { PrintAndLogEx(ERR, "--fid must be 1 byte"); return PM3_EINVARG; } - if (wdata_len < 1) { PrintAndLogEx(ERR, "--data required"); return PM3_EINVARG; } - if (use_tlv && wdata_len > 243) { PrintAndLogEx(ERR, "--data too long for TLV write (max 243 bytes)"); return PM3_EINVARG; } + if (rec < 1 || rec > 255) { + PrintAndLogEx(ERR, "--rec must be 1-255"); + return PM3_EINVARG; + } + if (fid_len != 1) { + PrintAndLogEx(ERR, "--fid must be 1 byte"); + return PM3_EINVARG; + } + if (wdata_len < 1) { + PrintAndLogEx(ERR, "--data required"); + return PM3_EINVARG; + } + if (use_tlv && wdata_len > 243) { + PrintAndLogEx(ERR, "--data too long for TLV write (max 243 bytes)"); + return PM3_EINVARG; + } if (prot != FMCOS_PROT_NONE && key_len != 8 && key_len != 16) { PrintAndLogEx(ERR, "--key must be 8 or 16 bytes when --prot is set"); return PM3_EINVARG; @@ -1711,16 +2004,29 @@ static int CmdHFFmcosWriteRecord(const char *Cmd) { send_buf, send_len, prot, key, (size_t)key_len, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "Record " _GREEN_("written")); - else + } else { PrintAndLogEx(FAILED, "Write record " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -1770,7 +2076,10 @@ static int CmdHFFmcosAppend(const char *Cmd) { int wdata_len = 0; CLIGetHexWithReturn(ctx, 2, wdata, &wdata_len); int prot = FMCOS_PROT_NONE; - if (CLIGetOptionList(arg_get_str(ctx, 3), g_fmcos_prot_opts, &prot)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 3), g_fmcos_prot_opts, &prot)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t key[16] = {0}; int key_len = 0; CLIGetHexWithReturn(ctx, 4, key, &key_len); @@ -1778,8 +2087,14 @@ static int CmdHFFmcosAppend(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 6); CLIParserFree(ctx); - if (fid_len != 1) { PrintAndLogEx(ERR, "--fid must be 1 byte"); return PM3_EINVARG; } - if (wdata_len < 1) { PrintAndLogEx(ERR, "--data required"); return PM3_EINVARG; } + if (fid_len != 1) { + PrintAndLogEx(ERR, "--fid must be 1 byte"); + return PM3_EINVARG; + } + if (wdata_len < 1) { + PrintAndLogEx(ERR, "--data required"); + return PM3_EINVARG; + } if (prot != FMCOS_PROT_NONE && key_len != 8 && key_len != 16) { PrintAndLogEx(ERR, "--key must be 8 or 16 bytes when --prot is set"); return PM3_EINVARG; @@ -1794,16 +2109,29 @@ static int CmdHFFmcosAppend(const char *Cmd) { wdata, (size_t)wdata_len, prot, key, (size_t)key_len, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "Record " _GREEN_("appended")); - else + } else { PrintAndLogEx(FAILED, "Append record " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -1865,7 +2193,10 @@ static int CmdHFFmcosWriteKey(const char *Cmd) { int id_len = 0; CLIGetHexWithReturn(ctx, 2, id_buf, &id_len); int ktype = 0; - if (CLIGetOptionList(arg_get_str(ctx, 3), g_fmcos_keytype_opts, &ktype)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 3), g_fmcos_keytype_opts, &ktype)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t usage_b[1] = {0}; int usage_len = 0; CLIGetHexWithReturn(ctx, 4, usage_b, &usage_len); @@ -1891,17 +2222,32 @@ static int CmdHFFmcosWriteKey(const char *Cmd) { int authkey_len = 0; CLIGetHexWithReturn(ctx, 11, authkey, &authkey_len); int prot = FMCOS_PROT_NONE; - if (CLIGetOptionList(arg_get_str(ctx, 12), g_fmcos_prot_opts, &prot)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 12), g_fmcos_prot_opts, &prot)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } bool keep = arg_get_lit(ctx, 13); bool apdu_log = arg_get_lit(ctx, 14); CLIParserFree(ctx); - if (op_len != 1 || id_len != 1) { PrintAndLogEx(ERR, "--op and --id must each be 1 byte"); return PM3_EINVARG; } - if (usage_len != 1) { PrintAndLogEx(ERR, "--usage must be 1 byte"); return PM3_EINVARG; } + if (op_len != 1 || id_len != 1) { + PrintAndLogEx(ERR, "--op and --id must each be 1 byte"); + return PM3_EINVARG; + } + if (usage_len != 1) { + PrintAndLogEx(ERR, "--usage must be 1 byte"); + return PM3_EINVARG; + } if (ktype == 0x3A) { - if (kval_len < 2 || kval_len > 6) { PrintAndLogEx(ERR, "--key for pin type must be 2-6 bytes (PIN value)"); return PM3_EINVARG; } + if (kval_len < 2 || kval_len > 6) { + PrintAndLogEx(ERR, "--key for pin type must be 2-6 bytes (PIN value)"); + return PM3_EINVARG; + } } else { - if (kval_len != 8 && kval_len != 16) { PrintAndLogEx(ERR, "--key must be 8 or 16 bytes"); return PM3_EINVARG; } + if (kval_len != 8 && kval_len != 16) { + PrintAndLogEx(ERR, "--key must be 8 or 16 bytes"); + return PM3_EINVARG; + } } if (prot != FMCOS_PROT_NONE && authkey_len != 8 && authkey_len != 16) { PrintAndLogEx(ERR, "--authkey must be 8 or 16 bytes when --prot is set"); @@ -1925,9 +2271,8 @@ static int CmdHFFmcosWriteKey(const char *Cmd) { data[data_len++] = change_b[0]; data[data_len++] = version_b[0]; data[data_len++] = algo_b[0]; - } // Group B-extauth: extauth - else if (ktype == 0x39) { + } else if (ktype == 0x39) { if (change_len != 1 || followup_len != 1 || errcnt_len != 1) { PrintAndLogEx(ERR, "extauth key type needs --change --followup --errcount"); return PM3_EINVARG; @@ -1935,9 +2280,8 @@ static int CmdHFFmcosWriteKey(const char *Cmd) { data[data_len++] = change_b[0]; data[data_len++] = followup_b[0]; data[data_len++] = errcnt_b[0]; - } // Group B-pin: pin - else if (ktype == 0x3A) { + } else if (ktype == 0x3A) { if (followup_len != 1 || errcnt_len != 1) { PrintAndLogEx(ERR, "pin key type needs --followup --errcount"); return PM3_EINVARG; @@ -1945,9 +2289,8 @@ static int CmdHFFmcosWriteKey(const char *Cmd) { data[data_len++] = 0xEF; data[data_len++] = followup_b[0]; data[data_len++] = errcnt_b[0]; - } // Group C: lineprotect unlockpin changepin - else if (ktype == 0x36 || ktype == 0x37 || ktype == 0x38) { + } else if (ktype == 0x36 || ktype == 0x37 || ktype == 0x38) { if (change_len != 1 || errcnt_len != 1) { PrintAndLogEx(ERR, "This key type needs --change --errcount"); return PM3_EINVARG; @@ -1972,16 +2315,29 @@ static int CmdHFFmcosWriteKey(const char *Cmd) { data, data_len, prot, authkey, (size_t)authkey_len, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "Key " _GREEN_("written")); - else + } else { PrintAndLogEx(FAILED, "Write key " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -2016,8 +2372,14 @@ static int CmdHFFmcosPinVerify(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 4); CLIParserFree(ctx); - if (id_len != 1) { PrintAndLogEx(ERR, "--id must be 1 byte"); return PM3_EINVARG; } - if (pin_len < 2 || pin_len > 6) { PrintAndLogEx(ERR, "--pin must be 2-6 bytes"); return PM3_EINVARG; } + if (id_len != 1) { + PrintAndLogEx(ERR, "--id must be 1 byte"); + return PM3_EINVARG; + } + if (pin_len < 2 || pin_len > 6) { + PrintAndLogEx(ERR, "--pin must be 2-6 bytes"); + return PM3_EINVARG; + } SetAPDULogging(apdu_log); // CLA=00 INS=20 P1=00 P2=key_id Lc=pin_len Data=pin @@ -2032,19 +2394,32 @@ static int CmdHFFmcosPinVerify(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; int res = fmcos_send_apdu(apdu, 5 + (size_t)pin_len, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "PIN " _GREEN_("verified")); - else if (sw1 == 0x63) + } else if (sw1 == 0x63) { PrintAndLogEx(FAILED, "Wrong PIN, retries remaining: %d", sw2 & 0x0F); - else + } else { PrintAndLogEx(FAILED, "PIN verify " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -2079,9 +2454,18 @@ static int CmdHFFmcosPinChange(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 5); CLIParserFree(ctx); - if (id_len != 1) { PrintAndLogEx(ERR, "--id must be 1 byte"); return PM3_EINVARG; } - if (old_len < 2 || old_len > 6) { PrintAndLogEx(ERR, "--old must be 2-6 bytes"); return PM3_EINVARG; } - if (new_len < 2 || new_len > 6) { PrintAndLogEx(ERR, "--new must be 2-6 bytes"); return PM3_EINVARG; } + if (id_len != 1) { + PrintAndLogEx(ERR, "--id must be 1 byte"); + return PM3_EINVARG; + } + if (old_len < 2 || old_len > 6) { + PrintAndLogEx(ERR, "--old must be 2-6 bytes"); + return PM3_EINVARG; + } + if (new_len < 2 || new_len > 6) { + PrintAndLogEx(ERR, "--new must be 2-6 bytes"); + return PM3_EINVARG; + } SetAPDULogging(apdu_log); // Data = old_pin + 0xFF + new_pin @@ -2104,17 +2488,30 @@ static int CmdHFFmcosPinChange(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; int res = fmcos_send_apdu(apdu, 5 + data_len, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "PIN " _GREEN_("changed")); - else + } else { PrintAndLogEx(FAILED, "PIN change " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -2150,20 +2547,32 @@ static int CmdHFFmcosPinReset(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 5); CLIParserFree(ctx); - if (id_len != 1) { PrintAndLogEx(ERR, "--id must be 1 byte"); return PM3_EINVARG; } - if (pin_len < 2 || pin_len > 6) { PrintAndLogEx(ERR, "--pin must be 2-6 bytes"); return PM3_EINVARG; } - if (key_len != 16) { PrintAndLogEx(ERR, "--key must be 16 bytes (change-PIN key)"); return PM3_EINVARG; } + if (id_len != 1) { + PrintAndLogEx(ERR, "--id must be 1 byte"); + return PM3_EINVARG; + } + if (pin_len < 2 || pin_len > 6) { + PrintAndLogEx(ERR, "--pin must be 2-6 bytes"); + return PM3_EINVARG; + } + if (key_len != 16) { + PrintAndLogEx(ERR, "--key must be 16 bytes (change-PIN key)"); + return PM3_EINVARG; + } SetAPDULogging(apdu_log); // mac_key = key_left XOR key_right uint8_t mac_key[8]; - for (int i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) { mac_key[i] = key[i] ^ key[i + 8]; + } // MAC = DES-CBC-MAC(new_pin, mac_key, iv=0) uint8_t zero_iv[8] = {0}; uint8_t mac[4] = {0}; - fmcos_des_mac(pin, (size_t)pin_len, mac_key, zero_iv, mac, 4); + if (!fmcos_des_mac(pin, (size_t)pin_len, mac_key, zero_iv, mac, 4)) { + return PM3_EMALLOC; + } // Data = new_pin + mac[4] uint8_t data[10]; @@ -2182,17 +2591,30 @@ static int CmdHFFmcosPinReset(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; int res = fmcos_send_apdu(apdu, 5 + data_len, true, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "PIN " _GREEN_("reset")); - else + } else { PrintAndLogEx(FAILED, "PIN reset " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -2228,9 +2650,18 @@ static int CmdHFFmcosPinUnblock(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 5); CLIParserFree(ctx); - if (id_len != 1) { PrintAndLogEx(ERR, "--id must be 1 byte"); return PM3_EINVARG; } - if (pin_len < 2 || pin_len > 6) { PrintAndLogEx(ERR, "--pin must be 2-6 bytes"); return PM3_EINVARG; } - if (key_len != 8 && key_len != 16) { PrintAndLogEx(ERR, "--key must be 8 or 16 bytes"); return PM3_EINVARG; } + if (id_len != 1) { + PrintAndLogEx(ERR, "--id must be 1 byte"); + return PM3_EINVARG; + } + if (pin_len < 2 || pin_len > 6) { + PrintAndLogEx(ERR, "--pin must be 2-6 bytes"); + return PM3_EINVARG; + } + if (key_len != 8 && key_len != 16) { + PrintAndLogEx(ERR, "--key must be 8 or 16 bytes"); + return PM3_EINVARG; + } SetAPDULogging(apdu_log); // Encrypt [len_byte | pin] with the unlock key @@ -2243,11 +2674,18 @@ static int CmdHFFmcosPinUnblock(const char *Cmd) { // GET CHALLENGE for packet MAC IV - CLA=84, INS=24 uint8_t chal[8] = {0}; int res = fmcos_get_challenge(8, true, chal); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } uint8_t mac[4] = {0}; - fmcos_packet_mac(0x84, 0x24, id_buf[0], 0x00, - enc_data, enc_len, chal, key, (size_t)key_len, mac); + if (!fmcos_packet_mac(0x84, 0x24, id_buf[0], 0x00, + enc_data, enc_len, chal, key, (size_t)key_len, mac)) { + return PM3_EMALLOC; + } // Build data = enc_data + mac uint8_t data[20]; @@ -2266,17 +2704,30 @@ static int CmdHFFmcosPinUnblock(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; res = fmcos_send_apdu(apdu, 5 + data_len, false, keep, resp, &resp_len); - if (res != PM3_SUCCESS) { if (!keep) DropField(); return res; } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (res != PM3_SUCCESS) { + if (!keep) { + DropField(); + } + return res; + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "PIN " _GREEN_("unblocked")); - else + } else { PrintAndLogEx(FAILED, "PIN unblock " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -2312,10 +2763,12 @@ static void fmcos_get_datetime_bcd(uint8_t date_out[4], uint8_t time_out[3]) { char ds[9], ts[7]; strftime(ds, sizeof(ds), "%Y%m%d", t); strftime(ts, sizeof(ts), "%H%M%S", t); - for (int i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { date_out[i] = (uint8_t)((((ds[i * 2] - '0') & 0xF) << 4) | ((ds[i * 2 + 1] - '0') & 0xF)); - for (int i = 0; i < 3; i++) + } + for (int i = 0; i < 3; i++) { time_out[i] = (uint8_t)((((ts[i * 2] - '0') & 0xF) << 4) | ((ts[i * 2 + 1] - '0') & 0xF)); + } } // --------------------------------------------------------------------------- @@ -2341,14 +2794,17 @@ static int CmdHFFmcosBalance(const char *Cmd) { bool keep = arg_get_lit(ctx, 2); CLIParserFree(ctx); - if (res != PM3_SUCCESS) return PM3_EINVARG; + if (res != PM3_SUCCESS) { + return PM3_EINVARG; + } uint8_t apdu[5] = {0x80, 0x5C, 0x00, (uint8_t)bal_type, 0x04}; uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } if (resp_len < 2) { PrintAndLogEx(ERR, "Short response"); @@ -2391,7 +2847,11 @@ static int CmdHFFmcosCredit(const char *Cmd) { int bal_type = 0; int res = CLIGetOptionList(arg_get_str(ctx, 1), g_fmcos_baltype_opts, &bal_type); - int key_id = (int)strtol(arg_get_str(ctx, 2)->sval[0], NULL, 16); + int key_id; + if (!fmcos_parse_hex_int(arg_get_str(ctx, 2)->sval[0], &key_id)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } int amount_i = arg_get_int(ctx, 3); uint8_t terminal[6] = {0}; @@ -2409,12 +2869,30 @@ static int CmdHFFmcosCredit(const char *Cmd) { bool keep = arg_get_lit(ctx, 7); CLIParserFree(ctx); - if (res != PM3_SUCCESS) return PM3_EINVARG; - if (key_id < 0 || key_id > 0xFF) { PrintAndLogEx(ERR, "Key ID must be 0-255"); return PM3_EINVARG; } - if (amount_i <= 0) { PrintAndLogEx(ERR, "Amount must be positive"); return PM3_EINVARG; } - if (terminal_len != 6) { PrintAndLogEx(ERR, "Terminal ID must be 6 bytes"); return PM3_EINVARG; } - if (crde_key_len != 16) { PrintAndLogEx(ERR, "Credit key must be 16 bytes"); return PM3_EINVARG; } - if (ikey_len != 16) { PrintAndLogEx(ERR, "Internal key must be 16 bytes"); return PM3_EINVARG; } + if (res != PM3_SUCCESS) { + return PM3_EINVARG; + } + + if (key_id < 0 || key_id > 0xFF) { + PrintAndLogEx(ERR, "Key ID must be 0-255"); + return PM3_EINVARG; + } + if (amount_i <= 0) { + PrintAndLogEx(ERR, "Amount must be positive"); + return PM3_EINVARG; + } + if (terminal_len != 6) { + PrintAndLogEx(ERR, "Terminal ID must be 6 bytes"); + return PM3_EINVARG; + } + if (crde_key_len != 16) { + PrintAndLogEx(ERR, "Credit key must be 16 bytes"); + return PM3_EINVARG; + } + if (ikey_len != 16) { + PrintAndLogEx(ERR, "Internal key must be 16 bytes"); + return PM3_EINVARG; + } uint32_t amount = (uint32_t)amount_i; uint8_t tx_type = (uint8_t)bal_type; // transaction_type == balance_type for credit @@ -2438,22 +2916,29 @@ static int CmdHFFmcosCredit(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(ph1, sizeof(ph1), true, true, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(ph1, sizeof(ph1), true, true, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } // Response: old_balance[4] online_serial[2] key_ver[1] algo[1] random_1[4] mac_1[4] SW[2] if (resp_len < 18) { - if (resp_len >= 2) fmcos_print_sw(resp[resp_len - 2], resp[resp_len - 1]); + if (resp_len >= 2) { + fmcos_print_sw(resp[resp_len - 2], resp[resp_len - 1]); + } PrintAndLogEx(ERR, "Phase 1 short response (%d bytes) -- DF selected?", resp_len); g_fmcos_session_active = keep; - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; if (sw1 != 0x90 || sw2 != 0x00) { fmcos_print_sw(sw1, sw2); g_fmcos_session_active = keep; - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } @@ -2483,12 +2968,16 @@ static int CmdHFFmcosCredit(const char *Cmd) { uint8_t zero_iv[8] = {0}; uint8_t mac1_calc[4] = {0}; - fmcos_des_mac(mac1_buf, 15, process_key, zero_iv, mac1_calc, 4); + if (!fmcos_des_mac(mac1_buf, 15, process_key, zero_iv, mac1_calc, 4)) { + return PM3_EMALLOC; + } if (memcmp(mac1_calc, resp + 12, 4) != 0) { PrintAndLogEx(ERR, "MAC1 mismatch - card response invalid"); g_fmcos_session_active = keep; - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } PrintAndLogEx(INFO, "MAC1 OK old balance %u", old_balance); @@ -2508,7 +2997,9 @@ static int CmdHFFmcosCredit(const char *Cmd) { memcpy(mac2_buf + 15, ttime, 3); uint8_t mac2[4] = {0}; - fmcos_des_mac(mac2_buf, 18, process_key, zero_iv, mac2, 4); + if (!fmcos_des_mac(mac2_buf, 18, process_key, zero_iv, mac2, 4)) { + return PM3_EMALLOC; + } // ---- Phase 2: CREDIT (INS 52) ---- // APDU: CLA INS P1 P2 Lc[=11] date[4] time[3] mac2[4] Le[=4] @@ -2525,8 +3016,9 @@ static int CmdHFFmcosCredit(const char *Cmd) { memset(resp, 0, sizeof(resp)); resp_len = 0; - if (fmcos_send_apdu(ph2, sizeof(ph2), false, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(ph2, sizeof(ph2), false, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } // Response: TAC[4] SW[2] if (resp_len < 6) { @@ -2544,7 +3036,9 @@ static int CmdHFFmcosCredit(const char *Cmd) { // tac_key = XOR of the two 8-byte halves of the internal key uint32_t new_balance = old_balance + amount; uint8_t tac_key[8]; - for (int i = 0; i < 8; i++) tac_key[i] = ikey[i] ^ ikey[i + 8]; + for (int i = 0; i < 8; i++) { + tac_key[i] = ikey[i] ^ ikey[i + 8]; + } uint8_t tac_buf[24]; tac_buf[0] = (new_balance >> 24) & 0xFF; @@ -2555,7 +3049,9 @@ static int CmdHFFmcosCredit(const char *Cmd) { memcpy(tac_buf + 6, mac2_buf, 18); uint8_t tac_calc[4] = {0}; - fmcos_des_mac(tac_buf, 24, tac_key, zero_iv, tac_calc, 4); + if (!fmcos_des_mac(tac_buf, 24, tac_key, zero_iv, tac_calc, 4)) { + return PM3_EMALLOC; + } if (memcmp(tac_calc, resp, 4) != 0) { PrintAndLogEx(WARNING, "TAC mismatch - new balance %u may be incorrect", new_balance); @@ -2593,7 +3089,11 @@ static int CmdHFFmcosPurchase(const char *Cmd) { int bal_type = 0; int res = CLIGetOptionList(arg_get_str(ctx, 1), g_fmcos_baltype_opts, &bal_type); - int key_id = (int)strtol(arg_get_str(ctx, 2)->sval[0], NULL, 16); + int key_id; + if (!fmcos_parse_hex_int(arg_get_str(ctx, 2)->sval[0], &key_id)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } int amount_i = arg_get_int(ctx, 3); uint8_t terminal[6] = {0}; @@ -2615,13 +3115,34 @@ static int CmdHFFmcosPurchase(const char *Cmd) { bool keep = arg_get_lit(ctx, 8); CLIParserFree(ctx); - if (res != PM3_SUCCESS) return PM3_EINVARG; - if (key_id < 0 || key_id > 0xFF) { PrintAndLogEx(ERR, "Key ID must be 0-255"); return PM3_EINVARG; } - if (amount_i <= 0) { PrintAndLogEx(ERR, "Amount must be positive"); return PM3_EINVARG; } - if (terminal_len != 6) { PrintAndLogEx(ERR, "Terminal ID must be 6 bytes"); return PM3_EINVARG; } - if (purch_key_len != 16) { PrintAndLogEx(ERR, "Purchase key must be 16 bytes"); return PM3_EINVARG; } - if (ikey_len != 16) { PrintAndLogEx(ERR, "Internal key must be 16 bytes"); return PM3_EINVARG; } - if (serial_len != 0 && serial_len != 4) { PrintAndLogEx(ERR, "Serial must be 4 bytes"); return PM3_EINVARG; } + if (res != PM3_SUCCESS) { + return PM3_EINVARG; + } + + if (key_id < 0 || key_id > 0xFF) { + PrintAndLogEx(ERR, "Key ID must be 0-255"); + return PM3_EINVARG; + } + if (amount_i <= 0) { + PrintAndLogEx(ERR, "Amount must be positive"); + return PM3_EINVARG; + } + if (terminal_len != 6) { + PrintAndLogEx(ERR, "Terminal ID must be 6 bytes"); + return PM3_EINVARG; + } + if (purch_key_len != 16) { + PrintAndLogEx(ERR, "Purchase key must be 16 bytes"); + return PM3_EINVARG; + } + if (ikey_len != 16) { + PrintAndLogEx(ERR, "Internal key must be 16 bytes"); + return PM3_EINVARG; + } + if (serial_len != 0 && serial_len != 4) { + PrintAndLogEx(ERR, "Serial must be 4 bytes"); + return PM3_EINVARG; + } uint32_t amount = (uint32_t)amount_i; // transaction_type: 0x05 passbook purchase, 0x06 wallet purchase @@ -2646,22 +3167,29 @@ static int CmdHFFmcosPurchase(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(ph1, sizeof(ph1), true, true, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(ph1, sizeof(ph1), true, true, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } // Response: old_balance[4] offline_serial[2] overdraft_lim[3] key_ver[1] algo[1] random_1[4] SW[2] if (resp_len < 17) { - if (resp_len >= 2) fmcos_print_sw(resp[resp_len - 2], resp[resp_len - 1]); + if (resp_len >= 2) { + fmcos_print_sw(resp[resp_len - 2], resp[resp_len - 1]); + } PrintAndLogEx(ERR, "Phase 1 short response (%d bytes) -- DF selected?", resp_len); g_fmcos_session_active = keep; - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; if (sw1 != 0x90 || sw2 != 0x00) { fmcos_print_sw(sw1, sw2); g_fmcos_session_active = keep; - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } @@ -2696,7 +3224,9 @@ static int CmdHFFmcosPurchase(const char *Cmd) { uint8_t zero_iv[8] = {0}; uint8_t mac1[4] = {0}; - fmcos_des_mac(mac1_buf, 18, process_key, zero_iv, mac1, 4); + if (!fmcos_des_mac(mac1_buf, 18, process_key, zero_iv, mac1, 4)) { + return PM3_EMALLOC; + } // ---- Phase 2: DEBIT (INS 54, P1 01, P2 00) ---- // APDU: CLA INS P1 P2 Lc[=15] tx_serial[4] date[4] time[3] mac1[4] Le[=8] @@ -2714,8 +3244,9 @@ static int CmdHFFmcosPurchase(const char *Cmd) { memset(resp, 0, sizeof(resp)); resp_len = 0; - if (fmcos_send_apdu(ph2, sizeof(ph2), false, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(ph2, sizeof(ph2), false, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } // Response: TAC[4] mac2_card[4] SW[2] if (resp_len < 10) { @@ -2733,7 +3264,9 @@ static int CmdHFFmcosPurchase(const char *Cmd) { // tac_key = XOR of the two 8-byte halves of the internal key uint32_t new_balance = old_balance - amount; uint8_t tac_key[8]; - for (int i = 0; i < 8; i++) tac_key[i] = ikey[i] ^ ikey[i + 8]; + for (int i = 0; i < 8; i++) { + tac_key[i] = ikey[i] ^ ikey[i + 8]; + } uint8_t tac_buf[22]; tac_buf[0] = (amount >> 24) & 0xFF; @@ -2747,7 +3280,9 @@ static int CmdHFFmcosPurchase(const char *Cmd) { memcpy(tac_buf + 19, ttime, 3); uint8_t tac_calc[4] = {0}; - fmcos_des_mac(tac_buf, 22, tac_key, zero_iv, tac_calc, 4); + if (!fmcos_des_mac(tac_buf, 22, tac_key, zero_iv, tac_calc, 4)) { + return PM3_EMALLOC; + } if (memcmp(tac_calc, resp, 4) != 0) { PrintAndLogEx(WARNING, "TAC mismatch - new balance %u may be incorrect", new_balance); @@ -2781,7 +3316,11 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int key_id = (int)strtol(arg_get_str(ctx, 1)->sval[0], NULL, 16); + int key_id; + if (!fmcos_parse_hex_int(arg_get_str(ctx, 1)->sval[0], &key_id)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } int limit_i = arg_get_int(ctx, 2); uint8_t terminal[6] = {0}; @@ -2799,11 +3338,26 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { bool keep = arg_get_lit(ctx, 6); CLIParserFree(ctx); - if (key_id < 0 || key_id > 0xFF) { PrintAndLogEx(ERR, "Key ID must be 0-255"); return PM3_EINVARG; } - if (limit_i < 0 || limit_i > 0xFFFFFF) { PrintAndLogEx(ERR, "Limit must be 0-16777215"); return PM3_EINVARG; } - if (terminal_len != 6) { PrintAndLogEx(ERR, "Terminal ID must be 6 bytes"); return PM3_EINVARG; } - if (od_key_len != 16) { PrintAndLogEx(ERR, "Overdraft key must be 16 bytes"); return PM3_EINVARG; } - if (ikey_len != 0 && ikey_len != 16) { PrintAndLogEx(ERR, "Internal key must be 16 bytes"); return PM3_EINVARG; } + if (key_id < 0 || key_id > 0xFF) { + PrintAndLogEx(ERR, "Key ID must be 0-255"); + return PM3_EINVARG; + } + if (limit_i < 0 || limit_i > 0xFFFFFF) { + PrintAndLogEx(ERR, "Limit must be 0-16777215"); + return PM3_EINVARG; + } + if (terminal_len != 6) { + PrintAndLogEx(ERR, "Terminal ID must be 6 bytes"); + return PM3_EINVARG; + } + if (od_key_len != 16) { + PrintAndLogEx(ERR, "Overdraft key must be 16 bytes"); + return PM3_EINVARG; + } + if (ikey_len != 0 && ikey_len != 16) { + PrintAndLogEx(ERR, "Internal key must be 16 bytes"); + return PM3_EINVARG; + } bool verify_tac = (ikey_len == 16); uint32_t new_limit = (uint32_t)limit_i; @@ -2823,22 +3377,29 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(ph1, sizeof(ph1), true, true, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(ph1, sizeof(ph1), true, true, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } // Response: old_balance[4] online_serial[2] old_od_limit[3] key_ver[1] algo[1] random_1[4] card_mac1[4] SW[2] if (resp_len < 21) { - if (resp_len >= 2) fmcos_print_sw(resp[resp_len - 2], resp[resp_len - 1]); + if (resp_len >= 2) { + fmcos_print_sw(resp[resp_len - 2], resp[resp_len - 1]); + } PrintAndLogEx(ERR, "Phase 1 short response (%d bytes) -- DF selected?", resp_len); g_fmcos_session_active = keep; - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; if (sw1 != 0x90 || sw2 != 0x00) { fmcos_print_sw(sw1, sw2); g_fmcos_session_active = keep; - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } @@ -2867,12 +3428,16 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { uint8_t zero_iv[8] = {0}; uint8_t mac1_calc[4] = {0}; - fmcos_des_mac(mac1_buf, 14, process_key, zero_iv, mac1_calc, 4); + if (!fmcos_des_mac(mac1_buf, 14, process_key, zero_iv, mac1_calc, 4)) { + return PM3_EMALLOC; + } if (memcmp(mac1_calc, resp + 15, 4) != 0) { PrintAndLogEx(ERR, "MAC1 mismatch - card response invalid"); g_fmcos_session_active = keep; - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } PrintAndLogEx(INFO, "MAC1 OK"); @@ -2891,7 +3456,9 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { memcpy(mac2_buf + 14, ttime, 3); uint8_t mac2[4] = {0}; - fmcos_des_mac(mac2_buf, 17, process_key, zero_iv, mac2, 4); + if (!fmcos_des_mac(mac2_buf, 17, process_key, zero_iv, mac2, 4)) { + return PM3_EMALLOC; + } // ---- Phase 2: UPDATE OVERDRAFT (INS 58, P1 00, P2 00) ---- // APDU: CLA INS P1 P2 Lc[=14] new_limit[3] date[4] time[3] mac2[4] Le[=4] @@ -2911,8 +3478,9 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { memset(resp, 0, sizeof(resp)); resp_len = 0; - if (fmcos_send_apdu(ph2, sizeof(ph2), false, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(ph2, sizeof(ph2), false, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } if (resp_len < 2) { PrintAndLogEx(ERR, "Phase 2 short response"); @@ -2922,8 +3490,9 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 != 0x90 || sw2 != 0x00) + if (sw1 != 0x90 || sw2 != 0x00) { return PM3_ESOFT; + } PrintAndLogEx(SUCCESS, "Overdraft limit updated to " _GREEN_("%u"), new_limit); @@ -2933,7 +3502,9 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { } else { // TAC key: XOR of the two 8-byte halves of the internal key (same as credit/purchase) uint8_t tac_key[8]; - for (int i = 0; i < 8; i++) tac_key[i] = ikey[i] ^ ikey[i + 8]; + for (int i = 0; i < 8; i++) { + tac_key[i] = ikey[i] ^ ikey[i + 8]; + } // TAC buffer: tac_bal[4]|serial[2]|new_limit[3]|0x07[1]|terminal[6]|date[4]|time[3] // The card stores (actual_funds + od_limit) as its balance field. When the limit @@ -2954,7 +3525,9 @@ static int CmdHFFmcosOverdraft(const char *Cmd) { memcpy(tac_buf + 20, ttime, 3); uint8_t tac_calc[4] = {0}; - fmcos_des_mac(tac_buf, 23, tac_key, zero_iv, tac_calc, 4); + if (!fmcos_des_mac(tac_buf, 23, tac_key, zero_iv, tac_calc, 4)) { + return PM3_EMALLOC; + } if (memcmp(tac_calc, resp, 4) != 0) { PrintAndLogEx(WARNING, "TAC mismatch - overdraft limit update may be unverified"); @@ -3022,7 +3595,9 @@ static int CmdHFFmcosBlock(const char *Cmd) { uint8_t chal[8] = {0}; int res = fmcos_get_challenge(8, true, chal); if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } @@ -3040,7 +3615,9 @@ static int CmdHFFmcosBlock(const char *Cmd) { uint8_t p1 = 0x00; uint8_t mac[4] = {0}; - fmcos_packet_mac(cla, ins, p1, p2, NULL, 0, chal, key, (size_t)key_len, mac); + if (!fmcos_packet_mac(cla, ins, p1, p2, NULL, 0, chal, key, (size_t)key_len, mac)) { + return PM3_EMALLOC; + } uint8_t apdu[9]; apdu[0] = cla; @@ -3054,13 +3631,17 @@ static int CmdHFFmcosBlock(const char *Cmd) { int resp_len = 0; res = fmcos_send_apdu(apdu, sizeof(apdu), false, keep, resp, &resp_len); if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } if (resp_len < 2) { PrintAndLogEx(ERR, "Empty response"); - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } @@ -3079,7 +3660,9 @@ static int CmdHFFmcosBlock(const char *Cmd) { PrintAndLogEx(FAILED, "Block command " _RED_("failed")); } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -3118,13 +3701,17 @@ static int CmdHFFmcosUnblock(const char *Cmd) { uint8_t chal[8] = {0}; int res = fmcos_get_challenge(8, true, chal); if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } // APP UNBLOCK: CLA=84 INS=18 P1=00 P2=00 uint8_t mac[4] = {0}; - fmcos_packet_mac(0x84, 0x18, 0x00, 0x00, NULL, 0, chal, key, (size_t)key_len, mac); + if (!fmcos_packet_mac(0x84, 0x18, 0x00, 0x00, NULL, 0, chal, key, (size_t)key_len, mac)) { + return PM3_EMALLOC; + } uint8_t apdu[9]; apdu[0] = 0x84; @@ -3138,13 +3725,17 @@ static int CmdHFFmcosUnblock(const char *Cmd) { int resp_len = 0; res = fmcos_send_apdu(apdu, sizeof(apdu), false, keep, resp, &resp_len); if (res != PM3_SUCCESS) { - if (!keep) DropField(); + if (!keep) { + DropField(); + } return res; } if (resp_len < 2) { PrintAndLogEx(ERR, "Empty response"); - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_ESOFT; } @@ -3158,7 +3749,9 @@ static int CmdHFFmcosUnblock(const char *Cmd) { PrintAndLogEx(FAILED, "Unblock command " _RED_("failed")); } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -3192,9 +3785,17 @@ static int CmdHFFmcosHistory(const char *Cmd) { bool apdu_log = arg_get_lit(ctx, 4); CLIParserFree(ctx); - if (fid_len != 1) { PrintAndLogEx(ERR, "--fid must be 1 byte"); return PM3_EINVARG; } - if (count < 0 || count > 255) { PrintAndLogEx(ERR, "--count must be 0-255"); return PM3_EINVARG; } - if (count == 0) count = 255; + if (fid_len != 1) { + PrintAndLogEx(ERR, "--fid must be 1 byte"); + return PM3_EINVARG; + } + if (count < 0 || count > 255) { + PrintAndLogEx(ERR, "--count must be 0-255"); + return PM3_EINVARG; + } + if (count == 0) { + count = 255; + } SetAPDULogging(apdu_log); @@ -3219,16 +3820,23 @@ static int CmdHFFmcosHistory(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, sizeof(apdu), true, true, resp, &resp_len) != PM3_SUCCESS) - break; - - if (resp_len < 2) break; - uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; - if (sw1 != 0x90 || sw2 != 0x00) { - if (rec == 1) fmcos_print_sw(sw1, sw2); + if (fmcos_send_apdu(apdu, sizeof(apdu), true, true, resp, &resp_len) != PM3_SUCCESS) { + break; + } + + if (resp_len < 2) { + break; + } + uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; + if (sw1 != 0x90 || sw2 != 0x00) { + if (rec == 1) { + fmcos_print_sw(sw1, sw2); + } + break; + } + if (resp_len < 25) { break; } - if (resp_len < 25) break; uint8_t *p = resp; uint16_t serial = ((uint16_t)p[0] << 8) | p[1]; @@ -3253,7 +3861,10 @@ static int CmdHFFmcosHistory(const char *Cmd) { snprintf(type_hex_buf, sizeof(type_hex_buf), "0x%02X ", tx_type); const char *type_name = type_hex_buf; for (size_t i = 0; i < ntypes; i++) { - if (tx_types[i].code == tx_type) { type_name = tx_types[i].name; break; } + if (tx_types[i].code == tx_type) { + type_name = tx_types[i].name; + break; + } } PrintAndLogEx(INFO, "%2d | %s | %s | %s | %10u | %8u | %06X | %s", @@ -3263,12 +3874,15 @@ static int CmdHFFmcosHistory(const char *Cmd) { found++; } - if (!keep) DropField(); + if (!keep) { + DropField(); + } - if (found == 0) + if (found == 0) { PrintAndLogEx(INFO, "(no records found)"); - else + } else { PrintAndLogEx(SUCCESS, "%d record%s", found, found == 1 ? "" : "s"); + } return PM3_SUCCESS; } @@ -3316,18 +3930,27 @@ static int CmdHFFmcosTidSetCard(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "SET CARD " _GREEN_("OK")); - else + } else { PrintAndLogEx(FAILED, "SET CARD " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -3360,18 +3983,27 @@ static int CmdHFFmcosTidSetUID(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, 5 + (size_t)uid_len, true, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(apdu, 5 + (size_t)uid_len, true, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "SET UID " _GREEN_("OK")); - else + } else { PrintAndLogEx(FAILED, "SET UID " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -3410,19 +4042,28 @@ static int CmdHFFmcosTidSetAuth(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "SET INTERNAL AUTH " _GREEN_("OK") " (key %s)", lock ? _RED_("locked") : "unlocked"); - else + } else { PrintAndLogEx(FAILED, "SET INTERNAL AUTH " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -3445,18 +4086,27 @@ static int CmdHFFmcosTidErase(const char *Cmd) { uint8_t apdu[5] = {0xE0, 0xEC, 0x00, 0x00, 0x00}; uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; + } - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "ERASE " _GREEN_("OK")); - else + } else { PrintAndLogEx(FAILED, "ERASE " _RED_("failed")); + } - if (!keep) DropField(); + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -3487,8 +4137,14 @@ static int CmdHFFmcosTidProvision(const char *Cmd) { bool keep = arg_get_lit(ctx, 4); CLIParserFree(ctx); - if (uid_len < 4 || uid_len > 7) { PrintAndLogEx(ERR, "--uid must be 4-7 bytes"); return PM3_EINVARG; } - if (key_len != 8) { PrintAndLogEx(ERR, "--key must be 8 bytes"); return PM3_EINVARG; } + if (uid_len < 4 || uid_len > 7) { + PrintAndLogEx(ERR, "--uid must be 4-7 bytes"); + return PM3_EINVARG; + } + if (key_len != 8) { + PrintAndLogEx(ERR, "--key must be 8 bytes"); + return PM3_EINVARG; + } uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; @@ -3507,67 +4163,69 @@ static int CmdHFFmcosTidProvision(const char *Cmd) { // 1 - SET CARD { - uint8_t a[44] = {0x00, 0xEF, 0x00, 0x00, 0x27}; - memcpy(a + 5, g_fmcos_tid_setcard_data, 39); - TID_STEP("SET CARD", a, sizeof(a), true, true); + uint8_t apdu[44] = {0x00, 0xEF, 0x00, 0x00, 0x27}; + memcpy(apdu + 5, g_fmcos_tid_setcard_data, 39); + TID_STEP("SET CARD", apdu, sizeof(apdu), true, true); } // 2 - SET UID { - uint8_t a[12] = {0x00, 0x85, 0x00, 0x00, (uint8_t)uid_len}; - memcpy(a + 5, uid, (size_t)uid_len); - TID_STEP("SET UID", a, 5 + (size_t)uid_len, true, true); + uint8_t apdu[12] = {0x00, 0x85, 0x00, 0x00, (uint8_t)uid_len}; + memcpy(apdu + 5, uid, (size_t)uid_len); + TID_STEP("SET UID", apdu, 5 + (size_t)uid_len, true, true); } // 3 - SET INTERNAL AUTH { - uint8_t a[15] = {0x00, 0x21, 0x00, 0x00, 0x0A}; - memcpy(a + 5, key, 8); - a[13] = lock ? FMCOS_TID_AUTH_LOCKED : FMCOS_TID_AUTH_UNLOCKED; - a[14] = 0x00; - TID_STEP("SET INTERNAL AUTH", a, sizeof(a), true, true); + uint8_t apdu[15] = {0x00, 0x21, 0x00, 0x00, 0x0A}; + memcpy(apdu + 5, key, 8); + apdu[13] = lock ? FMCOS_TID_AUTH_LOCKED : FMCOS_TID_AUTH_UNLOCKED; + apdu[14] = 0x00; + TID_STEP("SET INTERNAL AUTH", apdu, sizeof(apdu), true, true); } // 4 - ERASE PrintAndLogEx(WARNING, "Erasing card file system..."); { - uint8_t a[5] = {0xE0, 0xEC, 0x00, 0x00, 0x00}; - TID_STEP("ERASE", a, sizeof(a), true, true); + uint8_t apdu[5] = {0xE0, 0xEC, 0x00, 0x00, 0x00}; + TID_STEP("ERASE", apdu, sizeof(apdu), true, true); } // 5 - SELECT MF (3F00) { - uint8_t a[7] = {0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00}; - TID_STEP("SELECT MF", a, sizeof(a), true, true); + uint8_t apdu[7] = {0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00}; + TID_STEP("SELECT MF", apdu, sizeof(apdu), true, true); } // 6 - CREATE DF (3F00, SFI=02, name="1PAY.SYS.DDF01") { uint8_t name_len = (uint8_t)sizeof(g_fmcos_tid_mf_name); - uint8_t a[32] = {0x80, 0xE0, 0x00, 0x00, (uint8_t)(name_len + 9), - 0x3F, 0x00, 0x6F, 0xFF, 0xF0, 0xF0, 0x02, 0x01, 0x00 - }; - memcpy(a + 14, g_fmcos_tid_mf_name, name_len); - TID_STEP("CREATE DF (3F00)", a, 14 + name_len, true, true); + uint8_t apdu[32] = {0x80, 0xE0, 0x00, 0x00, (uint8_t)(name_len + 9), + 0x3F, 0x00, 0x6F, 0xFF, 0xF0, 0xF0, 0x02, 0x01, 0x00 + }; + memcpy(apdu + 14, g_fmcos_tid_mf_name, name_len); + TID_STEP("CREATE DF (3F00)", apdu, 14 + name_len, true, true); } // 7 - SELECT DF 3F00 { - uint8_t a[7] = {0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00}; - TID_STEP("SELECT DF (3F00)", a, sizeof(a), true, true); + uint8_t apdu[7] = {0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00}; + TID_STEP("SELECT DF (3F00)", apdu, sizeof(apdu), true, true); } // 8 - CREATE KEYFILE { - uint8_t a[16] = {0x80, 0xE0, 0x02, 0x00, 0x0B}; - memcpy(a + 5, g_fmcos_tid_keyfile_data, 11); - TID_STEP("CREATE KEYFILE", a, sizeof(a), true, keep); + uint8_t apdu[16] = {0x80, 0xE0, 0x02, 0x00, 0x0B}; + memcpy(apdu + 5, g_fmcos_tid_keyfile_data, 11); + TID_STEP("CREATE KEYFILE", apdu, sizeof(apdu), true, keep); } #undef TID_STEP PrintAndLogEx(SUCCESS, "TID provisioning " _GREEN_("complete")); - if (!keep) DropField(); + if (!keep) { + DropField(); + } return PM3_SUCCESS; tid_provision_fail: @@ -3594,7 +4252,11 @@ static int CmdHFFmcosTidCreateDF(const char *Cmd) { uint8_t id_buf[2] = {0}; int id_len = 0; CLIGetHexWithReturn(ctx, 1, id_buf, &id_len); - int size = (int)strtol(arg_get_str(ctx, 2)->sval[0], NULL, 16); + int size; + if (!fmcos_parse_hex_int(arg_get_str(ctx, 2)->sval[0], &size)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t sfi_buf[1] = {0}; int sfi_len = 0; CLIGetHexWithReturn(ctx, 3, sfi_buf, &sfi_len); @@ -3604,9 +4266,18 @@ static int CmdHFFmcosTidCreateDF(const char *Cmd) { bool keep = arg_get_lit(ctx, 5); CLIParserFree(ctx); - if (id_len != 2) { PrintAndLogEx(ERR, "--id must be 2 bytes"); return PM3_EINVARG; } - if (sfi_len != 1) { PrintAndLogEx(ERR, "--sfi must be 1 byte"); return PM3_EINVARG; } - if (size < 1 || size > 0xFFFF) { PrintAndLogEx(ERR, "--size out of range"); return PM3_EINVARG; } + if (id_len != 2) { + PrintAndLogEx(ERR, "--id must be 2 bytes"); + return PM3_EINVARG; + } + if (sfi_len != 1) { + PrintAndLogEx(ERR, "--sfi must be 1 byte"); + return PM3_EINVARG; + } + if (size < 1 || size > 0xFFFF) { + PrintAndLogEx(ERR, "--size out of range"); + return PM3_EINVARG; + } // 80 E0 01 00 [fid_hi][fid_lo] [size_hi][size_lo] F0 F0 [sfi] 01 FF [name...] uint8_t apdu[32] = { @@ -3621,16 +4292,25 @@ static int CmdHFFmcosTidCreateDF(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, 14 + (size_t)name_len, true, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(apdu, 14 + (size_t)name_len, true, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "CREATE DF " _GREEN_("OK")); - else + } else { PrintAndLogEx(FAILED, "CREATE DF " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -3663,15 +4343,22 @@ static int CmdHFFmcosTidCreateBin(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); int ftype = 0; - if (CLIGetOptionList(arg_get_str(ctx, 1), g_fmcos_tid_create_opts, &ftype)) { CLIParserFree(ctx); return PM3_EINVARG; } + if (CLIGetOptionList(arg_get_str(ctx, 1), g_fmcos_tid_create_opts, &ftype)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } bool is_keyfile = (ftype == 1); uint8_t id_buf[2] = {0}; int id_len = 0; CLIGetHexWithReturn(ctx, 2, id_buf, &id_len); int size = 0; - if (arg_get_str(ctx, 3)->count > 0) - size = (int)strtol(arg_get_str(ctx, 3)->sval[0], NULL, 16); + if (arg_get_str(ctx, 3)->count > 0) { + if (!fmcos_parse_hex_int(arg_get_str(ctx, 3)->sval[0], &size)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + } uint8_t sfi_buf[1] = {0}; int sfi_len = 0; CLIGetHexWithReturn(ctx, 4, sfi_buf, &sfi_len); @@ -3690,11 +4377,24 @@ static int CmdHFFmcosTidCreateBin(const char *Cmd) { // Fixed TID keyfile: 80 E0 02 00 0B 1E 00 00 00 30 FF FF 00 30 00 00 memcpy(apdu + 5, g_fmcos_tid_keyfile_data, 11); } else { - if (rperm_len == 0) rperm_buf[0] = 0xF0; - if (wperm_len == 0) wperm_buf[0] = 0xF0; - if (id_len != 2) { PrintAndLogEx(ERR, "--id must be 2 bytes"); return PM3_EINVARG; } - if (sfi_len != 1) { PrintAndLogEx(ERR, "--sfi must be 1 byte"); return PM3_EINVARG; } - if (size < 1 || size > 0xFFFF) { PrintAndLogEx(ERR, "--size out of range"); return PM3_EINVARG; } + if (rperm_len == 0) { + rperm_buf[0] = 0xF0; + } + if (wperm_len == 0) { + wperm_buf[0] = 0xF0; + } + if (id_len != 2) { + PrintAndLogEx(ERR, "--id must be 2 bytes"); + return PM3_EINVARG; + } + if (sfi_len != 1) { + PrintAndLogEx(ERR, "--sfi must be 1 byte"); + return PM3_EINVARG; + } + if (size < 1 || size > 0xFFFF) { + PrintAndLogEx(ERR, "--size out of range"); + return PM3_EINVARG; + } // 80 E0 02 00 0B 00 [fid_hi][fid_lo] [size_hi][size_lo] [rperm][wperm] [sfi] 00 FF 00 apdu[5] = 0x00; apdu[6] = id_buf[0]; @@ -3711,16 +4411,25 @@ static int CmdHFFmcosTidCreateBin(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, is_keyfile ? "CREATE KEYFILE " _GREEN_("OK") : "CREATE binary EF " _GREEN_("OK")); - else + } else { PrintAndLogEx(FAILED, is_keyfile ? "CREATE KEYFILE " _RED_("failed") : "CREATE binary EF " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; } @@ -3764,13 +4473,29 @@ static int CmdHFFmcosTidCreateRec(const char *Cmd) { bool keep = arg_get_lit(ctx, 7); CLIParserFree(ctx); - if (rperm_len == 0) rperm_buf[0] = 0xF0; - if (wperm_len == 0) wperm_buf[0] = 0xF0; + if (rperm_len == 0) { + rperm_buf[0] = 0xF0; + } + if (wperm_len == 0) { + wperm_buf[0] = 0xF0; + } - if (id_len != 2) { PrintAndLogEx(ERR, "--id must be 2 bytes"); return PM3_EINVARG; } - if (cnt_len != 1) { PrintAndLogEx(ERR, "--count must be 1 byte"); return PM3_EINVARG; } - if (rlen_len != 1) { PrintAndLogEx(ERR, "--reclen must be 1 byte"); return PM3_EINVARG; } - if (sfi_len != 1) { PrintAndLogEx(ERR, "--sfi must be 1 byte"); return PM3_EINVARG; } + if (id_len != 2) { + PrintAndLogEx(ERR, "--id must be 2 bytes"); + return PM3_EINVARG; + } + if (cnt_len != 1) { + PrintAndLogEx(ERR, "--count must be 1 byte"); + return PM3_EINVARG; + } + if (rlen_len != 1) { + PrintAndLogEx(ERR, "--reclen must be 1 byte"); + return PM3_EINVARG; + } + if (sfi_len != 1) { + PrintAndLogEx(ERR, "--sfi must be 1 byte"); + return PM3_EINVARG; + } // 80 E0 02 00 0B 01 [fid_hi][fid_lo] [count][reclen] [rperm][wperm] [sfi] 00 FF 00 uint8_t apdu[16] = { @@ -3785,16 +4510,25 @@ static int CmdHFFmcosTidCreateRec(const char *Cmd) { uint8_t resp[APDU_RES_LEN] = {0}; int resp_len = 0; - if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) + if (fmcos_send_apdu(apdu, sizeof(apdu), true, keep, resp, &resp_len) != PM3_SUCCESS) { return PM3_ESOFT; - if (resp_len < 2) { if (!keep) DropField(); return PM3_ESOFT; } + } + if (resp_len < 2) { + if (!keep) { + DropField(); + } + return PM3_ESOFT; + } uint8_t sw1 = resp[resp_len - 2], sw2 = resp[resp_len - 1]; fmcos_print_sw(sw1, sw2); - if (sw1 == 0x90 && sw2 == 0x00) + if (sw1 == 0x90 && sw2 == 0x00) { PrintAndLogEx(SUCCESS, "CREATE record EF " _GREEN_("OK")); - else + } else { PrintAndLogEx(FAILED, "CREATE record EF " _RED_("failed")); - if (!keep) DropField(); + } + if (!keep) { + DropField(); + } return (sw1 == 0x90 && sw2 == 0x00) ? PM3_SUCCESS : PM3_ESOFT; }