diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 37c7561f2..dac0c4f80 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -287,6 +287,66 @@ static const char *felica_model_name(uint8_t rom_type, uint8_t ic_type) { return "Unknown IC Type"; } +static const char *felica_specification_option_name(size_t option_index) { + switch (option_index) { + case 0: + return "DES....................."; + case 1: + return "Special................."; + case 2: + return "Extended Overlap........"; + case 3: + return "Value-Limited Purse....."; + case 4: + return "Communication with MAC.."; + default: + return "Unknown................."; + } +} + +static void print_specification_versions(int level, + const felica_request_specification_version_info_t *specification_version_info, + bool include_hex) { + if (specification_version_info == NULL || specification_version_info->has_specification_version == false) { + return; + } + + uint8_t basic_major = specification_version_info->basic_version[1] & 0x0F; + uint8_t basic_minor = (specification_version_info->basic_version[0] >> 4) & 0x0F; + uint8_t basic_patch = specification_version_info->basic_version[0] & 0x0F; + PrintAndLogEx(level, "Versions:"); + + PrintAndLogEx(level, " Format version.......... " _GREEN_("%02X"), specification_version_info->format_version); + + PrintAndLogEx(level, " Option count............ " _GREEN_("%u"), specification_version_info->number_of_option); + + if (include_hex) { + PrintAndLogEx(level, " Specification........... " _GREEN_("%u.%u.%u") " (" _YELLOW_("0x%02X%02X") ")", + basic_major, basic_minor, basic_patch, + specification_version_info->basic_version[0], + specification_version_info->basic_version[1]); + } else { + PrintAndLogEx(level, " Specification........... " _GREEN_("%u.%u.%u"), basic_major, basic_minor, basic_patch); + } + + for (size_t i = 0; i < specification_version_info->option_version_count; i++) { + const uint8_t *option = specification_version_info->option_version_list + (i * 2); + uint8_t option_major = option[1] & 0x0F; + uint8_t option_minor = (option[0] >> 4) & 0x0F; + uint8_t option_patch = option[0] & 0x0F; + const char *option_name = felica_specification_option_name(i); + + if (include_hex) { + PrintAndLogEx(level, " %s " _GREEN_("%u.%u.%u") " (" _YELLOW_("0x%02X%02X") ")", + option_name, option_major, option_minor, option_patch, + option[0], option[1]); + } else { + PrintAndLogEx(level, " %s " _GREEN_("%u.%u.%u"), + option_name, option_major, option_minor, option_patch); + } + } +} + /** * Wait for response from pm3 or timeout. * Checks if receveid bytes have a valid CRC. @@ -582,6 +642,66 @@ static int send_get_platform_information(uint8_t flags, uint16_t datalen, uint8_ return PM3_SUCCESS; } +static int send_request_specification_version(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, + bool logging, uint32_t timeout_ms, uint32_t retries, + felica_request_specification_version_info_t *specification_version_info) { + if (specification_version_info == NULL) { + return PM3_EINVARG; + } + + memset(specification_version_info, 0, sizeof(*specification_version_info)); + + PacketResponseNG resp; + if (send_felica_payload_with_retries(flags, datalen, data, verbose, + FELICA_REQUEST_SPEC_VERSION_ACK, + timeout_ms, retries, + logging, &resp, "request specification version") != PM3_SUCCESS) { + return PM3_ERFTRANS; + } + + if (resp.length < sizeof(felica_status_response_t)) { + return PM3_ESOFT; + } + + const size_t status_offset = sizeof(felica_frame_response_t); + memcpy(&specification_version_info->status_flags, + resp.data.asBytes + status_offset, + sizeof(specification_version_info->status_flags)); + + if (specification_version_info->status_flags.status_flag1[0] != 0x00) { + return PM3_SUCCESS; + } + + const size_t specification_offset = sizeof(felica_status_response_t); + if (resp.length < specification_offset + 4) { + return PM3_ESOFT; + } + + specification_version_info->has_specification_version = true; + specification_version_info->format_version = resp.data.asBytes[specification_offset]; + memcpy(specification_version_info->basic_version, + resp.data.asBytes + specification_offset + 1, + sizeof(specification_version_info->basic_version)); + specification_version_info->number_of_option = resp.data.asBytes[specification_offset + 3]; + + const size_t option_bytes = (size_t)specification_version_info->number_of_option * 2U; + const size_t payload_bytes = resp.length - (specification_offset + 4); + if (payload_bytes < option_bytes) { + return PM3_ESOFT; + } + + size_t copy_option_bytes = option_bytes; + if (copy_option_bytes > sizeof(specification_version_info->option_version_list)) { + copy_option_bytes = sizeof(specification_version_info->option_version_list); + } + + memcpy(specification_version_info->option_version_list, + resp.data.asBytes + specification_offset + 4, + copy_option_bytes); + specification_version_info->option_version_count = copy_option_bytes / 2U; + return PM3_SUCCESS; +} + static int info_felica(bool verbose) { clear_and_send_command(FELICA_CONNECT | FELICA_NO_DISCONNECT, 0, NULL, false); @@ -660,6 +780,21 @@ static int info_felica(bool verbose) { PrintAndLogEx(INFO, "Platform info.. %s", sprint_hex_inrow(platform_information_data, platform_information_data_len)); } + felica_request_specification_version_request_t request_specification_version_request; + memset(&request_specification_version_request, 0, sizeof(request_specification_version_request)); + request_specification_version_request.length[0] = sizeof(request_specification_version_request); + request_specification_version_request.command_code[0] = FELICA_REQUEST_SPEC_VERSION_REQ; + memcpy(request_specification_version_request.IDm, card.IDm, sizeof(request_specification_version_request.IDm)); + + felica_request_specification_version_info_t specification_version_info; + if (send_request_specification_version(optional_flags, sizeof(request_specification_version_request), + (uint8_t *)&request_specification_version_request, false, + false, FELICA_OPTIONAL_CMD_TIMEOUT_MS, FELICA_OPTIONAL_CMD_RETRIES, + &specification_version_info) == PM3_SUCCESS && + specification_version_info.has_specification_version) { + print_specification_versions(INFO, &specification_version_info, true); + } + felica_get_container_id_request_t container_id_request; memset(&container_id_request, 0, sizeof(container_id_request)); container_id_request.length[0] = sizeof(container_id_request); @@ -1797,10 +1932,8 @@ static int CmdHFFelicaRequestSpecificationVersion(const char *Cmd) { "Response:\n" " - Format version: Fixed value 00h. Provided only if Status Flag1 = 00h\n" " - Basic version: Each value of version is expressed in BCD notation. Provided only if Status Flag1 = 00h\n" - " - Number of Option: value = 0: AES card, value = 1: AES/DES card. Provided only if Status Flag1 = 00h\n" - " - Option version list: Provided only if Status Flag1 = 00h\n" - " - AES card: not added\n" - " - AES/DES card: DES option version is added - BCD notation", + " - Number of Option: number of entries in Option Version List.\n" + " - Option version list: BCD notation (major.minor.patch), little-endian, provided only if Status Flag1 = 00h", "hf felica rqspecver\n" "hf felica rqspecver -r 0001\n" @@ -1838,63 +1971,58 @@ static int CmdHFFelicaRequestSpecificationVersion(const char *Cmd) { } CLIParserFree(ctx); - uint8_t data[PM3_CMD_DATA_SIZE]; - memset(data, 0, sizeof(data)); - data[0] = 0x0C; // Static length - data[1] = 0x3C; // Command ID + felica_request_specification_version_request_t request_specification_version_request; + memset(&request_specification_version_request, 0, sizeof(request_specification_version_request)); + request_specification_version_request.length[0] = sizeof(request_specification_version_request); + request_specification_version_request.command_code[0] = FELICA_REQUEST_SPEC_VERSION_REQ; bool custom_IDm = false; - - // add custom idm if (ilen) { custom_IDm = true; - memcpy(data + 2, idm, sizeof(idm)); + memcpy(request_specification_version_request.IDm, idm, sizeof(idm)); } - // add custom reserved if (rlen) { - memcpy(data + 10, reserved, sizeof(reserved)); - } else { - data[10] = 0x00; // Reserved Value - data[11] = 0x00; // Reserved Value + memcpy(request_specification_version_request.reserved, reserved, sizeof(reserved)); } - uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Reserved (2) - if (custom_IDm == false && check_last_idm(data, datalen) == false) { + if (custom_IDm == false && + check_last_idm((uint8_t *)&request_specification_version_request, + sizeof(request_specification_version_request)) == false) { return PM3_EINVARG; } - uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); - - clear_and_send_command(flags, datalen, data, 0); - - PacketResponseNG resp; - if (waitCmdFelica(false, &resp, true) == false) { + felica_request_specification_version_info_t specification_version_info; + uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; + if (send_request_specification_version(flags, sizeof(request_specification_version_request), + (uint8_t *)&request_specification_version_request, + verbose, + true, FELICA_DEFAULT_TIMEOUT_MS, 0, + &specification_version_info) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Got no response from card"); return PM3_ERFTRANS; } - felica_request_spec_response_t spec_response; - memcpy(&spec_response, (felica_request_spec_response_t *)resp.data.asBytes, sizeof(felica_request_spec_response_t)); + PrintAndLogEx(SUCCESS, "Got Request Response"); + PrintAndLogEx(SUCCESS, "IDm............ %s", + sprint_hex(request_specification_version_request.IDm, sizeof(request_specification_version_request.IDm))); + PrintAndLogEx(SUCCESS, "Status Flag1... %s", + sprint_hex(specification_version_info.status_flags.status_flag1, + sizeof(specification_version_info.status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "Status Flag2... %s", + sprint_hex(specification_version_info.status_flags.status_flag2, + sizeof(specification_version_info.status_flags.status_flag2))); - if (spec_response.frame_response.IDm[0] != 0) { - PrintAndLogEx(SUCCESS, "Got Request Response"); - PrintAndLogEx(SUCCESS, "IDm............ %s", sprint_hex(spec_response.frame_response.IDm, sizeof(spec_response.frame_response.IDm))); - PrintAndLogEx(SUCCESS, "Status Flag1... %s", sprint_hex(spec_response.status_flags.status_flag1, sizeof(spec_response.status_flags.status_flag1))); - PrintAndLogEx(SUCCESS, "Status Flag2... %s", sprint_hex(spec_response.status_flags.status_flag2, sizeof(spec_response.status_flags.status_flag2))); + if (specification_version_info.has_specification_version) { + print_specification_versions(SUCCESS, &specification_version_info, true); - if (spec_response.status_flags.status_flag1[0] == 0) { - PrintAndLogEx(SUCCESS, "Format Version..... %s", sprint_hex(spec_response.format_version, sizeof(spec_response.format_version))); - PrintAndLogEx(SUCCESS, "Basic Version...... %s", sprint_hex(spec_response.basic_version, sizeof(spec_response.basic_version))); - PrintAndLogEx(SUCCESS, "Number of Option... %s", sprint_hex(spec_response.number_of_option, sizeof(spec_response.number_of_option))); - if (spec_response.number_of_option[0] == 1) { - PrintAndLogEx(SUCCESS, "Option Version List..."); - for (int i = 0; i < spec_response.number_of_option[0]; i++) { - PrintAndLogEx(SUCCESS, " - %s", sprint_hex(spec_response.option_version_list + i * 2, 2)); - } - } + if (specification_version_info.option_version_count < specification_version_info.number_of_option) { + PrintAndLogEx(WARNING, "Truncated Option Version List: card returned %u entries, showing %zu", + specification_version_info.number_of_option, + specification_version_info.option_version_count); } } + return PM3_SUCCESS; } diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index c8d3a243c..8a8599c8e 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -2049,11 +2049,11 @@ void annotateFelica(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { case FELICA_GETSTATUS_ACK: snprintf(exp, size, "GET STATUS ACK"); break; - case FELICA_OSVER_REQ: - snprintf(exp, size, "REQUEST SPECIFIC VERSION"); + case FELICA_REQUEST_SPEC_VERSION_REQ: + snprintf(exp, size, "REQUEST SPEC VERSION"); break; - case FELICA_OSVER_ACK: - snprintf(exp, size, "RSV ACK"); + case FELICA_REQUEST_SPEC_VERSION_ACK: + snprintf(exp, size, "REQUEST SPEC ACK"); break; case FELICA_RESET_MODE_REQ: snprintf(exp, size, "RESET MODE"); diff --git a/include/iso18.h b/include/iso18.h index 73e20dd9a..3ca4d04b5 100644 --- a/include/iso18.h +++ b/include/iso18.h @@ -20,6 +20,8 @@ #include "common.h" +#define FELICA_SPECIFICATION_VERSION_MAX_OPTIONS 16U + typedef enum FELICA_COMMAND { FELICA_CONNECT = (1 << 0), FELICA_NO_DISCONNECT = (1 << 1), @@ -133,6 +135,23 @@ typedef struct { uint8_t IDm[8]; } PACKED felica_get_platform_info_request_t; +typedef struct { + uint8_t length[1]; + uint8_t command_code[1]; + uint8_t IDm[8]; + uint8_t reserved[2]; +} PACKED felica_request_specification_version_request_t; + +typedef struct { + felica_status_flags_t status_flags; + bool has_specification_version; + uint8_t format_version; + uint8_t basic_version[2]; + uint8_t number_of_option; + size_t option_version_count; + uint8_t option_version_list[FELICA_SPECIFICATION_VERSION_MAX_OPTIONS * 2]; +} felica_request_specification_version_info_t; + typedef struct { felica_frame_response_t frame_response; uint8_t node_number[1]; @@ -162,15 +181,6 @@ typedef struct { uint8_t system_code_list[32]; } PACKED felica_syscode_response_t; -typedef struct { - felica_frame_response_t frame_response; - felica_status_flags_t status_flags; - uint8_t format_version[1]; - uint8_t basic_version[2]; - uint8_t number_of_option[1]; - uint8_t option_version_list[4]; -} PACKED felica_request_spec_response_t; - typedef struct { felica_frame_response_t frame_response; uint8_t m2c[8]; diff --git a/include/protocols.h b/include/protocols.h index b8522a53b..2b84fcd35 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -864,8 +864,8 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define FELICA_GETPLATFORMINFO_REQ 0x3a #define FELICA_GETPLATFORMINFO_ACK 0x3b -#define FELICA_OSVER_REQ 0x3c -#define FELICA_OSVER_ACK 0x3d +#define FELICA_REQUEST_SPEC_VERSION_REQ 0x3c +#define FELICA_REQUEST_SPEC_VERSION_ACK 0x3d #define FELICA_RESET_MODE_REQ 0x3e #define FELICA_RESET_MODE_ACK 0x3f