diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 72a1b1d26..c84c8c519 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1734,80 +1734,80 @@ static int CmdHFFelicaDump(const char *Cmd) { } uint8_t len = resp.frame_response.length[0]; uint16_t node_code = resp.payload[0] | (resp.payload[1] << 8); - if (node_code == 0xFFFF) break; + if (node_code == 0xFFFF) break; char attrib_str[64] = ""; switch (len) { - case 0x0E: - break; - case 0x0C: { - uint8_t attribute = node_code & 0x3F; - bool is_public = (attribute & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0; - strcat(attrib_str, is_public ? "| Public " : "| Private "); + case 0x0E: + break; + case 0x0C: { + uint8_t attribute = node_code & 0x3F; + bool is_public = (attribute & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0; + strcat(attrib_str, is_public ? "| Public " : "| Private "); - bool is_purse = (attribute & FELICA_SERVICE_ATTRIBUTE_PURSE) != 0; - // Subfield bitwise attributes are applicable depending on is PURSE or not + bool is_purse = (attribute & FELICA_SERVICE_ATTRIBUTE_PURSE) != 0; + // Subfield bitwise attributes are applicable depending on is PURSE or not - if(is_purse) { - strcat(attrib_str, "| Purse |"); - switch((attribute & FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD) >> 1) { - case 0: - strcat(attrib_str, " Direct |"); - break; - case 1: - strcat(attrib_str, " Cashback |"); - break; - case 2: - strcat(attrib_str, " Decrement |"); - break; - case 3: - strcat(attrib_str, " Read Only |"); - break; - default: - strcat(attrib_str, " Unknown |"); - break; + if (is_purse) { + strcat(attrib_str, "| Purse |"); + switch ((attribute & FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD) >> 1) { + case 0: + strcat(attrib_str, " Direct |"); + break; + case 1: + strcat(attrib_str, " Cashback |"); + break; + case 2: + strcat(attrib_str, " Decrement |"); + break; + case 3: + strcat(attrib_str, " Read Only |"); + break; + default: + strcat(attrib_str, " Unknown |"); + break; + } + } else { + bool is_random = (attribute & FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS) != 0; + strcat(attrib_str, is_random ? "| Random |" : "| Cyclic |"); + bool is_readonly = (attribute & FELICA_SERVICE_ATTRIBUTE_READ_ONLY) != 0; + strcat(attrib_str, is_readonly ? " Read Only |" : " Read/Write |"); } - } else { - bool is_random = (attribute & FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS) != 0; - strcat(attrib_str, is_random ? "| Random |" : "| Cyclic |"); - bool is_readonly = (attribute & FELICA_SERVICE_ATTRIBUTE_READ_ONLY) != 0; - strcat(attrib_str, is_readonly ? " Read Only |" : " Read/Write |"); - } - PrintAndLogEx(INFO, "Service %04X %s", node_code, attrib_str); + PrintAndLogEx(INFO, "Service %04X %s", node_code, attrib_str); - if (is_public) { - // dump blocks here - PrintAndLogEx(INFO, " block | data "); - PrintAndLogEx(INFO, "-------+----------------------------------------"); + if (is_public) { + // dump blocks here + PrintAndLogEx(INFO, " block | data "); + PrintAndLogEx(INFO, "-------+----------------------------------------"); - data_block_dump[11] = resp.payload[0]; // convert service code to little endian - data_block_dump[12] = resp.payload[1]; + data_block_dump[11] = resp.payload[0]; // convert service code to little endian + data_block_dump[12] = resp.payload[1]; - uint16_t last_blockno = 0xFF; - for (uint16_t i = 0x00; i < last_blockno; i++) { - data_block_dump[15] = i; - AddCrc(data_block_dump, block_datalen); - felica_read_without_encryption_response_t rd_noCry_resp; - if ((send_rd_plain(flags, block_datalen + 2, data_block_dump, 0, &rd_noCry_resp) == PM3_SUCCESS)) { - if (rd_noCry_resp.status_flags.status_flag1[0] == 0 && rd_noCry_resp.status_flags.status_flag2[0] == 0) { - print_rd_plain_response(&rd_noCry_resp); + uint16_t last_blockno = 0xFF; + for (uint16_t i = 0x00; i < last_blockno; i++) { + data_block_dump[15] = i; + AddCrc(data_block_dump, block_datalen); + felica_read_without_encryption_response_t rd_noCry_resp; + if ((send_rd_plain(flags, block_datalen + 2, data_block_dump, 0, &rd_noCry_resp) == PM3_SUCCESS)) { + if (rd_noCry_resp.status_flags.status_flag1[0] == 0 && rd_noCry_resp.status_flags.status_flag2[0] == 0) { + print_rd_plain_response(&rd_noCry_resp); + } else { + break; // no more blocks to read + } } else { - break; // no more blocks to read + break; } - } else { - break; } } + break; } - break; - } - default: - PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", - len, cursor); - return PM3_ERFTRANS; + default: + PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", + len, cursor); + return PM3_ERFTRANS; } cursor++; - if (cursor == 0) break; + if (cursor == 0) break; } PrintAndLogEx(SUCCESS, "Unauth service dump complete."); @@ -2448,7 +2448,7 @@ static int read_without_encryption( uint16_t size = hdr_size + sizeof(svc) + 1; *n = size; - memcpy(out, &(uint8_t){ size }, sizeof(uint8_t)); + memcpy(out, &(uint8_t) { size }, sizeof(uint8_t)); memcpy(out + 1, &request, hdr_size); memcpy(out + hdr_size + 1, &svc, sizeof(svc)); @@ -2462,7 +2462,7 @@ static bool check_write_req_data(const felica_write_request_haeder_t *hdr, const uint8_t num = *(hdr->number_of_block); if (num != 1 && num != 2) return false; - + // Check Block data size if (num * 16 != datalen) return false; @@ -2470,11 +2470,11 @@ static bool check_write_req_data(const felica_write_request_haeder_t *hdr, const return true; } -static int write_without_encryption( - const uint8_t *idm, - uint8_t num, - uint8_t *blk_numbers, - const uint8_t *data, +static int write_without_encryption( + const uint8_t *idm, + uint8_t num, + uint8_t *blk_numbers, + const uint8_t *data, size_t datalen, uint8_t *out, uint16_t *n) { @@ -2499,7 +2499,7 @@ static int write_without_encryption( if (ret) { return PM3_EINVARG; } - + size_t hdr_size = sizeof(hdr); size_t offset = hdr_size + (num * 2) + 1; @@ -2507,7 +2507,7 @@ static int write_without_encryption( uint8_t size = hdr_size + sizeof(blk) + dl + 1; *n = size; - memcpy(out, &(uint8_t){ size }, sizeof(uint8_t)); + memcpy(out, &(uint8_t) { size }, sizeof(uint8_t)); memcpy(out + 1, &hdr, hdr_size); memcpy(out + hdr_size + 1, &blk, sizeof(blk)); memcpy(out + offset, data, dl); @@ -2530,7 +2530,7 @@ static int parse_multiple_block_data(const uint8_t *data, const size_t datalen, PrintAndLogEx(ERR, "internal error"); return PM3_ERFTRANS; } - + if (res.status_flags.status_flag1[0] != 0x00 || res.status_flags.status_flag2[0] != 0x00) { PrintAndLogEx(ERR, "error status"); @@ -2551,13 +2551,13 @@ static int parse_multiple_block_data(const uint8_t *data, const size_t datalen, } static int felica_auth_context_init( - mbedtls_des3_context* ctx, - const uint8_t* rc, + mbedtls_des3_context *ctx, + const uint8_t *rc, const size_t rclen, - const uint8_t* key, + const uint8_t *key, const size_t keylen, felica_auth_context_t *auth_ctx) { - + int ret = PM3_SUCCESS; uint8_t rev_rc[16], rev_key[16]; @@ -2578,7 +2578,7 @@ static int felica_auth_context_init( ret = PM3_ECRYPTO; goto cleanup; } - + if (mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, 16, iv, rev_rc, encrypted_sk) != 0) { ret = PM3_ECRYPTO; goto cleanup; @@ -2599,12 +2599,12 @@ cleanup: } static int felica_generate_mac( - mbedtls_des3_context* ctx, + mbedtls_des3_context *ctx, const felica_auth_context_t *auth_ctx, - const uint8_t* initialize_block, - const uint8_t* block_data, + const uint8_t *initialize_block, + const uint8_t *block_data, const size_t length, - uint8_t* mac) { + uint8_t *mac) { int ret = PM3_SUCCESS; @@ -2734,7 +2734,7 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { felica_status_response_t sres; if (send_wr_plain(flags, datalen, data, false, &sres) != PM3_SUCCESS) { - return PM3_ERFTRANS; + return PM3_ERFTRANS; } if (sres.status_flags.status_flag1[0] != 0x00 && sres.status_flags.status_flag2[0] != 0x00) { @@ -2811,7 +2811,7 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { if (memcmp(mac_blk, mac, FELICA_BLK_HALF) != 0) { PrintAndLogEx(ERR, "\nAuthenticate Failed"); - return PM3_ERFTRANS; + return PM3_ERFTRANS; } PrintAndLogEx(SUCCESS, "Authenticate Success"); diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 1a071244e..248a4a667 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -242,6 +242,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf felica reader" }, { 0, "hf felica sniff" }, { 0, "hf felica wrbl" }, + { 0, "hf felica dump" }, { 0, "hf felica rqservice" }, { 0, "hf felica rqresponse" }, { 0, "hf felica scsvcode" }, @@ -252,6 +253,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf felica resetmode" }, { 0, "hf felica litesim" }, { 0, "hf felica litedump" }, + { 0, "hf felica liteauth" }, { 1, "hf fido help" }, { 1, "hf fido list" }, { 0, "hf fido info" }, diff --git a/doc/commands.json b/doc/commands.json index 13c1b6227..ea1ffa469 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -2659,6 +2659,19 @@ ], "usage": "hf felica auth2 [-hv] [-i ] [-c ] [-k ]" }, + "hf felica dump": { + "command": "hf felica dump", + "description": "Dump all existing Area Code and Service Code. Only works on services that do not require authentication yet.", + "notes": [ + "hf felica dump" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--no-auth read public services" + ], + "usage": "hf felica dump [-h] [--no-auth]" + }, "hf felica help": { "command": "hf felica help", "description": "----------- ----------------------- General ----------------------- help This help list List ISO 18092/FeliCa history ----------- ----------------------- Operations ----------------------- ----------- ----------------------- FeliCa Standard ----------------------- ----------- ----------------------- FeliCa Light ----------------------- --------------------------------------------------------------------------------------- hf felica list available offline: yes Alias of `trace list -t felica` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", @@ -2692,6 +2705,23 @@ ], "usage": "hf felica info [-h]" }, + "hf felica liteauth": { + "command": "hf felica liteauth", + "description": "Authenticate", + "notes": [ + "hf felica liteauth -i 11100910C11BC407", + "hf felica liteauth -k 46656c69436130313233343536616263", + "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 -k 46656c69436130313233343536616263" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-k, --key set card key, 16 bytes", + "-c, set random challenge, 16 bytes", + "-i, set custom IDm" + ], + "usage": "hf felica liteauth [-h] [-k ] [-c ] [-i ]" + }, "hf felica litedump": { "command": "hf felica litedump", "description": "Dump ISO/18092 FeliCa Lite tag. It will timeout after 200sec", @@ -13453,8 +13483,8 @@ } }, "metadata": { - "commands_extracted": 773, + "commands_extracted": 775, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-28T13:42:15" + "extracted_on": "2025-08-04T17:50:35" } } diff --git a/doc/commands.md b/doc/commands.md index 0671c769d..fc80c4404 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -333,6 +333,7 @@ Check column "offline" for their availability. |`hf felica reader `|N |`Act like an ISO18092/FeliCa reader` |`hf felica sniff `|N |`Sniff ISO 18092/FeliCa traffic` |`hf felica wrbl `|N |`write block data to an authentication-not-required Service.` +|`hf felica dump `|N |`Wait for and try dumping FeliCa` |`hf felica rqservice `|N |`verify the existence of Area and Service, and to acquire Key Version.` |`hf felica rqresponse `|N |`verify the existence of a card and its Mode.` |`hf felica scsvcode `|N |`acquire Area Code and Service Code.` @@ -343,6 +344,7 @@ Check column "offline" for their availability. |`hf felica resetmode `|N |`reset Mode to Mode 0.` |`hf felica litesim `|N |`Emulating ISO/18092 FeliCa Lite tag` |`hf felica litedump `|N |`Wait for and try dumping FelicaLite` +|`hf felica liteauth `|N |`authenticate a card.` ### hf fido