mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-03-30 19:05:47 +00:00
Add request specification version to 'hf felica info'
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user