From e147bdc71b4e7cad377817d93fa0652a39c8f3ce Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 29 Jul 2020 11:02:30 +0200 Subject: [PATCH] renamed readtagfile to view\nrenamed clone to restore\nrefactored reader, info, dump, rdbl, wrbl to be on device side. Commands now also seamless works with NON_SECURE_MPAGE --- armsrc/Standalone/hf_iceclass.c | 159 ++++--- armsrc/appmain.c | 37 +- armsrc/iclass.c | 489 +++++++++++++++------ armsrc/iclass.h | 18 +- client/src/cmdhf.c | 6 +- client/src/cmdhficlass.c | 752 ++++++++++++++++++-------------- client/src/cmdhficlass.h | 3 +- include/pm3_cmd.h | 63 ++- 8 files changed, 953 insertions(+), 574 deletions(-) diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index d8df74773..53b6b6f92 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -13,10 +13,14 @@ #include "BigBuf.h" #include "fpgaloader.h" #include "util.h" +#include "ticks.h" #include "dbprint.h" #include "spiffs.h" #include "iclass.h" +#include "iso15693.h" #include "optimized_cipher.h" +#include "pm3_cmd.h" +#include "protocols.h" #define NUM_CSNS 9 #define MAC_RESPONSES_SIZE (16 * NUM_CSNS) @@ -36,15 +40,8 @@ char* cc_files[] = { HF_ICLASS_CC_A, HF_ICLASS_CC_B }; #define ICE_STATE_READER 3 #define ICE_STATE_CONFIGCARD 4 -typedef struct { - uint8_t app_limit; //[8] - uint8_t otp[2]; //[9-10] - uint8_t block_writelock;//[11] - uint8_t chip_config; //[12] - uint8_t mem_config; //[13] - uint8_t eas; //[14] - uint8_t fuses; //[15] -} picopass_conf_block_t; +// times in ssp_clk_cycles @ 3,3625MHz when acting as reader +#define DELAY_ICLASS_VICC_TO_VCD_READER DELAY_ISO15693_VICC_TO_VCD_READER // iclass card descriptors char * card_types[] = { @@ -70,9 +67,12 @@ uint8_t card_app2_limit[] = { }; static uint8_t aa2_key[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - static uint8_t legacy_aa1_key[] = {0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78}; +static uint8_t get_pagemap(const picopass_hdr *hdr) { + return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; +} + static uint8_t csns[8 * NUM_CSNS] = { 0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, 0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0, @@ -224,16 +224,10 @@ static int reader_attack_mode(void) { static int reader_dump_mode(void) { BigBuf_free(); + uint8_t *card_data = BigBuf_malloc(0xFF * 8); memset(card_data, 0xFF, sizeof(card_data)); - - struct p { - uint8_t key[8]; - bool use_raw; - bool use_elite; - bool use_credit_key; - } PACKED; - + for (;;) { if (BUTTON_PRESS()) { @@ -241,65 +235,104 @@ static int reader_dump_mode(void) { break; } - // AA1 - struct p payload = { + // setup authenticate AA1 + iclass_auth_req_t auth = { .use_raw = false, .use_elite = false, .use_credit_key = false, + .do_auth = true, + .send_reply = false, }; - memcpy(payload.key, legacy_aa1_key, sizeof(payload.key)); + memcpy(auth.key, legacy_aa1_key, sizeof(auth.key)); - bool isOK = iclass_auth((uint8_t*)&payload, false, card_data); - if (isOK == false) { + + Iso15693InitReader(); + + // select tag. + uint32_t eof_time = 0; + bool res = select_iclass_tag(card_data, auth.use_credit_key, &eof_time); + if (res == false) { + switch_off(); continue; } - - picopass_conf_block_t *conf = (picopass_conf_block_t*)(card_data + 8); + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + picopass_hdr *hdr = (picopass_hdr *)card_data; + // get 3 config bits - uint8_t type = (conf->chip_config & 0x10) >> 2; - type |= (conf->mem_config & 0x80) >> 6; - type |= (conf->mem_config & 0x20) >> 5; + uint8_t type = (hdr->conf.chip_config & 0x10) >> 2; + type |= (hdr->conf.mem_config & 0x80) >> 6; + type |= (hdr->conf.mem_config & 0x20) >> 5; - uint8_t app1_limit = conf->app_limit - 5; // minus header blocks - uint8_t app2_limit = card_app2_limit[type]; - - - uint16_t dumped = 0; - uint8_t block; - for (block = 5; block < app1_limit; block++) { - isOK = iclass_readblock(block, card_data + (8 * block)); - if (isOK) { - dumped++; + uint8_t pagemap = get_pagemap(hdr); + uint8_t app1_limit, app2_limit, start_block; + + // tags configured for NON SECURE PAGE, acts different + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + app1_limit = card_app2_limit[type]; + app2_limit = 0; + start_block = 3; + } else { + + app1_limit = hdr->conf.app_limit; + app2_limit = card_app2_limit[type]; + start_block = 6; + + res = authenticate_iclass_tag(&auth, hdr, &start_time, &eof_time, NULL); + if (res == false) { + switch_off(); + DbpString("failed AA1 auth"); + continue; } + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; } - // AA2 - payload.use_credit_key = true; - memcpy(payload.key, aa2_key, sizeof(payload.key)); + uint16_t dumped = 0; - isOK = iclass_auth((uint8_t*)&payload, false, card_data); - if (isOK) { - for (; block < app2_limit; block++) { - isOK = iclass_readblock(block, card_data + (8 * block)); - if (isOK) { - dumped++; + // main read loop + for (uint8_t i = start_block; i <= app1_limit; i++) { + res = iclass_read_block(i, card_data + (8 * i)); + if (res) { + dumped++; + } + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + } + + if (pagemap != PICOPASS_NON_SECURE_PAGEMODE) { + + // authenticate AA2 + auth.use_credit_key = true; + memcpy(auth.key, aa2_key, sizeof(auth.key)); + + res = select_iclass_tag(card_data, auth.use_credit_key, &eof_time); + if (res) { + res = authenticate_iclass_tag(&auth, hdr, &start_time, &eof_time, NULL); + if (res) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + for (uint8_t i = app1_limit + 1; i <= app2_limit; i++) { + res = iclass_read_block(i, card_data + (8 * i)); + if (res) { + dumped++; + } + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + } + } else { + DbpString("failed AA2 auth"); } } } - Dbprintf("Found %s", card_types[type]); -/* - Dbprintf("APP1 Blocks: %d", app1_limit); - Dbprintf("APP2 Blocks: %d", app2_limit - app1_limit - 5); // minus app1 and header - Dbprintf("Got %d blocks (saving %u, %u bytes )", dumped, dumped + 5, ((dumped+5)*8) ); -*/ - if (5 + dumped > app1_limit) { - save_to_flash(card_data, (5 + dumped) * 8 ); - } + switch_off(); + save_to_flash(card_data, (start_block + dumped) * 8 ); + + SpinDelay(250); + Dbprintf("Found a %s", card_types[type]); } - Dbprintf("exit read & dump mode"); + Dbprintf("-=[ exiting `read & dump` mode"); return PM3_SUCCESS; } @@ -336,7 +369,7 @@ void RunMod(void) { StandAloneMode(); Dbprintf(_YELLOW_("HF iCLASS mode a.k.a iceCLASS started")); - uint8_t mode = ICE_STATE_FULLSIM; + uint8_t mode = ICE_STATE_READER; for (;;) { @@ -344,14 +377,6 @@ void RunMod(void) { if (mode == ICE_STATE_NONE) break; if (data_available()) break; - -/* - // Was our button held down or pressed? - int button_pressed = BUTTON_HELD(1000); - if (button_pressed != BUTTON_NO_CLICK) { - break; - } - */ int res; switch (mode) { @@ -387,7 +412,7 @@ void RunMod(void) { break; } case ICE_STATE_READER: { - Dbprintf("enter read & dump mode"); + Dbprintf("enter read & dump mode, continuous scanning"); res = reader_dump_mode(); if (res == PM3_SUCCESS) download_instructions(mode); diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 7f4f24c43..d40d262f4 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1407,36 +1407,14 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ICLASS_WRITEBL: { - struct p { - uint8_t blockno; - uint8_t data[12]; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - iClass_WriteBlock(payload->blockno, payload->data); - break; - } - // iceman2019, unused? - case CMD_HF_ICLASS_READCHECK: { // auth step 1 - iClass_ReadCheck(packet->oldarg[0], packet->oldarg[1]); + iClass_WriteBlock(packet->data.asBytes); break; } case CMD_HF_ICLASS_READBL: { - /* - struct p { - uint8_t blockno; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - */ - iClass_ReadBlk(packet->data.asBytes[0]); + iClass_ReadBlock(packet->data.asBytes); break; } case CMD_HF_ICLASS_AUTH: { //check - /* - struct p { - uint8_t mac[4]; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - */ iClass_Authentication(packet->data.asBytes); break; } @@ -1445,12 +1423,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ICLASS_DUMP: { - struct p { - uint8_t start_blockno; - uint8_t numblks; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - iClass_Dump(payload->start_blockno, payload->numblks); + iClass_Dump(packet->data.asBytes); break; } case CMD_HF_ICLASS_CLONE: { @@ -1463,6 +1436,10 @@ static void PacketReceived(PacketCommandNG *packet) { iClass_Clone(payload->startblock, payload->endblock, payload->data); break; } + case CMD_HF_ICLASS_RESTORE: { + iClass_Restore(packet->data.asBytes); + break; + } #endif #ifdef WITH_HFSNIFF diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 16988f74e..0a3a63741 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -57,6 +57,12 @@ #define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1) +/* +static uint8_t get_pagemap(const picopass_hdr *hdr) { + return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; +} +*/ + /* * CARD TO READER * in ISO15693-2 mode - Manchester @@ -836,19 +842,21 @@ static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, } static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* resp, size_t max_resp_size, - uint8_t expected_size, uint8_t tries, uint32_t start_time, + uint8_t expected_size, uint8_t tries, uint32_t *start_time, uint16_t timeout, uint32_t *eof_time) { while (tries-- > 0) { - iclass_send_as_reader(cmd, cmdsize, &start_time, eof_time); + iclass_send_as_reader(cmd, cmdsize, start_time, eof_time); - if (resp == NULL) - return true; + if (resp == NULL) { + return true; + } if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { return true; } - start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + *start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; } return false; } @@ -860,12 +868,13 @@ static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* * @return false = fail * true = Got all. */ -static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time) { +static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time, uint8_t *status) { static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; static uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; + uint8_t read_aia[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; @@ -916,21 +925,70 @@ static bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t //Save CONF in response data memcpy(card_data + 8, resp, 8); + + if (status) + *status |= (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF); - // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, eof_time); - - // expect a 8-byte response here - len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); - if (len != 8) - return false; + picopass_hdr *hdr = (picopass_hdr *)card_data; + uint8_t pagemap = (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; // 0x18 - //Save CC (e-purse) in response data - memcpy(card_data + 16, resp, 8); + if (pagemap != PICOPASS_NON_SECURE_PAGEMODE) { + + //Read App Issuer Area block 5 + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time); + + // expect a 10-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; + + if (status) { + *status |= FLAG_ICLASS_AIA; + memcpy(card_data + (8 * 5), resp, 8); + } + + // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, eof_time); + + // expect a 8-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 8) + return false; + + memcpy(card_data + (8 * 2), resp, 8); + *status |= FLAG_ICLASS_CC; + + } else { + + //Read App Issuer Area block 2 + read_aia[1] = 0x02; + read_aia[2] = 0x61; + read_aia[3] = 0x10; + + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time); + + // expect a 10-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; + + if (status) { + *status |= FLAG_ICLASS_AIA; + memcpy(card_data + (8 * 2), resp, 8); + } + } + return true; } +bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time) { + uint8_t result = 0; + return select_iclass_tag_ex(card_data, use_credit_key, eof_time, &result); +} + // Reader iClass Anticollission // turn off afterwards void ReaderIClass(uint8_t flags) { @@ -940,9 +998,8 @@ void ReaderIClass(uint8_t flags) { uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; memset(resp, 0xFF, sizeof(resp)); -// bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully - bool use_credit_key = flags & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key - bool flag_read_aia = flags & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area +// bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully + bool use_credit_key = flags & FLAG_ICLASS_READER_CREDITKEY; // flag to use credit key if ((flags & FLAG_ICLASS_READER_INIT) == FLAG_ICLASS_READER_INIT) { Iso15693InitReader(); @@ -951,31 +1008,18 @@ void ReaderIClass(uint8_t flags) { if ((flags & FLAG_ICLASS_READER_CLEARTRACE) == FLAG_ICLASS_READER_CLEARTRACE) { clear_trace(); } - + + uint8_t result_status = 0; uint32_t eof_time = 0; - bool status = select_iclass_tag(card_data, use_credit_key, &eof_time); + bool status = select_iclass_tag_ex(card_data, use_credit_key, &eof_time, &result_status); if (status == false) { reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); switch_off(); return; } - uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - uint8_t result_status = (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC); - - //Read block 5, AIA - if (flag_read_aia) { - //Read App Issuer Area block CRC(0x05) => 0xde 0x64 - uint8_t read_aa[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; - status = iclass_send_cmd_with_retries(read_aa, sizeof(read_aa), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - if (status) { - result_status |= FLAG_ICLASS_AIA; - memcpy(card_data + (8 * 5), resp, 8); - } else { - if (DBGLEVEL >= DBG_EXTENDED) DbpString("Failed to dump AIA block"); - } - } - + + // Page mapping for secure mode // 0 : CSN // 1 : Configuration // 2 : e-purse @@ -983,7 +1027,12 @@ void ReaderIClass(uint8_t flags) { // 4 : kc / credit / aa1 (write-only) // 5 : AIA, Application issuer area // - //Then we can 'ship' back the 6 * 8 bytes of data, + // Page mapping for non secure mode + // 0 : CSN + // 1 : Configuration + // 2 : AIA, Application issuer area + + // Return to client, e 6 * 8 bytes of data. // with 0xFF:s in block 3 and 4. LED_B_ON(); @@ -1046,7 +1095,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { memcpy(check + 5, mac, 4); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - if (iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { + if (iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { if (DBGLEVEL >= DBG_EXTENDED) DbpString("Error: Authentication Fail!"); continue; } @@ -1056,7 +1105,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { AddCrc(read + 1, 1); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - if (iclass_send_cmd_with_retries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { + if (iclass_send_cmd_with_retries(read, sizeof(read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time) == false) { if (DBGLEVEL >= DBG_EXTENDED) DbpString("Dump config (block 1) failed"); continue; } @@ -1085,7 +1134,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { AddCrc(read + 1, 1); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - if (iclass_send_cmd_with_retries(read, sizeof(read), resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { + if (iclass_send_cmd_with_retries(read, sizeof(read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time)) { if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x", block, @@ -1142,73 +1191,47 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { switch_off(); } -// not used. ?!? ( CMD_HF_ICLASS_READCHECK) -// turn off afterwards -void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { - uint8_t readcheck[] = { keytype, blockno }; - uint8_t resp[8] = {0}; - uint32_t eof_time = 0; -// start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - bool isOK = iclass_send_cmd_with_retries(readcheck, sizeof(readcheck), resp, sizeof(resp), 8, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); - switch_off(); -} - // used with function select_and_auth (cmdhficlass.c) // which needs to authenticate before doing more things like read/write // selects and authenticate to a card, sends back div_key and mac to client. -void iClass_Authentication(uint8_t *bytes) { - iclass_auth(bytes, true, NULL); +void iClass_Authentication(uint8_t *msg) { } -bool iclass_auth(uint8_t *bytes, bool send_reply, uint8_t *dataout) { +bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out) { - iclass_auth_req_t *payload = (iclass_auth_req_t *)bytes; - iclass_auth_resp_t packet; - - Iso15693InitReader(); - - uint8_t card_data[3 * 8] = {0xFF}; - uint32_t eof_time = 0; - - packet.isOK = select_iclass_tag(card_data, payload->use_credit_key, &eof_time); - if (packet.isOK == false) { - if (send_reply) - reply_ng(CMD_HF_ICLASS_AUTH, PM3_ESOFT, (uint8_t *)&packet, sizeof(packet)); - - return false; - } - uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - - uint8_t check[9] = { ICLASS_CMD_CHECK }; + uint8_t cmd_check[9] = { ICLASS_CMD_CHECK }; + uint8_t div_key[8] = {0}; + uint8_t mac[4] = {0}; + uint8_t resp_auth[4] = {0}; uint8_t ccnr[12] = {0}; - memcpy(ccnr, card_data + 16, 8); + + uint8_t *pmac = mac; + if (mac_out) + pmac = mac_out; + + memcpy(ccnr, hdr->epurse, sizeof(hdr->epurse)); if (payload->use_raw) - memcpy(packet.div_key, payload->key, 8); + memcpy(div_key, payload->key, 8); else - iclass_calc_div_key(card_data, payload->key, packet.div_key, payload->use_elite); + iclass_calc_div_key(hdr->csn, payload->key, div_key, payload->use_elite); - opt_doReaderMAC(ccnr, packet.div_key, packet.mac); + if (payload->use_credit_key) + memcpy(hdr->key_c, div_key, sizeof(hdr->key_c)); + else + memcpy(hdr->key_d, div_key, sizeof(hdr->key_d)); + +// Dbhexdump(sizeof(div_key), div_key, false); + + opt_doReaderMAC(ccnr, div_key, pmac); // copy MAC to check command (readersignature) - check[5] = packet.mac[0]; - check[6] = packet.mac[1]; - check[7] = packet.mac[2]; - check[8] = packet.mac[3]; + cmd_check[5] = pmac[0]; + cmd_check[6] = pmac[1]; + cmd_check[7] = pmac[2]; + cmd_check[8] = pmac[3]; - uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; - packet.isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - - if (send_reply) - reply_ng(CMD_HF_ICLASS_AUTH, (packet.isOK)? PM3_SUCCESS : PM3_ESOFT, (uint8_t *)&packet, sizeof(packet)); - - if (dataout) { - memcpy(dataout, card_data, sizeof(card_data)); - memcpy(dataout + (3 * 8), packet.div_key, sizeof(packet.div_key)); - } - - return true; + return iclass_send_cmd_with_retries(cmd_check, sizeof(cmd_check), resp_auth, sizeof(resp_auth), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time); } typedef struct iclass_premac { @@ -1279,7 +1302,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { check[8] = keys[i].mac[3]; // expect 4bytes, 3 retries times.. - isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); if (isOK) goto out; @@ -1299,27 +1322,78 @@ out: // Tries to read block. // retries 10times. -bool iclass_readblock(uint8_t blockno, uint8_t *data) { +// reply 8 bytes block +bool iclass_read_block(uint8_t blockno, uint8_t *data) { uint8_t resp[10]; uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; AddCrc(c + 1, 1); - uint32_t eof_time = 0; - bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 10, 0, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - memcpy(data, resp, 8); + uint32_t eof_time = 0, start_time = 0; + bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 10, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + if (isOK) + memcpy(data, resp, 8); return isOK; } // turn off afterwards -// readblock 8 + 2. only want 8. -void iClass_ReadBlk(uint8_t blockno) { - struct p { - bool isOK; - uint8_t blockdata[8]; - } PACKED result; +// send in authentication needed data, if to use auth. +// reply 8 bytes block if send_reply (for client) +void iClass_ReadBlock(uint8_t *msg) { - LED_A_ON(); - result.isOK = iclass_readblock(blockno, result.blockdata); - reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&result, sizeof(result)); + iclass_auth_req_t *payload = (iclass_auth_req_t *)msg; + + iclass_readblock_resp_t response = { .isOK = true }; + memset(response.data, 0, sizeof(response.data)); + + uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, payload->blockno, 0x00, 0x00}; + AddCrc(cmd_read + 1, 1); + + Iso15693InitReader(); + + // select tag. + uint32_t eof_time = 0; + picopass_hdr hdr = {0}; + bool res = select_iclass_tag( (uint8_t *)&hdr, payload->use_credit_key, &eof_time); + if (res == false) { + if (payload->send_reply) { + response.isOK = res; + reply_ng(CMD_HF_ICLASS_READBL, PM3_ETIMEOUT, (uint8_t *)&response, sizeof(response)); + } + goto out; + } + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // authenticate + if (payload->do_auth) { + + res = authenticate_iclass_tag(payload, &hdr, &start_time, &eof_time, NULL); + if (res == false) { + if (payload->send_reply) { + response.isOK = res; + reply_ng(CMD_HF_ICLASS_READBL, PM3_ETIMEOUT, (uint8_t *)&response, sizeof(response)); + } + goto out; + } + } + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // read data + uint8_t resp[10]; + res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), resp, sizeof(resp), 10, 10, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + if (res) { + memcpy(response.data, resp, sizeof(response.data)); + if (payload->send_reply) { + reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&response, sizeof(response)); + } + } else { + if (payload->send_reply) { + response.isOK = res; + reply_ng(CMD_HF_ICLASS_READBL, PM3_ETIMEOUT, (uint8_t *)&response, sizeof(response)); + } + } + +out: switch_off(); } @@ -1328,8 +1402,8 @@ void iClass_ReadBlk(uint8_t blockno) { // then authenticate AA2, and read those blocks by calling this. // By the looks at it only 2K cards is supported, or first page dumps on larger cards. // turn off afterwards -void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { - +void iClass_Dump(uint8_t *msg) { + BigBuf_free(); uint8_t *dataout = BigBuf_malloc(0xFF * 8); @@ -1341,27 +1415,90 @@ void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { } memset(dataout, 0xFF, 0xFF * 8); - bool isOK; - uint8_t blkcnt = 0; - for (; blkcnt < numblks; blkcnt++) { - isOK = iclass_readblock(start_blockno + blkcnt, dataout + (8 * blkcnt)); - if (isOK == false) { - Dbprintf("failed to read block %02X", start_blockno + blkcnt); - break; + iclass_dump_req_t *cmd = (iclass_dump_req_t *)msg; + iclass_auth_req_t *req = &cmd->req; + + Iso15693InitReader(); + + // select tag. + uint32_t eof_time = 0; + picopass_hdr hdr = {0}; + bool res = select_iclass_tag( (uint8_t *)&hdr, req->use_credit_key, &eof_time); + if (res == false) { + if (req->send_reply) { + reply_ng(CMD_HF_ICLASS_DUMP, PM3_ETIMEOUT, NULL, 0); + } + switch_off(); + return; + } + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // authenticate + if (req->do_auth) { + + res = authenticate_iclass_tag(req, &hdr, &start_time, &eof_time, NULL); + if (res == false) { + if (req->send_reply) { + reply_ng(CMD_HF_ICLASS_DUMP, PM3_ETIMEOUT, NULL, 0); + } + switch_off(); + return; } } - switch_off(); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - struct p { - bool isOK; - uint8_t block_cnt; - uint32_t bb_offset; - } PACKED payload; - payload.isOK = isOK; - payload.block_cnt = blkcnt; - payload.bb_offset = dataout - BigBuf_get_addr(); - reply_ng(CMD_HF_ICLASS_DUMP, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); + // main read loop + uint8_t i = cmd->start_block; + for (; i <= cmd->end_block; i++) { + res = iclass_read_block(i, dataout + (8 * i)); + if (res == false) { + Dbprintf("failed to read block %02X", req->blockno + i); + break; + } + /* + else { + Dbprintf("blk: %u (0x%02x) | %02x %02x %02x %02x %02x %02x %02x %02x ", + i, + i, + dataout[8 * i], + dataout[(8 * i) + 1], + dataout[(8 * i) + 2], + dataout[(8 * i) + 3], + dataout[(8 * i) + 4], + dataout[(8 * i) + 5], + dataout[(8 * i) + 6], + dataout[(8 * i) + 7] + ); + + + } + */ + } + + switch_off(); + + // copy diversified key back. + if (req->do_auth) { + if (req->use_credit_key) + memcpy(dataout + (8 * 4), hdr.key_c, 8); + else + memcpy(dataout + (8 * 3), hdr.key_d, 8); + } + + if (req->send_reply) { + struct p { + bool isOK; + uint8_t block_cnt; + uint32_t bb_offset; + } PACKED response; + + response.isOK = res; + response.block_cnt = i; + response.bb_offset = dataout - BigBuf_get_addr(); + reply_ng(CMD_HF_ICLASS_DUMP, PM3_SUCCESS, (uint8_t *)&response, sizeof(response)); + } BigBuf_free(); } @@ -1372,8 +1509,8 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data) { AddCrc(write + 1, 13); uint8_t resp[10] = {0}; - uint32_t eof_time = 0; - bool isOK = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, 0, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + uint32_t eof_time = 0, start_time = 0; + bool isOK = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); if (isOK == false) { return false; } @@ -1400,21 +1537,103 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data) { } // turn off afterwards -void iClass_WriteBlock(uint8_t blockno, uint8_t *data) { +void iClass_WriteBlock(uint8_t *msg) { + LED_A_ON(); - uint8_t isOK = iclass_writeblock_ext(blockno, data); + + iclass_writeblock_req_t *payload = (iclass_writeblock_req_t *)msg; + + uint8_t write[16] = { 0x80 | ICLASS_CMD_UPDATE, payload->req.blockno }; + + Iso15693InitReader(); + + // select tag. + uint32_t eof_time = 0; + picopass_hdr hdr = {0}; + bool res = select_iclass_tag( (uint8_t *)&hdr, payload->req.use_credit_key, &eof_time); + if (res == false) { + goto out; + } + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + uint8_t mac[4] = {0}; + + // authenticate + if (payload->req.do_auth) { + + res = authenticate_iclass_tag(&payload->req, &hdr, &start_time, &eof_time, mac); + if (res == false) { + goto out; + } + } + + // calc new mac for write + uint8_t wb[9]; + wb[0] = payload->req.blockno; + memcpy(wb + 1, payload->data, 8); + + if (payload->req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + + memcpy(write + 2, payload->data, 8); // data + memcpy(write + 10, mac, sizeof(mac)); // mac + AddCrc(write + 1, 13); + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + uint8_t resp[10] = {0}; + res = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + if (res == false) { + goto out; + } + + // verify write + uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (payload->req.blockno == 2) { + // check response. e-purse update swaps first and second half + if (memcmp(payload->data + 4, resp, 4) || memcmp(payload->data, resp + 4, 4)) { + res = false; + goto out; + } + } else if (payload->req.blockno == 3 || payload->req.blockno == 4) { + // check response. Key updates always return 0xffffffffffffffff + if (memcmp(all_ff, resp, 8)) { + res = false; + goto out; + } + } else { + // check response. All other updates return unchanged data + if (memcmp(payload->data, resp, 8)) { + res = false; + goto out; + } + } + +out: switch_off(); - reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + + if (payload->req.send_reply) + reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t)); } // turn off afterwards void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) { +} + +void iClass_Restore(uint8_t *msg) { + + iclass_restore_req_t *cmd = (iclass_restore_req_t *)msg; +// iclass_auth_req_t *req = &cmd->req; + LED_A_ON(); uint16_t written = 0; - uint16_t total_blocks = (endblock - startblock) + 1; - for (uint8_t b = startblock; b < total_blocks; b++) { + uint16_t total_blocks = (cmd->end_block - cmd->start_block) + 1; + for (uint8_t b = cmd->start_block; b < total_blocks; b++) { - if (iclass_writeblock_ext(b, data + ((b - startblock) * 12))) { + if (iclass_writeblock_ext(b, cmd->data + ((b - cmd->start_block) * 12))) { Dbprintf("Write block [%02x] successful", b); written++; } else { diff --git a/armsrc/iclass.h b/armsrc/iclass.h index cc93f771c..008b11207 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -12,15 +12,17 @@ #define __ICLASS_H #include "common.h" +#include "pm3_cmd.h" void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); void ReaderIClass(uint8_t arg0); void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac); -void iClass_WriteBlock(uint8_t blockno, uint8_t *data); -void iClass_Dump(uint8_t blockno, uint8_t numblks); +void iClass_WriteBlock(uint8_t *msg); +void iClass_Dump(uint8_t *msg); + +void iClass_Restore(uint8_t *msg); void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); -void iClass_ReadCheck(uint8_t blockno, uint8_t keytype); int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf); void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); @@ -28,8 +30,12 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain); void iClass_Authentication(uint8_t *bytes); -bool iclass_auth(uint8_t *bytes, bool send_reply, uint8_t *dataout); +bool iclass_auth(iclass_auth_req_t *payload, uint8_t *out); + +void iClass_ReadBlock(uint8_t *msg); +bool iclass_read_block(uint8_t blockno, uint8_t *data); + +bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time); +bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out); -void iClass_ReadBlk(uint8_t blockno); -bool iclass_readblock(uint8_t blockno, uint8_t *data); #endif diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index e1cf99305..f0c967205 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -130,10 +130,10 @@ int CmdHFSearch(const char *Cmd) { } PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for iClass / PicoPass tag..."); + PrintAndLogEx(INPLACE, " Searching for iCLASS / PicoPass tag..."); if (IfPm3Iclass()) { - if (readIclass(false, false) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iClass tag / PicoPass tag") " found\n"); + if (read_iclass_csn(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iCLASS tag / PicoPass tag") " found\n"); res = PM3_SUCCESS; } } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 6a99c0db7..0c3c1c7ec 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -212,9 +212,9 @@ static int usage_hf_iclass_readblock(void) { PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } -static int usage_hf_iclass_readtagfile(void) { +static int usage_hf_iclass_view(void) { PrintAndLogEx(NORMAL, "Print a iCLASS tag dump file\n"); - PrintAndLogEx(NORMAL, "Usage: hf iClass readtagfile [f ] [s ] [e ] [v]\n"); + PrintAndLogEx(NORMAL, "Usage: hf iClass view [f ] [s ] [e ] [v]\n"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h Show this help"); PrintAndLogEx(NORMAL, " f filename of dump"); @@ -222,8 +222,8 @@ static int usage_hf_iclass_readtagfile(void) { PrintAndLogEx(NORMAL, " e end printing at this block (default 0, ALL)"); PrintAndLogEx(NORMAL, " v verbose output"); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass readtagfile f hf-iclass-AA162D30F8FF12F1-dump.bin")); - PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass readtagfile s 1 f hf-iclass-AA162D30F8FF12F1-dump.bin")); + PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass view f hf-iclass-AA162D30F8FF12F1-dump.bin")); + PrintAndLogEx(NORMAL, _YELLOW_("\thf iclass view s 1 f hf-iclass-AA162D30F8FF12F1-dump.bin")); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -362,26 +362,6 @@ static int usage_hf_iclass_permutekey(void) { return PM3_SUCCESS; } -// iclass / picopass chip config structures and shared routines -typedef struct { - uint8_t app_limit; //[8] - uint8_t otp[2]; //[9-10] - uint8_t block_writelock;//[11] - uint8_t chip_config; //[12] - uint8_t mem_config; //[13] - uint8_t eas; //[14] - uint8_t fuses; //[15] -} picopass_conf_block_t; - -typedef struct { - uint8_t csn[8]; - picopass_conf_block_t conf; - uint8_t epurse[8]; - uint8_t key_d[8]; - uint8_t key_c[8]; - uint8_t app_issuer_area[8]; -} picopass_hdr; - typedef enum { None = 0, DES, @@ -436,6 +416,10 @@ static uint8_t notset(uint8_t val, uint8_t mask) { return !(val & mask); } +static uint8_t get_pagemap(const picopass_hdr *hdr) { + return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; +} + static void fuse_config(const picopass_hdr *hdr) { uint8_t fuses = hdr->conf.fuses; @@ -452,14 +436,22 @@ static void fuse_config(const picopass_hdr *hdr) { else PrintAndLogEx(SUCCESS, "Coding: " _YELLOW_("ISO 14443-B only")); } - // 1 1 - if (isset(fuses, FUSE_CRYPT1) && isset(fuses, FUSE_CRYPT0)) PrintAndLogEx(SUCCESS, " Crypt: Secured page, " _GREEN_("keys not locked")); - // 1 0 - if (isset(fuses, FUSE_CRYPT1) && notset(fuses, FUSE_CRYPT0)) PrintAndLogEx(INFO, " Crypt: Secured page, keys locked"); - // 0 1 - if (notset(fuses, FUSE_CRYPT1) && isset(fuses, FUSE_CRYPT0)) PrintAndLogEx(SUCCESS, " Crypt: Non secured page"); - // 0 0 - if (notset(fuses, FUSE_CRYPT1) && notset(fuses, FUSE_CRYPT0)) PrintAndLogEx(INFO, " Crypt: No auth possible. Read only if RA is enabled"); + + uint8_t pagemap = get_pagemap(hdr); + switch (pagemap) { + case 0x0: + PrintAndLogEx(INFO, " Crypt: No auth possible. Read only if RA is enabled"); + break; + case 0x1: + PrintAndLogEx(SUCCESS, " Crypt: Non secured page"); + break; + case 0x2: + PrintAndLogEx(INFO, " Crypt: Secured page, keys locked"); + break; + case 0x03: + PrintAndLogEx(SUCCESS, " Crypt: Secured page, " _GREEN_("keys not locked")); + break; + } if (isset(fuses, FUSE_RA)) PrintAndLogEx(SUCCESS, " RA: Read access enabled"); @@ -527,21 +519,22 @@ static void mem_app_config(const picopass_hdr *hdr) { getMemConfig(mem, chip, &app_areas, &kb); - // three configuration bits that decides sizes - uint8_t type = (chip & 0x10) >> 2; - type |= (mem & 0x80) >> 6; - type |= (mem & 0x20) >> 5; - + uint8_t type = get_mem_config(hdr); uint8_t app1_limit = hdr->conf.app_limit - 5; // minus header blocks uint8_t app2_limit = card_app2_limit[type]; + uint8_t pagemap = get_pagemap(hdr); PrintAndLogEx(INFO, "------ " _CYAN_("Memory") " ------"); + + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + PrintAndLogEx(INFO, " %u KBits (%u bytes)", kb, app2_limit * 8); + PrintAndLogEx(INFO, " Tag has not App Areas"); + return; + } + PrintAndLogEx(INFO, " %u KBits/%u App Areas (%u bytes)", kb, app_areas, app2_limit * 8); PrintAndLogEx(INFO, " AA1 blocks %u { 0x06 - 0x%02X (06 - %02d) }", app1_limit , app1_limit + 5, app1_limit + 5); - if (app1_limit <= app2_limit) - PrintAndLogEx(INFO, " AA2 blocks %u { 0x%02X - 0x%02X (%02d - %02d) }", app2_limit - app1_limit, app1_limit + 5 + 1, app2_limit, app1_limit + 5 + 1, app2_limit); - else - PrintAndLogEx(INFO, " AA1 is configured to take all available space"); + PrintAndLogEx(INFO, " AA2 blocks %u { 0x%02X - 0x%02X (%02d - %02d) }", app2_limit - app1_limit, app1_limit + 5 + 1, app2_limit, app1_limit + 5 + 1, app2_limit); PrintAndLogEx(INFO, "------ " _CYAN_("KeyAccess") " ------"); PrintAndLogEx(INFO, " Kd = Debit key (AA1), Kc = Credit key (AA2)"); @@ -811,14 +804,55 @@ static int CmdHFiClassSim(const char *Cmd) { } static int CmdHFiClassInfo(const char *Cmd) { - return readIclass(false, true); + return info_iclass(); +} + +int read_iclass_csn(bool loop, bool verbose) { + + uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); + int res = PM3_SUCCESS; + + while (kbd_enter_pressed() == false) { + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ICLASS_READER, flags, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + + uint8_t status = resp.oldarg[0] & 0xff; + + if (loop == false) { + if (status == 0 || status == 0xFF) { + if (verbose) PrintAndLogEx(WARNING, "iCLASS / ISO15693 card select failed"); + res = PM3_EOPABORTED; + break; + } + } else { + if (status == 0xFF) + continue; + } + + picopass_hdr *hdr = (picopass_hdr *)resp.data.asBytes; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(hdr->csn, sizeof(hdr->csn))); + PrintAndLogEx(SUCCESS, " Config: " _GREEN_("%s"), sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); + + if (loop == false) + break; + } + } + + DropField(); + return res; } static int CmdHFiClassReader(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_hf_iclass_reader(); bool loop_read = (cmdp == '1') ? false : true; - return readIclass(loop_read, true); + + return read_iclass_csn(loop_read, true); } static int CmdHFiClassReader_Replay(const char *Cmd) { @@ -1006,7 +1040,7 @@ static int CmdHFiClassESave(const char *Cmd) { } saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, 16); + saveFileEML(filename, dump, bytes, 8); saveFileJSON(filename, jsfIclass, dump, bytes, NULL); free(dump); @@ -1316,11 +1350,9 @@ static void calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_ doMAC_N(wb, sizeof(wb), div_key, MAC); } -static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool verbose) { +static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) { uint8_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); - if (use_credit_key) - flags |= FLAG_ICLASS_READER_CEDITKEY; clearCommandBuffer(); PacketResponseNG resp; @@ -1377,7 +1409,7 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u return false; } - iclass_auth_resp_t *packet = (iclass_auth_resp_t *)resp.data.asBytes; + iclass_readblock_resp_t *packet = (iclass_readblock_resp_t *)resp.data.asBytes; if (packet->isOK == 0) { if (verbose) PrintAndLogEx(FAILED, "authentication error"); @@ -1398,9 +1430,6 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u static int CmdHFiClassDump(const char *Cmd) { - uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t c_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t CreditKEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t keyNbr = 0; @@ -1409,13 +1438,11 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t fileNameLen = 0; char filename[FILE_PATH_SIZE] = {0}; char tempStr[50] = {0}; - bool have_debit_key = false; bool have_credit_key = false; - bool use_credit_key = false; bool elite = false; bool rawkey = false; bool errors = false; - bool verbose = false; + bool auth = false; uint8_t cmdp = 0; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { @@ -1423,6 +1450,7 @@ static int CmdHFiClassDump(const char *Cmd) { case 'h': return usage_hf_iclass_dump(); case 'c': + auth = true; have_credit_key = true; dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); if (dataLen == 16) { @@ -1456,7 +1484,7 @@ static int CmdHFiClassDump(const char *Cmd) { cmdp += 2; break; case 'k': - have_debit_key = true; + auth = true; dataLen = param_getstr(Cmd, cmdp + 1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); @@ -1480,24 +1508,34 @@ static int CmdHFiClassDump(const char *Cmd) { rawkey = true; cmdp++; break; - case 'v': - verbose = true; - cmdp++; - break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); errors = true; break; } } - if (errors || cmdp < 2) return usage_hf_iclass_dump(); + if (errors) return usage_hf_iclass_dump(); // if no debit key given try credit key on AA1 (not for iclass but for some picopass this will work) - if (!have_debit_key && have_credit_key) use_credit_key = true; - uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE | FLAG_ICLASS_READER_AIA); +// vi behöver: +// block 0-5 (6st) om AA1/AA2 +// - block 3 får vi vid KD normalt 0xFF +// - block 4 får vi vid KC normalt 0xFF - //get config and first 3 blocks +// block 0-2 (3st) om non-secure.. +// +// +// vi har 0,1,2 redan. +// +// från bigbuffer så behöver vi... +// +// block 18 /0x12 saknas i dump data. + + + uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); + + //get CSN and config PacketResponseNG resp; uint8_t tag_data[255 * 8]; @@ -1511,8 +1549,7 @@ static int CmdHFiClassDump(const char *Cmd) { DropField(); uint8_t readStatus = resp.oldarg[0] & 0xff; - uint8_t *data = resp.data.asBytes; - picopass_hdr *hdr = (picopass_hdr *)data; + picopass_hdr *hdr = (picopass_hdr *)resp.data.asBytes; if (readStatus == 0) { PrintAndLogEx(FAILED, "no tag found"); @@ -1520,32 +1557,63 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_ESOFT; } - if (readStatus & (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC | FLAG_ICLASS_AIA)) { - memcpy(tag_data, data, 8 * 6); + uint8_t pagemap = get_pagemap(hdr); + + if (readStatus & (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC)) { + + memcpy(tag_data, hdr, 24); uint8_t type = get_mem_config(hdr); - app_limit1 = hdr->conf.app_limit; - app_limit2 = card_app2_limit[type]; + + // tags configured for NON SECURE PAGE, acts different + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + + PrintAndLogEx(INFO, "Card in non-secure page mode detected"); + + app_limit1 = card_app2_limit[type]; + app_limit2 = 0; + } else { + app_limit1 = hdr->conf.app_limit; + app_limit2 = card_app2_limit[type]; + } + } else { PrintAndLogEx(FAILED, "failed to read block 0,1,2"); DropField(); return PM3_ESOFT; } - // authenticate debit key and get div_key - later store in dump block 3 - if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose) == false) { - PrintAndLogEx(WARNING, "failed authenticating with debit key"); - DropField(); - return PM3_ESOFT; + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + PrintAndLogEx(INFO, "Dumping all available memory, block 3 - %u (0x%02x)",app_limit1 , app_limit1); + if (auth) { + PrintAndLogEx(INFO, "No keys needed, ignoring user supplied key"); + } + } else { + if (auth == false) { + PrintAndLogEx(FAILED, "Run command with keys"); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Card has atleast 2 application areas. AA1 limit %u (0x%02X) AA2 limit %u (0x%02X)", app_limit1, app_limit1, app_limit2, app_limit2); } - struct { - uint8_t start_blockno; - uint8_t numblks; - } PACKED payload; - payload.start_blockno = 6; - payload.numblks = app_limit1 - 5; - + iclass_dump_req_t payload = { + .req.use_raw = rawkey, + .req.use_elite = elite, + .req.use_credit_key = false, + .req.send_reply = true, + .req.do_auth = auth, + .end_block = app_limit1, + }; + memcpy(payload.req.key, KEY, 8); + + // tags configured for NON SECURE PAGE, acts different + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + payload.start_block = 3; + payload.req.do_auth = false; + } else { + payload.start_block = 6; + } + clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_DUMP, (uint8_t*)&payload, sizeof(payload)); @@ -1568,8 +1636,6 @@ static int CmdHFiClassDump(const char *Cmd) { return resp.status; } - // dump cmd switch off at device when finished. - struct p_resp { bool isOK; uint8_t block_cnt; @@ -1582,114 +1648,135 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_ESOFT; } - // 13 + uint32_t startindex = packet->bb_offset; uint32_t blocks_read = packet->block_cnt; - if (blocks_read == app_limit1 - 5) { - PrintAndLogEx(INFO, "ICE: got all AA1"); + + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + if (blocks_read != app_limit1 - 2) { + PrintAndLogEx(WARNING, "failed to get all memory (non secure page mode)"); + } + } else { + if (blocks_read != app_limit1 - 5) { + PrintAndLogEx(WARNING, "failed to get all AA1 memory"); + } } - uint16_t offset = (6 * 8); - uint32_t startindex = packet->bb_offset; + uint8_t tempbuf[0xFF * 8] = {0}; // response ok - now get bigbuf content of the dump - if (!GetFromDevice(BIG_BUF, tag_data + offset, blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - PrintAndLogEx(INFO, "ICE: blocks_read (13) == %u (0x%02x)", blocks_read, blocks_read); - offset += (blocks_read * 8); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + // all memory available + memcpy(tag_data + (8 * 3), tempbuf + (8 * 3), (blocks_read * 8) ); + } else { + // div key KD + memcpy(tag_data + (8 * 3), tempbuf + (8 * 3), 8); + // AA1 data + memcpy(tag_data + (8 * 6), tempbuf + (8 * 6), (blocks_read * 8) ); + } + uint16_t bytes_got = (app_limit1 + 1) * 8; + // try AA2 Kc, Credit - if (have_credit_key) { + bool aa2_success = false; - memset(MAC, 0, 4); + if (have_credit_key && pagemap != 0x01) { - // AA2 authenticate credit key and git c_div_key - later store in dump block 4 - if (select_and_auth(CreditKEY, MAC, c_div_key, true, elite, rawkey, verbose) == false) { - PrintAndLogEx(WARNING, "failed authenticating with credit key"); - DropField(); - return PM3_ESOFT; - } - - payload.start_blockno = app_limit1; - payload.numblks = app_limit2 - app_limit1; + // AA2 authenticate credit key + memcpy(payload.req.key, CreditKEY, 8); + + payload.req.use_credit_key = true; + payload.start_block = app_limit1 + 1; + payload.end_block = app_limit2; + payload.req.do_auth = true; clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_DUMP, (uint8_t*)&payload, sizeof(payload)); - if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "command execute timeout 2"); - return PM3_ETIMEOUT; + while (true) { + printf("."); + fflush(stdout); + if (kbd_enter_pressed()) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "aborted via keyboard!\n"); + DropField(); + return PM3_EOPABORTED; + } + + if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) + break; } if (resp.status != PM3_SUCCESS) { PrintAndLogEx(ERR, "failed to communicate with card"); - return resp.status; + goto write_dump; } packet = (struct p_resp *)resp.data.asBytes; if (packet->isOK == false) { - PrintAndLogEx(WARNING, "read block failed using credit key"); - return PM3_ESOFT; + PrintAndLogEx(WARNING, "failed read block using credit key"); + goto write_dump; } - // + blocks_read = packet->block_cnt; startindex = packet->bb_offset; - if (blocks_read == app_limit2 - app_limit1) { - PrintAndLogEx(INFO, "ICE: got all AA2"); - } - - if (blocks_read * 8 > sizeof(tag_data) - offset) { - PrintAndLogEx(FAILED, "data exceeded buffer size!"); - blocks_read = (sizeof(tag_data) - offset) / 8; + if (blocks_read * 8 > sizeof(tag_data) - bytes_got) { + PrintAndLogEx(WARNING, "data exceeded buffer size! "); + blocks_read = (sizeof(tag_data) - bytes_got) / 8; } // get dumped data from bigbuf - if (!GetFromDevice(BIG_BUF, tag_data + offset, blocks_read * 8, startindex, NULL, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); - return PM3_ETIMEOUT; + goto write_dump; } + + // div key KC + memcpy(tag_data + (8 * 4), tempbuf + (8 * 4), 8); - offset += blocks_read * 8; + // AA2 data + memcpy(tag_data + (8 * (app_limit1 + 1)), tempbuf + (8 * (app_limit1 + 1)), (blocks_read * 8) ); + + bytes_got = (blocks_read * 8); + + aa2_success = true; } - DropField(); - - // add diversified keys to dump - if (have_debit_key) - memcpy(tag_data + (3 * 8), div_key, 8); - - if (have_credit_key) - memcpy(tag_data + (4 * 8), c_div_key, 8); - +write_dump: + + if (have_credit_key && pagemap != 0x01 && aa2_success == false) + PrintAndLogEx(INFO, "Reading AA2 failed. dumping AA1 data to file"); // print the dump PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "------+--+-------------------------+----------"); - PrintAndLogEx(INFO, " CSN |00| " _GREEN_("%s") "|", sprint_hex(tag_data, 8)); - printIclassDumpContents(tag_data, 1, (offset / 8), offset); + PrintAndLogEx(INFO, "------+----+-------------------------+----------"); + PrintAndLogEx(INFO, " CSN |0x00| " _GREEN_("%s") "|", sprint_hex(tag_data, 8)); + printIclassDumpContents(tag_data, 1, (bytes_got / 8), bytes_got); + // use CSN as filename if (filename[0] == 0) { - //Use the first block (CSN) for filename strcat(filename, "hf-iclass-"); FillFileNameByUID(filename, tag_data, "-dump", 8); } // save the dump to .bin file - PrintAndLogEx(SUCCESS, "saving dump file - %zu blocks read", offset / 8); - saveFile(filename, ".bin", tag_data, offset); - saveFileEML(filename, tag_data, offset, 8); - saveFileJSON(filename, jsfIclass, tag_data, offset, NULL); + PrintAndLogEx(SUCCESS, "saving dump file - %zu blocks read", bytes_got / 8); + saveFile(filename, ".bin", tag_data, bytes_got); + saveFileEML(filename, tag_data, bytes_got, 8); + saveFileJSON(filename, jsfIclass, tag_data, bytes_got, NULL); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass decrypt") "` to decrypt dump file"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass readtagfile ") "` to view dump file"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view") "` to view dump file"); return PM3_SUCCESS; } static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool verbose) { - +/* uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose) == false) { @@ -1706,7 +1793,38 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bo memcpy(payload.data, bldata, 8); memcpy(payload.data + 8, MAC, 4); + + +// +typedef struct { + uint8_t key[8]; + bool use_raw; + bool use_elite; + bool use_credit_key; + bool send_reply; + bool do_auth; + uint8_t blockno; +} PACKED iclass_auth_req_t; +// iCLASS write block request data structure +typedef struct { + iclass_auth_req_t req; + uint8_t data[8]; +} PACKED iclass_writeblock_req_t; + + +*/ +iclass_writeblock_req_t payload = { + .req.use_raw = rawkey, + .req.use_elite = elite, + .req.use_credit_key = use_credit_key, + .req.blockno = blockno, + .req.send_reply = true, + .req.do_auth = true, + }; + memcpy(payload.req.key, KEY, 8); + memcpy(payload.data, bldata, sizeof(payload.data)); + clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_WRITEBL, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; @@ -2015,38 +2133,26 @@ static int CmdHFiClassCloneTag(const char *Cmd) { return resp.status; } -static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose, bool auth) { - - // block 0,1 should always be able to read, and block 5 on some cards. - if (auth || blockno >= 2) { - uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_and_auth(KEY, MAC, div_key, (keyType == 0x18), elite, rawkey, verbose) == false) { - if (verbose) PrintAndLogEx(FAILED, "select/auth failed"); - DropField(); - return PM3_ESOFT; - } - } else { - uint8_t CSN[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_only(CSN, CCNR, (keyType == 0x18), verbose) == false) { - if (verbose) PrintAndLogEx(FAILED, "select only failed"); - DropField(); - return PM3_ESOFT; - } - } - +static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose, bool auth, uint8_t *out) { + + iclass_auth_req_t payload = { + .use_raw = rawkey, + .use_elite = elite, + .use_credit_key = (keyType == 0x18), + .blockno = blockno, + .send_reply = true, + .do_auth = auth, + }; + memcpy(payload.key, KEY, 8); + PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_READBL, (uint8_t *)&blockno, sizeof(uint8_t)); + clearCommandBuffer(); + SendCommandNG(CMD_HF_ICLASS_READBL, (uint8_t*)&payload, sizeof(payload)); if (WaitForResponseTimeout(CMD_HF_ICLASS_READBL, &resp, 2000) == false) { if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); - DropField(); return PM3_ETIMEOUT; } - - DropField(); if (resp.status != PM3_SUCCESS) { if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); @@ -2054,83 +2160,20 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo } // return data. - struct p { - bool isOK; - uint8_t blockdata[8]; - } PACKED; + iclass_readblock_resp_t *packet = (iclass_readblock_resp_t *)resp.data.asBytes; - struct p *result = (struct p *)resp.data.asBytes; - if (result->isOK == false) { + if (packet->isOK == false) { + if (verbose) PrintAndLogEx(FAILED, "authentication error"); return PM3_ESOFT; } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, " block %02X : " _GREEN_("%s"), blockno, sprint_hex(result->blockdata, sizeof(result->blockdata))); + PrintAndLogEx(SUCCESS, " block %02X : " _GREEN_("%s"), blockno, sprint_hex(packet->data, sizeof(packet->data))); PrintAndLogEx(NORMAL, ""); - - if (memcmp(result->blockdata, empty, 8) == 0) - return PM3_SUCCESS; - if (blockno < 6 || blockno > 7) - return PM3_SUCCESS; + if (out) + memcpy(out, packet->data, sizeof(packet->data)); - bool use_sc = IsCryptoHelperPresent(verbose); - if (use_sc == false) - return PM3_SUCCESS; - - // crypto helper available. - PrintAndLogEx(INFO, "----------------------------- " _CYAN_("cardhelper") " -----------------------------"); - - switch (blockno) { - case 6: { - DecodeBlock6(result->blockdata); - break; - } - case 7: { - - uint8_t dec_data[8]; - - uint64_t a = bytes_to_num(result->blockdata, 8); - bool starts = (leadingzeros(a) < 12); - bool ones = (countones(a) > 16 && countones(a) < 48); - - if (starts && ones) { - PrintAndLogEx(INFO, "data looks encrypted, False Positives " _YELLOW_("ARE") " possible"); - Decrypt(result->blockdata, dec_data); - PrintAndLogEx(SUCCESS, "decrypted : " _GREEN_("%s"), sprint_hex(dec_data, sizeof(dec_data))); - } else { - memcpy(dec_data, result->blockdata, sizeof(dec_data)); - PrintAndLogEx(INFO, "data looks unencrypted, trying to decode"); - } - - if (memcmp(dec_data, empty, 8) != 0) { - - //todo: remove preamble/sentinal - - uint32_t top = 0, mid, bot; - mid = bytes_to_num(dec_data, 4); - bot = bytes_to_num(dec_data + 4, 4); - - char hexstr[16 + 1] = {0}; - hex_to_buffer((uint8_t *)hexstr, dec_data, 8, sizeof(hexstr) - 1, 0, 0, true); - char binstr[64 + 1] = {0}; - hextobinstring(binstr, hexstr); - uint8_t i = 0; - while (i < strlen(binstr) && binstr[i++] == '0'); - - i &= 0x3C; - PrintAndLogEx(SUCCESS, " bin : %s", binstr + i); - PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, "------------------------------ " _CYAN_("wiegand") " -------------------------------"); - wiegand_message_t packed = initialize_message_object(top, mid, bot); - HIDTryUnpack(&packed, true); - } else { - PrintAndLogEx(INFO, "no credential found"); - } - break; - } - } - PrintAndLogEx(INFO, "----------------------------------------------------------------------"); return PM3_SUCCESS; } @@ -2214,11 +2257,79 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { PrintAndLogEx(SUCCESS, "Using key %s", sprint_hex(KEY, 8)); } + if (auth == false && verbose) { + PrintAndLogEx(WARNING, "warning: no authentication used with read. Typical for cards configured toin `non-secure page`"); + } - if (auth == false) - PrintAndLogEx(WARNING, "warning: no authentication used with read, only a few specific blocks can be read accurately without authentication."); + uint8_t data[8] = {0}; + int res = iclass_read_block(KEY, blockno, keyType, elite, rawkey, verbose, auth, data); + if (res != PM3_SUCCESS) + return res; + + if (blockno < 6 || blockno > 7) + return PM3_SUCCESS; + + if (memcmp(data, empty, 8) == 0) + return PM3_SUCCESS; - return iclass_read_block(KEY, blockno, keyType, elite, rawkey, verbose, auth); + bool use_sc = IsCryptoHelperPresent(verbose); + if (use_sc == false) + return PM3_SUCCESS; + + // crypto helper available. + PrintAndLogEx(INFO, "----------------------------- " _CYAN_("cardhelper") " -----------------------------"); + + switch (blockno) { + case 6: { + DecodeBlock6(data); + break; + } + case 7: { + + uint8_t dec_data[8]; + + uint64_t a = bytes_to_num(data, 8); + bool starts = (leadingzeros(a) < 12); + bool ones = (countones(a) > 16 && countones(a) < 48); + + if (starts && ones) { + PrintAndLogEx(INFO, "data looks encrypted, False Positives " _YELLOW_("ARE") " possible"); + Decrypt(data, dec_data); + PrintAndLogEx(SUCCESS, "decrypted : " _GREEN_("%s"), sprint_hex(dec_data, sizeof(dec_data))); + } else { + memcpy(dec_data, data, sizeof(dec_data)); + PrintAndLogEx(INFO, "data looks unencrypted, trying to decode"); + } + + if (memcmp(dec_data, empty, 8) != 0) { + + //todo: remove preamble/sentinal + + uint32_t top = 0, mid, bot; + mid = bytes_to_num(dec_data, 4); + bot = bytes_to_num(dec_data + 4, 4); + + char hexstr[16 + 1] = {0}; + hex_to_buffer((uint8_t *)hexstr, dec_data, 8, sizeof(hexstr) - 1, 0, 0, true); + char binstr[64 + 1] = {0}; + hextobinstring(binstr, hexstr); + uint8_t i = 0; + while (i < strlen(binstr) && binstr[i++] == '0'); + + i &= 0x3C; + PrintAndLogEx(SUCCESS, " bin : %s", binstr + i); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "------------------------------ " _CYAN_("wiegand") " -------------------------------"); + wiegand_message_t packed = initialize_message_object(top, mid, bot); + HIDTryUnpack(&packed, true); + } else { + PrintAndLogEx(INFO, "no credential found"); + } + break; + } + } + PrintAndLogEx(INFO, "----------------------------------------------------------------------"); + return PM3_SUCCESS; } static int CmdHFiClass_loclass(const char *Cmd) { @@ -2281,16 +2392,16 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e */ int i = startblock; - PrintAndLogEx(INFO, "------+--+-------------------------+----------"); + PrintAndLogEx(INFO, "------+----+-------------------------+----------"); while (i <= endblock) { uint8_t *blk = iclass_dump + (i * 8); - PrintAndLogEx(INFO, " |%02X| %s", i, sprint_hex_ascii(blk, 8)); + PrintAndLogEx(INFO, " |0x%02X| %s", i, sprint_hex_ascii(blk, 8)); i++; } - PrintAndLogEx(INFO, "------+--+-------------------------+----------"); + PrintAndLogEx(INFO, "------+----+-------------------------+----------"); } -static int CmdHFiClassReadTagFile(const char *Cmd) { +static int CmdHFiClassView(const char *Cmd) { int startblock = 0; int endblock = 0; char filename[FILE_PATH_SIZE]; @@ -2299,7 +2410,7 @@ static int CmdHFiClassReadTagFile(const char *Cmd) { while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': - return usage_hf_iclass_readtagfile(); + return usage_hf_iclass_view(); case 'f': if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { PrintAndLogEx(FAILED, "Filename too long"); @@ -2327,7 +2438,7 @@ static int CmdHFiClassReadTagFile(const char *Cmd) { } } - if (errors || (strlen(Cmd) == 0)) return usage_hf_iclass_readtagfile(); + if (errors || (strlen(Cmd) == 0)) return usage_hf_iclass_view(); uint8_t *dump = NULL; size_t bytes_read = 0; @@ -2348,8 +2459,8 @@ static int CmdHFiClassReadTagFile(const char *Cmd) { PrintAndLogEx(NORMAL, ""); uint8_t *csn = dump; - PrintAndLogEx(INFO, "------+--+-------------------------+----------"); - PrintAndLogEx(INFO, " CSN |00| " _GREEN_("%s") "|", sprint_hex(csn, 8)); + PrintAndLogEx(INFO, "------+----+-------------------------+----------"); + PrintAndLogEx(INFO, " CSN |0x00| " _GREEN_("%s") "|", sprint_hex(csn, 8)); printIclassDumpContents(dump, startblock, endblock, bytes_read); free(dump); return PM3_SUCCESS; @@ -2472,7 +2583,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { if (givenCSN == false) { uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_only(CSN, CCNR, false, true) == false) { + if (select_only(CSN, CCNR, true) == false) { DropField(); return PM3_ESOFT; } @@ -2683,7 +2794,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { // Get CSN / UID and CCNR PrintAndLogEx(SUCCESS, "Reading tag CSN"); for (uint8_t i = 0; i < ICLASS_AUTH_RETRY && !got_csn; i++) { - got_csn = select_only(CSN, CCNR, false, false); + got_csn = select_only(CSN, CCNR, false); if (got_csn == false) PrintAndLogEx(WARNING, "one more try"); } @@ -3192,7 +3303,7 @@ static command_t CommandTable[] = { {"permutekey", CmdHFiClassPermuteKey, IfPm3Iclass, " Permute function from 'heart of darkness' paper"}, {"rdbl", CmdHFiClass_ReadBlock, IfPm3Iclass, "[options..] Read iCLASS block"}, {"reader", CmdHFiClassReader, IfPm3Iclass, " Act like an iCLASS reader"}, - {"readtagfile", CmdHFiClassReadTagFile, AlwaysAvailable, "[options..] Display content from tag dump file"}, + {"view", CmdHFiClassView, AlwaysAvailable, "[options..] Display content from tag dump file"}, {"replay", CmdHFiClassReader_Replay, IfPm3Iclass, " Read iCLASS tag via replay attack"}, {"sim", CmdHFiClassSim, IfPm3Iclass, "[options..] Simulate iCLASS tag"}, {"sniff", CmdHFiClassSniff, IfPm3Iclass, " Eavesdrop iCLASS communication"}, @@ -3211,7 +3322,6 @@ int CmdHFiClass(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } - //static void test_credential_type(void) { // need AA1 key // Block 5 -> tells if its a legacy or SIO, also tells which key to use. @@ -3225,53 +3335,45 @@ int CmdHFiClass(const char *Cmd) { // SEOS | | | //} -int readIclass(bool loop, bool verbose) { - bool tagFound = false; +int info_iclass(void) { - uint32_t flags = ( - FLAG_ICLASS_READER_INIT | - FLAG_ICLASS_READER_CLEARTRACE | - FLAG_ICLASS_READER_ONLY_ONCE | - FLAG_ICLASS_READER_AIA - ); + uint32_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE); - uint32_t res = PM3_ETIMEOUT; - // loop in client not device - else on windows have a communication error - while (kbd_enter_pressed() == false) { + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ICLASS_READER, flags, 0, 0, NULL, 0); + PacketResponseNG resp; - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_READER, flags, 0, 0, NULL, 0); - PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + uint8_t readStatus = resp.oldarg[0] & 0xff; - uint8_t readStatus = resp.oldarg[0] & 0xff; + // no tag found or button pressed + if (readStatus == 0 || readStatus == 0xFF) { + DropField(); + return PM3_EOPABORTED; + } - // no tag found or button pressed - if ((readStatus == 0 && !loop) || readStatus == 0xFF) { - DropField(); - return PM3_EOPABORTED; - } + picopass_hdr *hdr = (picopass_hdr *)resp.data.asBytes; + picopass_ns_hdr *ns_hdr = (picopass_ns_hdr *)resp.data.asBytes; - uint8_t *data = resp.data.asBytes; - picopass_hdr *hdr = (picopass_hdr *)data; - uint16_t length = resp.length; - - if (length != sizeof(picopass_hdr)) - continue; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " --------------------------"); + PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " --------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); + if (readStatus & FLAG_ICLASS_CSN) { + PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " (uid)", sprint_hex(hdr->csn, sizeof(hdr->csn))); + } - if (readStatus & FLAG_ICLASS_CSN) { - PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " (uid)", sprint_hex(hdr->csn, sizeof(hdr->csn))); - tagFound = true; - } - - if (readStatus & FLAG_ICLASS_CONF) { - PrintAndLogEx(SUCCESS, " Config: %s (Card configuration)", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); - } + if (readStatus & FLAG_ICLASS_CONF) { + PrintAndLogEx(SUCCESS, " Config: %s (Card configuration)", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); + } + + // page mapping. If fuse0|1 == 0x01, card is in non-secure mode, with CSN, CONF, AIA as top 3 blocks. + // page9 in http://www.proxmark.org/files/Documents/13.56%20MHz%20-%20iClass/DS%20Picopass%202KS%20V1-0.pdf + uint8_t pagemap = get_pagemap(hdr); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + PrintAndLogEx(SUCCESS, " AIA: %s (Application Issuer area)", sprint_hex(ns_hdr->app_issuer_area, sizeof(ns_hdr->app_issuer_area))); + } else { if (readStatus & FLAG_ICLASS_CC) { PrintAndLogEx(SUCCESS, "E-purse: %s (Card challenge, CC)", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); @@ -3281,52 +3383,44 @@ int readIclass(bool loop, bool verbose) { PrintAndLogEx(SUCCESS, " Kc: %s (Credit key, hidden)", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); if (readStatus & FLAG_ICLASS_AIA) { -// PrintAndLogEx(INFO, "--------- " _CYAN_("AIA") " ---------"); PrintAndLogEx(SUCCESS, " AIA: %s (Application Issuer area)", sprint_hex(hdr->app_issuer_area, sizeof(hdr->app_issuer_area))); - } - - if (readStatus & FLAG_ICLASS_CONF) { - print_picopass_info(hdr); - } - - // if CSN ends with FF12E0, it's inside HID CSN range. - bool isHidRange = (memcmp(hdr->csn + 5, "\xFF\x12\xE0", 3) == 0); - - if (readStatus & FLAG_ICLASS_AIA) { - bool legacy = (memcmp(hdr->app_issuer_area, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); - - bool se_enabled = (memcmp(hdr->app_issuer_area, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); - - PrintAndLogEx(INFO, "------ " _CYAN_("Fingerprint") " ------"); - - if (isHidRange) { - PrintAndLogEx(SUCCESS, "CSN is in HID range"); - if (legacy) - PrintAndLogEx(SUCCESS, "Credential : " _GREEN_("iCLASS legacy")); - if (se_enabled) - PrintAndLogEx(SUCCESS, "Credential : " _GREEN_("iCLASS SE")); - - } else { - PrintAndLogEx(SUCCESS, _YELLOW_("PicoPass")" (CSN is not in HID range)"); - } - } - - uint8_t cardtype = get_mem_config(hdr); - PrintAndLogEx(SUCCESS, " Card type : " _GREEN_("%s"), card_types[cardtype]); - - if (tagFound && !loop) { - PrintAndLogEx(NORMAL, ""); - DropField(); - return PM3_SUCCESS; - } - } else { - if (verbose) - PrintAndLogEx(WARNING, "command execute timeout"); + } } - if (!loop) break; + + if (readStatus & FLAG_ICLASS_CONF) { + print_picopass_info(hdr); + } + + PrintAndLogEx(INFO, "------ " _CYAN_("Fingerprint") " ------"); + + uint8_t aia[8]; + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) + memcpy(aia, ns_hdr->app_issuer_area, sizeof(aia)); + else + memcpy(aia, hdr->app_issuer_area, sizeof(aia)); + + // if CSN ends with FF12E0, it's inside HID CSN range. + bool isHidRange = (memcmp(hdr->csn + 5, "\xFF\x12\xE0", 3) == 0); + + bool legacy = (memcmp(aia, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + bool se_enabled = (memcmp(aia, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + + if (isHidRange) { + PrintAndLogEx(SUCCESS, "CSN is in HID range"); + if (legacy) + PrintAndLogEx(SUCCESS, "Credential : " _GREEN_("iCLASS legacy")); + if (se_enabled) + PrintAndLogEx(SUCCESS, "Credential : " _GREEN_("iCLASS SE")); + + } else { + PrintAndLogEx(SUCCESS, _YELLOW_("PicoPass")" (CSN is not in HID range)"); + } + + uint8_t cardtype = get_mem_config(hdr); + PrintAndLogEx(SUCCESS, " Card type : " _GREEN_("%s"), card_types[cardtype]); } - PrintAndLogEx(NORMAL, ""); + DropField(); - return res; + return PM3_SUCCESS; } diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index 53880d647..50152fd6c 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -29,7 +29,8 @@ typedef struct iclass_prekey { int CmdHFiClass(const char *Cmd); -int readIclass(bool loop, bool verbose); +int info_iclass(void); +int read_iclass_csn(bool loop, bool verbose); void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize); void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite); diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 766e78c54..258b11b02 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -291,19 +291,74 @@ typedef struct { // iCLASS auth request data structure +// used with read block, dump, write block typedef struct { uint8_t key[8]; bool use_raw; bool use_elite; bool use_credit_key; + bool send_reply; + bool do_auth; + uint8_t blockno; } PACKED iclass_auth_req_t; -// iCLASS auth response data structure +// iCLASS read block response data structure typedef struct { bool isOK; uint8_t div_key[8]; uint8_t mac[4]; -} PACKED iclass_auth_resp_t; + uint8_t data[8]; +} PACKED iclass_readblock_resp_t; + +// iCLASS dump data structure +typedef struct { + iclass_auth_req_t req; + uint8_t start_block; + uint8_t end_block; +} PACKED iclass_dump_req_t; + +// iCLASS write block request data structure +typedef struct { + iclass_auth_req_t req; + uint8_t data[8]; +} PACKED iclass_writeblock_req_t; + +// iCLASS dump data structure +typedef struct { + iclass_auth_req_t req; + uint8_t start_block; + uint8_t end_block; + uint8_t data[]; +} PACKED iclass_restore_req_t; + + +// iclass / picopass chip config structures and shared routines +typedef struct { + uint8_t app_limit; //[8] + uint8_t otp[2]; //[9-10] + uint8_t block_writelock;//[11] + uint8_t chip_config; //[12] + uint8_t mem_config; //[13] + uint8_t eas; //[14] + uint8_t fuses; //[15] +} picopass_conf_block_t; + +// iCLASS secure mode memory mapping +typedef struct { + uint8_t csn[8]; + picopass_conf_block_t conf; + uint8_t epurse[8]; + uint8_t key_d[8]; + uint8_t key_c[8]; + uint8_t app_issuer_area[8]; +} picopass_hdr; + +// iCLASS non-secure mode memory mapping +typedef struct { + uint8_t csn[8]; + picopass_conf_block_t conf; + uint8_t app_issuer_area[8]; +} picopass_ns_hdr; // For the bootloader @@ -485,6 +540,7 @@ typedef struct { #define CMD_HF_LEGIC_INFO 0x03BC #define CMD_HF_LEGIC_ESET 0x03BD +// iCLASS / Picopass #define CMD_HF_ICLASS_READCHECK 0x038F #define CMD_HF_ICLASS_CLONE 0x0390 #define CMD_HF_ICLASS_DUMP 0x0391 @@ -497,6 +553,7 @@ typedef struct { #define CMD_HF_ICLASS_EML_MEMSET 0x0398 #define CMD_HF_ICLASS_AUTH 0x0399 #define CMD_HF_ICLASS_CHKKEYS 0x039A +#define CMD_HF_ICLASS_RESTORE 0x039B // For ISO1092 / FeliCa #define CMD_HF_FELICA_SIMULATE 0x03A0 @@ -602,7 +659,7 @@ typedef struct { #define FLAG_ICLASS_READER_INIT 0x01 #define FLAG_ICLASS_READER_CLEARTRACE 0x02 #define FLAG_ICLASS_READER_ONLY_ONCE 0x04 -#define FLAG_ICLASS_READER_CEDITKEY 0x08 +#define FLAG_ICLASS_READER_CREDITKEY 0x08 #define FLAG_ICLASS_READER_AIA 0x10 // iCLASS reader status flags