diff --git a/CHANGELOG.md b/CHANGELOG.md index b07ce95dc..bb7fbe7b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Add lf t55xx chk e option. Checks calculated password based on the EM4100 id from some white cloners forumla by paleopterix (@mwalker33) - Add lf t55xx sniff to allow extracting commands and passwords used be cloners. (@mwalker33) - Add options to `lf read`, `lf cmdread`, `lf sniff` for repeated acquisitions (@doegox) - Change options of `lf read` to match `lf cmdread`, this affects historical `d` and `s` options (@doegox) diff --git a/README.md b/README.md index 3155f8093..ac4ab1f6d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ | Releases | Linux & OSX CI | Windows CI | Coverity | Contributors | | ------------------- |:-------------------:| -------------------:| -------------------:| -------------------:| -| [![Latest release](https://img.shields.io/github/v/release/rfidresearchgroup/proxmark3)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://api.travis-ci.org/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.org/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | [![Coverity Status](https://scan.coverity.com/projects/19334/badge.svg)](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo)| ![GitHub contributors](https://img.shields.io/github/contributors/rfidresearchgroup/proxmark3) | +| [![Latest release](https://img.shields.io/github/v/release/rfidresearchgroup/proxmark3)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://api.travis-ci.com/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.com/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | [![Coverity Status](https://scan.coverity.com/projects/19334/badge.svg)](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo)| ![GitHub contributors](https://img.shields.io/github/contributors/rfidresearchgroup/proxmark3) | diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 0844b5e4d..e91fce41a 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1139,7 +1139,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t /* Sends an APDU to the tag * TODO: check CRC and preamble */ -uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response, uint16_t respmaxlen) { +int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response, uint16_t respmaxlen) { LED_A_ON(); uint8_t message_frame[message_length + 4]; @@ -1158,7 +1158,7 @@ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *r uint32_t eof_time = 0; CodeAndTransmit14443bAsReader(message_frame, sizeof(message_frame), &start_time, &eof_time); - // get response + // Get response? if (response == NULL) { LED_A_OFF(); return 0; @@ -1170,13 +1170,13 @@ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *r if (retlen < 3) { LED_A_OFF(); - return 0; + return -1; } // VALIDATE CRC if (!check_crc(CRC_14443_B, response, retlen)) { if (DBGLEVEL > DBG_DEBUG) DbpString("CRC fail"); - return 0; + return -2; } return retlen; @@ -1185,7 +1185,7 @@ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *r /** * SRx Initialise. */ -static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { +static int iso14443b_select_srx_card(iso14b_card_select_t *card) { // INITIATE command: wake up the tag using the INITIATE static const uint8_t init_srx[] = { ISO14443B_INITIATE, 0x00, 0x97, 0x5b }; uint8_t r_init[3] = {0x0}; @@ -1201,7 +1201,7 @@ static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { FpgaDisableTracing(); if (retlen <= 0) - return 2; + return -1; // Randomly generated Chip ID if (card) { @@ -1222,17 +1222,17 @@ static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { FpgaDisableTracing(); if (retlen != 3) { - return 2; + return -1; } // Check the CRC of the answer: if (!check_crc(CRC_14443_B, r_select, retlen)) { - return 3; + return -2; } // Check response from the tag: should be the same UID as the command we just sent: if (select_srx[1] != r_select[0]) { - return 1; + return -3; } // First get the tag's UID: @@ -1248,12 +1248,12 @@ static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { FpgaDisableTracing(); if (retlen != 10) { - return 2; + return -1; } // The check the CRC of the answer if (!check_crc(CRC_14443_B, r_papid, retlen)) { - return 3; + return -2; } if (card) { @@ -1437,11 +1437,11 @@ void ReadSTMemoryIso14443b(uint16_t numofblocks) { uint8_t *mem = BigBuf_malloc((numofblocks + 1) * 4); iso14b_card_select_t card; - uint8_t res = iso14443b_select_srx_card(&card); - + int res = iso14443b_select_srx_card(&card); int isOK = PM3_SUCCESS; + // 0: OK 2: attrib fail, 3:crc fail, - if (res > 0) { + if (res < 1) { isOK = PM3_ETIMEOUT; goto out; } diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index ac2c4f831..199e5c63f 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -27,10 +27,10 @@ #endif void iso14443b_setup(void); -uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response, uint16_t respmaxlen); +int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response, uint16_t respmaxlen); int iso14443b_select_card(iso14b_card_select_t *card); -uint8_t iso14443b_select_card_srx(iso14b_card_select_t *card); +int iso14443b_select_card_srx(iso14b_card_select_t *card); void SimulateIso14443bTag(uint32_t pupi); void AcquireRawAdcSamplesIso14443b(uint32_t parameter); diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index d0318737c..58c2c40bc 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -57,6 +57,9 @@ f1d83f964314 # RKF RejskortDanmark KeyB # 4b0b20107ccb # TNP3xxx # +# Access control system +605F5E5D5C5B +# # more Keys from mfc_default_keys.lua 000000000001 000000000002 @@ -1199,4 +1202,4 @@ FEE2A3FBC5B6 # toru ent # taurus avm # -005078565703 \ No newline at end of file +005078565703 diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index cc6a7caab..aa8a29255 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1638,6 +1638,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { bool isMifareDESFire = false; bool isMifarePlus = false; bool isMifareUltralight = false; + bool isST = false; int nxptype = MTNONE; if (card.uidlen <= 4) { @@ -1657,6 +1658,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0])); switch (card.uid[0]) { + case 0x02: // ST + isST = true; + break; case 0x04: // NXP nxptype = detect_nxp_card(card.sak, ((card.atqa[1] << 8) + card.atqa[0])); @@ -1679,9 +1683,13 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } else if ((card.uid[1] & 0xF0) == 0x70) { printTag("my-d(tm) move lean SLE 66R01L"); } + isMifareUltralight = true; + isMifareClassic = false; if (card.sak == 0x88) { printTag("Infineon MIFARE CLASSIC 1K"); + isMifareUltralight = false; + isMifareClassic = true; } getTagLabel(card.uid[0], card.uid[1]); break; @@ -2018,6 +2026,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (isMifareDESFire && isMagic == 0) PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfdes info`")); + if (isST) + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf st info`")); + DropField(); return select_status; } diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 811fcb195..26821f812 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh -// Modified 2018 iceman +// Modified 2018, 2020 iceman // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -32,7 +32,7 @@ static int usage_hf_14b_info(void) { PrintAndLogEx(NORMAL, " s silently"); PrintAndLogEx(NORMAL, "Example:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b info")); - return 0; + return PM3_SUCCESS; } static int usage_hf_14b_reader(void) { PrintAndLogEx(NORMAL, "Usage: hf 14b reader [h] [s]"); @@ -41,7 +41,7 @@ static int usage_hf_14b_reader(void) { PrintAndLogEx(NORMAL, " s silently"); PrintAndLogEx(NORMAL, "Example:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b reader")); - return 0; + return PM3_SUCCESS; } static int usage_hf_14b_raw(void) { PrintAndLogEx(NORMAL, "Usage: hf 14b raw [-h] [-r] [-c] [-p] [-s / -ss] [-t] <0A 0B 0C ... hex>"); @@ -55,7 +55,7 @@ static int usage_hf_14b_raw(void) { PrintAndLogEx(NORMAL, " -t timeout in ms"); PrintAndLogEx(NORMAL, "Example:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b raw -s -c -p 0200a40400")); - return 0; + return PM3_SUCCESS; } static int usage_hf_14b_sniff(void) { PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer."); @@ -65,7 +65,7 @@ static int usage_hf_14b_sniff(void) { PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, "Example:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sniff")); - return 0; + return PM3_SUCCESS; } static int usage_hf_14b_sim(void) { PrintAndLogEx(NORMAL, "Emulating ISO/IEC 14443 type B tag with 4 UID / PUPI"); @@ -76,7 +76,7 @@ static int usage_hf_14b_sim(void) { PrintAndLogEx(NORMAL, "Example:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sim")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sim u 11223344")); - return 0; + return PM3_SUCCESS; } static int usage_hf_14b_read_srx(void) { PrintAndLogEx(NORMAL, "Usage: hf 14b sriread [h] <1|2>"); @@ -86,45 +86,38 @@ static int usage_hf_14b_read_srx(void) { PrintAndLogEx(NORMAL, "Example:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriread 1")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriread 2")); - return 0; + return PM3_SUCCESS; } static int usage_hf_14b_write_srx(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14b [h] sriwrite <1|2> "); + PrintAndLogEx(NORMAL, "Usage: hf 14b [h] sriwrite <1|2> "); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " <1|2> 1 = SRIX4K , 2 = SRI512"); - PrintAndLogEx(NORMAL, " BLOCK number depends on tag, special block == FF"); + PrintAndLogEx(NORMAL, " (hex) block number depends on tag, special block == FF"); PrintAndLogEx(NORMAL, " hex bytes of data to be written"); PrintAndLogEx(NORMAL, "Example:"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 1 7F 11223344")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 1 FF 11223344")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 2 15 11223344")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 2 FF 11223344")); - return 0; + return PM3_SUCCESS; } static int usage_hf_14b_dump(void) { PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-14443-B tag and save it to file\n" + "If memory size defaults to SRI4K if auto detect fails.\n" "\n" - "Usage: hf 14b dump [h] [card memory] \n" + "Usage: hf 14b dump [h] \n" "Options:\n" "\th this help\n" - "\t[card memory] 1 = SRIX4K (default), 2 = SRI512\n" - "\tf filename, if no UID will be used as filename\n" + "\tf (optional) filename, if no UID will be used as filename\n" "\n" "Example:\n" - _YELLOW_("\thf 14b dump f\n") - _YELLOW_("\thf 14b dump 2 f mydump") + _YELLOW_("\thf 14b dump\n") + _YELLOW_("\thf 14b dump f mydump") ); - return 0; + return PM3_SUCCESS; } -/* -static void switch_on_field_14b(void) { - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT, 0, 0, NULL, 0); -} -*/ - static int switch_off_field_14b(void) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_DISCONNECT, 0, 0, NULL, 0); @@ -311,12 +304,14 @@ static bool get_14b_UID(iso14b_card_select_t *card) { if (card == NULL) return false; + int status = 0; + PacketResponseNG resp; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - uint8_t status = resp.oldarg[0]; + status = resp.oldarg[0]; if (status == 0) { memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); return true; @@ -328,7 +323,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - uint8_t status = resp.oldarg[0]; + status = resp.oldarg[0]; if (status == 0) { memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); return true; @@ -345,7 +340,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { // 4 = bit rate capacity // 5 = max frame size / -4 info // 6 = FWI / Coding options -static void print_atqb_resp(uint8_t *data, uint8_t cid) { +static int print_atqb_resp(uint8_t *data, uint8_t cid) { //PrintAndLogEx(SUCCESS, " UID: %s", sprint_hex(data+1,4)); PrintAndLogEx(SUCCESS, " App Data: %s", sprint_hex(data, 4)); PrintAndLogEx(SUCCESS, " Protocol: %s", sprint_hex(data + 4, 3)); @@ -387,7 +382,7 @@ static void print_atqb_resp(uint8_t *data, uint8_t cid) { PrintAndLogEx(SUCCESS, "Tag :"); PrintAndLogEx(SUCCESS, " Max Buf Length: %u (MBLI) %s", cid >> 4, (cid & 0xF0) ? "" : "chained frames not supported"); PrintAndLogEx(SUCCESS, " CID : %u", cid & 0x0f); - return; + return PM3_SUCCESS; } // get SRx chip model (from UID) // from ST Microelectronics @@ -425,66 +420,209 @@ static char *get_ST_Chip_Model(uint8_t data) { return retStr; } -// REMAKE: -/* -static int print_ST_Lock_info(uint8_t model) { +static char *get_st_lock_info(uint8_t model, uint8_t *lockbytes, uint8_t blk) { - PrintAndLogEx(NORMAL, "Chip Write Protection Bits:"); - // now interpret the data - switch (model){ - case 0x0: //fall through (SRIX4K special) - case 0x3: //fall through (SRIx4K) - case 0x7: // (SRI4K) - //only need data[3] - blk1 = 9; - PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+3, 1)); - PrintAndLogEx(NORMAL, " 07/08:%slocked", (data[3] & 1) ? " not " : " " ); - for (uint8_t i = 1; i<8; i++){ - PrintAndLogEx(NORMAL, " %02u:%slocked", blk1, (data[3] & (1 << i)) ? " not " : " " ); - blk1++; + static char str[16]; + char *s = str; + sprintf(s, " "); + + if (blk > 15) { + return s; } - break; - case 0x4: //fall through (SRIX512) - case 0x6: //fall through (SRI512) - case 0xC: // (SRT512) - //need data[2] and data[3] - blk1 = 0; - PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+2, 2)); - for (uint8_t b=2; b<4; b++){ - for (uint8_t i=0; i<8; i++){ - PrintAndLogEx(NORMAL, " %02u:%slocked", blk1, (data[b] & (1 << i)) ? " not " : " " ); - blk1++; + + uint8_t mask = 0; + switch (model) { + case 0x0: // SRIX4K special + case 0x3: // SRIx4K + case 0x7: { // SRI4K + //only need data[3] + switch(blk) { + case 7: + case 8: + mask = 0x01; + break; + case 9: + mask = 0x02; + break; + case 10: + mask = 0x04; + break; + case 11: + mask = 0x08; + break; + case 12: + mask = 0x10; + break; + case 13: + mask = 0x20; + break; + case 14: + mask = 0x40; + break; + case 15: + mask = 0x80; + break; + default: + return s; + } + if ((lockbytes[1] & mask) == 0) { + sprintf(s, _RED_("1")); + } + return s; + } + case 0x4: // SRIX512 + case 0x6: // SRI512 + case 0xC: { // SRT512 + //need data[2] and data[3] + uint8_t b = 1; + switch(blk) { + case 0: + mask = 0x01; + break; + case 1: + mask = 0x02; + break; + case 2: + mask = 0x04; + break; + case 3: + mask = 0x08; + break; + case 4: + mask = 0x10; + break; + case 5: + mask = 0x20; + break; + case 6: + mask = 0x40; + break; + case 7: + mask = 0x80; + break; + case 8: + mask = 0x01; + b = 0; + break; + case 9: + mask = 0x02; + b = 0; + break; + case 10: + mask = 0x04; + b = 0; + break; + case 11: + mask = 0x08; + b = 0; + break; + case 12: + mask = 0x10; + b = 0; + break; + case 13: + mask = 0x20; + b = 0; + break; + case 14: + mask = 0x40; + b = 0; + break; + case 15: + mask = 0x80; + b = 0; + break; + } + if ((lockbytes[b] & mask) == 0) { + sprintf(s, _RED_("1")); + } + return s; + } + case 0x2: { // SR176 + //need data[2] + switch(blk) { + case 0: + case 1: + mask = 0x1; + break; + case 2: + case 3: + mask = 0x2; + break; + case 4: + case 5: + mask = 0x4; + break; + case 6: + case 7: + mask = 0x8; + break; + case 8: + case 9: + mask = 0x10; + break; + case 10: + case 11: + mask = 0x20; + break; + case 12: + case 13: + mask = 0x40; + break; + case 14: + case 15: + mask = 0x80; + break; + } + // iceman: this is opposite! need sample to test with. + if ((lockbytes[0] & mask)) { + sprintf(s, _RED_("1")); + } + return s; + } + default: + break; } - } - break; - case 0x2: // (SR176) - //need data[2] - blk1 = 0; - PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+2, 1)); - for (uint8_t i = 0; i<8; i++){ - PrintAndLogEx(NORMAL, " %02u/%02u:%slocked", blk1, blk1+1, (data[2] & (1 << i)) ? " " : " not " ); - blk1+=2; - } - break; - default: - return rawClose(); - } - return 1; + return s; } -*/ + +static uint8_t get_st_chipid(uint8_t *uid) { + return uid[5] >> 2; +} + +static uint8_t get_st_cardsize(uint8_t *uid) { + uint8_t chipid = get_st_chipid(uid); + switch(chipid) { + case 0x0: + case 0x3: + case 0x7: + return 1; + case 0x4: + case 0x6: + case 0xC: + return 2; + default: + return 0; + } + return 0; +} + // print UID info from SRx chips (ST Microelectronics) static void print_st_general_info(uint8_t *data, uint8_t len) { //uid = first 8 bytes in data + uint8_t mfgid = data[6]; + uint8_t chipid = get_st_chipid(data); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(SwapEndian64(data, 8, 8), len)); - PrintAndLogEx(SUCCESS, " MFG: %02X, " _YELLOW_("%s"), data[6], getTagInfo(data[6])); - PrintAndLogEx(SUCCESS, "Chip: %02X, " _YELLOW_("%s"), data[5] >> 2, get_ST_Chip_Model(data[5] >> 2)); + PrintAndLogEx(SUCCESS, " MFG: %02X, " _YELLOW_("%s"), mfgid, getTagInfo(mfgid)); + PrintAndLogEx(SUCCESS, "Chip: %02X, " _YELLOW_("%s"), chipid, get_ST_Chip_Model(chipid)); } -//05 00 00 = find one tag in field -//1d xx xx xx xx 00 08 01 00 = attrib xx=UID (resp 10 [f9 e0]) -//a3 = ? (resp 03 [e2 c2]) -//02 = ? (resp 02 [6a d3]) +// iceman, some 14B APDU break down +// 05 00 00 = find one tag in field +// 1d xx xx xx xx 00 08 01 00 = attrib xx=UID (resp 10 [f9 e0]) +// a3 = ? (resp 03 [e2 c2]) +// 02 = ? (resp 02 [6a d3]) // 022b (resp 02 67 00 [29 5b]) // 0200a40400 (resp 02 67 00 [29 5b]) // 0200a4040c07a0000002480300 (resp 02 67 00 [29 5b]) @@ -494,31 +632,31 @@ static void print_st_general_info(uint8_t *data, uint8_t len) { // 0200a404000cd2760001354b414e4d30310000 (resp 02 6a 82 [4b 4c]) // 0200a404000ca000000063504b43532d313500 (resp 02 6a 82 [4b 4c]) // 0200a4040010a000000018300301000000000000000000 (resp 02 6a 82 [4b 4c]) -//03 = ? (resp 03 [e3 c2]) -//c2 = ? (resp c2 [66 15]) -//b2 = ? (resp a3 [e9 67]) -//a2 = ? (resp 02 [6a d3]) +// 03 = ? (resp 03 [e3 c2]) +// c2 = ? (resp c2 [66 15]) +// b2 = ? (resp a3 [e9 67]) +// a2 = ? (resp 02 [6a d3]) // 14b get and print Full Info (as much as we know) static bool HF14B_Std_Info(bool verbose) { - bool isSuccess = false; + bool is_success = false; // 14b get and print UID only (general info) clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); switch_off_field_14b(); - return false; + return is_success; } iso14b_card_select_t card; memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - uint64_t status = resp.oldarg[0]; + int status = resp.oldarg[0]; switch (status) { case 0: @@ -527,12 +665,12 @@ static bool HF14B_Std_Info(bool verbose) { PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); print_atqb_resp(card.atqb, card.cid); - isSuccess = true; + is_success = true; break; - case 2: + case -1: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); break; - case 3: + case -2: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); break; default: @@ -540,15 +678,14 @@ static bool HF14B_Std_Info(bool verbose) { break; } - return isSuccess; + return is_success; } // SRx get and print full info (needs more info...) static bool HF14B_ST_Info(bool verbose) { - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); @@ -558,33 +695,11 @@ static bool HF14B_ST_Info(bool verbose) { iso14b_card_select_t card; memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - uint64_t status = resp.oldarg[0]; - if (status > 0) + int status = resp.oldarg[0]; + if (status < 0) return false; print_st_general_info(card.uid, card.uidlen); - - //add locking bit information here. uint8_t data[16] = {0x00}; - // uint8_t datalen = 2; - // uint8_t resplen; - // uint8_t blk1; - // data[0] = 0x08; - - // - // if (model == 0x2) { //SR176 has special command: - // data[1] = 0xf; - // resplen = 4; - // } else { - // data[1] = 0xff; - // resplen = 6; - // } - - // //std read cmd - // if (HF14BCmdRaw(true, true, data, &datalen, false)==0) - // return rawClose(); - - // if (datalen != resplen || !crc) return rawClose(); - //print_ST_Lock_info(data[5]>>2); return true; } @@ -599,52 +714,53 @@ static int CmdHF14Binfo(const char *Cmd) { static bool HF14B_ST_Reader(bool verbose) { - bool isSuccess = false; + bool is_success = false; // SRx get and print general info about SRx chip from UID clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); - return false; + return is_success; } iso14b_card_select_t card; memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - uint64_t status = resp.oldarg[0]; + int status = resp.oldarg[0]; switch (status) { case 0: print_st_general_info(card.uid, card.uidlen); - isSuccess = true; + is_success = true; break; - case 1: - if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 random chip id fail"); - break; - case 2: + case -1: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); break; - case 3: + case -2: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); break; + case -3: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 random chip id fail"); + break; default: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select SRx failed"); break; } - return isSuccess; + return is_success; } static bool HF14B_Std_Reader(bool verbose) { - bool isSuccess = false; + bool is_success = false; // 14b get and print UID only (general info) clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); return false; @@ -653,7 +769,7 @@ static bool HF14B_Std_Reader(bool verbose) { iso14b_card_select_t card; memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - uint64_t status = resp.oldarg[0]; + int status = resp.oldarg[0]; switch (status) { case 0: @@ -662,72 +778,104 @@ static bool HF14B_Std_Reader(bool verbose) { PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); print_atqb_resp(card.atqb, card.cid); - isSuccess = true; + is_success = true; break; - case 2: + case -1: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); break; - case 3: + case -2: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); break; default: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select failed"); break; } - return isSuccess; + return is_success; } // test for other 14b type tags (mimic another reader - don't have tags to identify) -static bool HF14B_Other_Reader(void) { +static bool HF14B_Other_Reader(bool verbose) { - // uint8_t data[] = {0x00, 0x0b, 0x3f, 0x80}; - // uint8_t datalen = 4; + uint8_t data[] = {0x00, 0x0b, 0x3f, 0x80}; + uint8_t datalen = 4; - // // 14b get and print UID only (general info) - // uint32_t flags = ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_RAW | ISO14B_APPEND_CRC; + // 14b get and print UID only (general info) + uint32_t flags = ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_RAW | ISO14B_APPEND_CRC; - // clearCommandBuffer(); - // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, datalen, 0, data, datalen); - // PacketResponseNG resp; - // WaitForResponse(CMD_HF_ISO14443B_COMMAND,&resp); + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, datalen, 0, data, datalen); - // if (datalen > 2 ) { - // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); - // PrintAndLogEx(NORMAL, "unknown tag type answered to a 0x000b3f80 command ans:"); - // //PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); - // rawclose(); - // return true; - // } + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + switch_off_field_14b(); + return false; + } + int status = resp.oldarg[0]; + PrintAndLogEx(DEBUG, "status %d", status); - // data[0] = ISO14443B_AUTHENTICATE; - // clearCommandBuffer(); - // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); - // PacketResponseNG resp; - // WaitForResponse(CMD_HF_ISO14443B_COMMAND, &resp); + if (status == 0) { + PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); + PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x000b3f80 command ans:"); + switch_off_field_14b(); + return true; + } else if (status > 0) { + PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); + PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x000b3f80 command ans:"); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, status)); + switch_off_field_14b(); + return true; + } - // if (datalen > 0) { - // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); - // PrintAndLogEx(NORMAL, "Unknown tag type answered to a 0x0A command ans:"); - // // PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); - // rawClose(); - // return true; - // } + data[0] = ISO14443B_AUTHENTICATE; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + switch_off_field_14b(); + return false; + } + status = resp.oldarg[0]; + PrintAndLogEx(DEBUG, "status %d", status); - // data[0] = ISO14443B_RESET; - // clearCommandBuffer(); - // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); - // PacketResponseNG resp; - // WaitForResponse(CMD_HF_ISO14443B_COMMAND, &resp); + if (status == 0) { + PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); + PrintAndLogEx(SUCCESS, "Unknown tag type answered to a 0x0A command ans:"); + switch_off_field_14b(); + return true; + } else if (status > 0) { + PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); + PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x0A command ans:"); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, status)); + switch_off_field_14b(); + return true; + } - // if (datalen > 0) { - // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); - // PrintAndLogEx(NORMAL, "Unknown tag type answered to a 0x0C command ans:"); - // PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); - // rawClose(); - // return true; - // } + data[0] = ISO14443B_RESET; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + switch_off_field_14b(); + return false; + } + status = resp.oldarg[0]; + PrintAndLogEx(DEBUG, "status %d", status); - // rawClose(); + if (status == 0) { + PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); + PrintAndLogEx(SUCCESS, "Unknown tag type answered to a 0x0C command ans:"); + switch_off_field_14b(); + return true; + } else if (status > 0) { + PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); + PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x0C command ans:"); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, status)); + switch_off_field_14b(); + return true; + } + + switch_off_field_14b(); return false; } @@ -832,10 +980,6 @@ static int CmdHF14BDump(const char *Cmd) { bool errors = false; uint8_t cmdp = 0, cardtype = 1; uint16_t cardsize = 0; - uint8_t blocks = 0; - iso14b_card_select_t card; - - if (strlen(Cmd) < 1) return usage_hf_14b_dump(); while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { @@ -846,20 +990,27 @@ static int CmdHF14BDump(const char *Cmd) { cmdp += 2; break; default: - if (cmdp == 0) { - cardtype = param_get8ex(Cmd, cmdp, 1, 10); - cmdp++; - } else { - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; } } //Validations if (errors) return usage_hf_14b_dump(); + iso14b_card_select_t card; + if (get_14b_UID(&card) == false) { + PrintAndLogEx(WARNING, "No tag found."); + return PM3_SUCCESS; + } + + // detect cardsize + // 1 = 4096 + // 2 = 512 + cardtype = get_st_cardsize(card.uid); + + uint8_t blocks = 0; switch (cardtype) { case 2: cardsize = (512 / 8) + 4; @@ -872,25 +1023,22 @@ static int CmdHF14BDump(const char *Cmd) { break; } - if (get_14b_UID(&card) == false) { - PrintAndLogEx(WARNING, "No tag found."); - return PM3_SUCCESS; - } - if (fileNameLen < 1) { PrintAndLogEx(INFO, "Using UID as filename"); fptr += sprintf(fptr, "hf-14b-"); - FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); + FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); } - // detect blocksize from card :) - PrintAndLogEx(NORMAL, "Reading memory from tag UID %s", sprint_hex(card.uid, card.uidlen)); + uint8_t chipid = get_st_chipid(card.uid); + PrintAndLogEx(SUCCESS, "Found a " _GREEN_("%s") " tag", get_ST_Chip_Model(chipid)); + // detect blocksize from card :) + PrintAndLogEx(INFO, "Reading memory from tag UID " _GREEN_("%s"), sprint_hex_inrow(SwapEndian64(card.uid, card.uidlen, 8), card.uidlen)); + uint8_t data[cardsize]; memset(data, 0, sizeof(data)); - - int blocknum = 0; uint8_t *recv = NULL; + int status = 0; PacketResponseNG resp; clearCommandBuffer(); @@ -898,14 +1046,17 @@ static int CmdHF14BDump(const char *Cmd) { //select if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - if (resp.oldarg[0]) { - PrintAndLogEx(INFO, "failed to select %" PRId64 " | %" PRId64, resp.oldarg[0], resp.oldarg[1]); + status = resp.oldarg[0]; + if (status < 0) { + PrintAndLogEx(FAILED, "failed to select arg0[%" PRId64 "] arg1 [%" PRId64 "]", resp.oldarg[0], resp.oldarg[1]); goto out; } } - uint8_t req[2] = {ISO14443B_READ_BLK}; + PrintAndLogEx(INFO, "." NOLF); + uint8_t req[2] = {ISO14443B_READ_BLK}; + int blocknum = 0; for (int retry = 0; retry < 5; retry++) { req[1] = blocknum; @@ -915,8 +1066,9 @@ static int CmdHF14BDump(const char *Cmd) { if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - uint8_t status = resp.oldarg[0] & 0xFF; - if (status > 0) { + status = resp.oldarg[0]; + if (status < 0) { + PrintAndLogEx(FAILED, "retrying one more time"); continue; } @@ -930,8 +1082,8 @@ static int CmdHF14BDump(const char *Cmd) { memcpy(data + (blocknum * 4), resp.data.asBytes, 4); + // last read. if (blocknum == 0xFF) { - //last read. break; } @@ -943,31 +1095,46 @@ static int CmdHF14BDump(const char *Cmd) { } PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); } } PrintAndLogEx(NORMAL, ""); if (blocknum != 0xFF) { - PrintAndLogEx(NORMAL, "Dump failed"); + PrintAndLogEx(FAILED, "Dump failed"); goto out; } - PrintAndLogEx(NORMAL, "block# | data | ascii"); - PrintAndLogEx(NORMAL, "---------+--------------+----------"); + PrintAndLogEx(DEBUG, "systemblock : %s", sprint_hex(data + (blocknum * 4), 4)); + PrintAndLogEx(DEBUG, " otp lock : %02x %02x", data[(blocknum * 4)], data[(blocknum * 4) + 1]); + + + PrintAndLogEx(INFO, " block# | data |lck| ascii"); + PrintAndLogEx(INFO, "---------+--------------+---+----------"); for (int i = 0; i <= blocks; i++) { - PrintAndLogEx(NORMAL, - "%3d/0x%02X | %s | %s", + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s | %s", i, i, sprint_hex(data + (i * 4), 4), + get_st_lock_info(chipid, data + (blocknum * 4), i), sprint_ascii(data + (i * 4), 4) ); } - + + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s | %s", + 0xFF, + 0xFF, + sprint_hex(data + (0xFF * 4), 4), + get_st_lock_info(chipid, data + (blocknum * 4), 0xFF), + sprint_ascii(data + (0xFF * 4), 4) + ); + PrintAndLogEx(INFO, "---------+--------------+---+----------"); PrintAndLogEx(NORMAL, ""); - + // save to file size_t datalen = (blocks + 1) * 4; saveFileEML(filename, data, datalen, 4); saveFile(filename, ".bin", data, datalen); @@ -1112,10 +1279,12 @@ int CmdHF14B(const char *Cmd) { int infoHF14B(bool verbose) { // try std 14b (atqb) - if (HF14B_Std_Info(verbose)) return 1; + if (HF14B_Std_Info(verbose)) + return 1; // try ST 14b - if (HF14B_ST_Info(verbose)) return 1; + if (HF14B_ST_Info(verbose)) + return 1; // try unknown 14b read commands (to be identified later) // could be read of calypso, CEPAS, moneo, or pico pass. @@ -1127,14 +1296,17 @@ int infoHF14B(bool verbose) { int readHF14B(bool verbose) { // try std 14b (atqb) - if (HF14B_Std_Reader(verbose)) return 1; + if (HF14B_Std_Reader(verbose)) + return 1; // try ST Microelectronics 14b - if (HF14B_ST_Reader(verbose)) return 1; + if (HF14B_ST_Reader(verbose)) + return 1; // try unknown 14b read commands (to be identified later) // could be read of calypso, CEPAS, moneo, or pico pass. - if (HF14B_Other_Reader()) return 1; + if (HF14B_Other_Reader(verbose)) + return 1; if (verbose) PrintAndLogEx(FAILED, "no 14443-B tag found"); return 0; diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 5517ad15f..3b36e033d 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -321,6 +321,7 @@ typedef enum { DESFIRE_EV3, DESFIRE_LIGHT, PLUS_EV1, + NTAG413DNA, } nxp_cardtype_t; typedef struct { @@ -347,9 +348,9 @@ static char *getCardSizeStr(uint8_t fsize) { // is LSB set? if (fsize & 1) - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("%d - %d bytes") ")", fsize, usize, lsize); + snprintf(retStr, sizeof(buf), "0x%02X (" _GREEN_("%d - %d bytes") ")", fsize, usize, lsize); else - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("%d bytes") ")", fsize, lsize); + snprintf(retStr, sizeof(buf), "0x%02X (" _GREEN_("%d bytes") ")", fsize, lsize); return buf; } @@ -377,18 +378,22 @@ static char *getVersionStr(uint8_t major, uint8_t minor) { char *retStr = buf; if (major == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _YELLOW_("DESFire MF3ICD40") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire MF3ICD40") ")", major, minor); else if (major == 0x01 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _YELLOW_("DESFire EV1") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV1") ")", major, minor); else if (major == 0x12 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _YELLOW_("DESFire EV2") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV2") ")", major, minor); else if (major == 0x33 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _YELLOW_("DESFire EV3") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV3") ")", major, minor); else if (major == 0x30 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _YELLOW_("DESFire Light") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire Light") ")", major, minor); + else if (major == 0x10 && minor == 0x00) + snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("NTAG413DNA") ")", major, minor); else snprintf(retStr, sizeof(buf), "%x.%x (" _YELLOW_("Unknown") ")", major, minor); return buf; + +//04 01 01 01 00 1A 05 } static int DESFIRESendApdu(bool activate_field, bool leavefield_on, sAPDU apdu, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint16_t *sw) { @@ -648,9 +653,10 @@ static nxp_cardtype_t getCardType(uint8_t major, uint8_t minor) { return DESFIRE_EV3; if (major == 0x30 && minor == 0x00) return DESFIRE_LIGHT; - if (major == 0x11 && minor == 0x00) + if (major == 0x11 && minor == 0x00) return PLUS_EV1; - + if (major == 0x10 && minor == 0x00) + return NTAG413DNA; return DESFIRE_UNKNOWN; } @@ -3318,7 +3324,10 @@ static int CmdHF14ADesInfo(const char *Cmd) { if (major == 0 && minor == 2) PrintAndLogEx(INFO, "\t0.2 - DESFire Light, Originality check, "); - if (cardtype == DESFIRE_EV2 || cardtype == DESFIRE_LIGHT || cardtype == DESFIRE_EV3) { + if (cardtype == DESFIRE_EV2 || + cardtype == DESFIRE_LIGHT || + cardtype == DESFIRE_EV3 || + cardtype == NTAG413DNA) { // Signature originality check uint8_t signature[56] = {0}; size_t signature_len = 0; diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 0fcbe1796..28b560991 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -272,14 +272,16 @@ uint32_t UL_TYPES_ARRAY[] = { UNKNOWN, UL, UL_C, UL_EV1_48, UL_EV1_128, NTAG, NTAG_203, NTAG_210, NTAG_212, NTAG_213, NTAG_215, NTAG_216, MY_D, MY_D_NFC, MY_D_MOVE, MY_D_MOVE_NFC, MY_D_MOVE_LEAN, FUDAN_UL, - UL_EV1, NTAG_213_F, NTAG_216_F, UL_NANO_40, NTAG_I2C_1K, NTAG_213_TT + UL_EV1, NTAG_213_F, NTAG_216_F, UL_NANO_40, NTAG_I2C_1K, NTAG_213_TT, + NTAG_213_C }; uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_ULEV1a_BLOCKS, MAX_ULEV1b_BLOCKS, MAX_NTAG_203, MAX_NTAG_203, MAX_NTAG_210, MAX_NTAG_212, MAX_NTAG_213, MAX_NTAG_215, MAX_NTAG_216, MAX_UL_BLOCKS, MAX_MY_D_NFC, MAX_MY_D_MOVE, MAX_MY_D_MOVE, MAX_MY_D_MOVE_LEAN, MAX_UL_BLOCKS, - MAX_ULEV1a_BLOCKS, MAX_NTAG_213, MAX_NTAG_216, MAX_UL_NANO_40, MAX_NTAG_I2C_1K, MAX_NTAG_213 + MAX_ULEV1a_BLOCKS, MAX_NTAG_213, MAX_NTAG_216, MAX_UL_NANO_40, MAX_NTAG_I2C_1K, MAX_NTAG_213, + MAX_NTAG_213 }; //------------------------------------ @@ -685,6 +687,8 @@ int ul_print_type(uint32_t tagtype, uint8_t spaces) { PrintAndLogEx(SUCCESS, "%*sTYPE: " _YELLOW_("NTAG 213 144bytes (NT2H1311G0DU)"), spaces, ""); else if (tagtype & NTAG_213_F) PrintAndLogEx(SUCCESS, "%*sTYPE: " _YELLOW_("NTAG 213F 144bytes (NT2H1311F0DTL)"), spaces, ""); + else if (tagtype & NTAG_213_C) + PrintAndLogEx(SUCCESS, "%*sTYPE: " _YELLOW_("NTAG 213C 144bytes (NT2H1311C1DTL)"), spaces, ""); else if (tagtype & NTAG_213_TT) PrintAndLogEx(SUCCESS, "%*sTYPE: " _YELLOW_("NTAG 213TT 144bytes (NT2H1311TTDU)"), spaces, ""); else if (tagtype & NTAG_215) @@ -1112,6 +1116,7 @@ uint32_t GetHF14AMfU_Type(void) { else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0B", 7) == 0) { tagtype = NTAG_210; break; } else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0E", 7) == 0) { tagtype = NTAG_212; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x01\x01\x0F", 7) == 0) { tagtype = NTAG_213_C; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x11", 7) == 0) { tagtype = NTAG_215; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = NTAG_216; break; } else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213_F; break; } @@ -1338,7 +1343,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // NTAG counters? - if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_TT | NTAG_215 | NTAG_216))) { + if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216))) { if (ntag_print_counter()) { // failed - re-select if (ul_auth_select(&card, tagtype, hasAuthKey, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) return PM3_ESOFT; @@ -1346,7 +1351,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // Read signature - if ((tagtype & (UL_EV1_48 | UL_EV1_128 | UL_EV1 | UL_NANO_40 | NTAG_213 | NTAG_213_F | NTAG_213_TT | NTAG_215 | NTAG_216 | NTAG_216_F | NTAG_I2C_1K | NTAG_I2C_2K | NTAG_I2C_1K_PLUS | NTAG_I2C_2K_PLUS))) { + if ((tagtype & (UL_EV1_48 | UL_EV1_128 | UL_EV1 | UL_NANO_40 | NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216 | NTAG_216_F | NTAG_I2C_1K | NTAG_I2C_2K | NTAG_I2C_1K_PLUS | NTAG_I2C_2K_PLUS))) { uint8_t ulev1_signature[32] = {0x00}; status = ulev1_readSignature(ulev1_signature, sizeof(ulev1_signature)); if (status == -1) { @@ -1461,12 +1466,20 @@ static int CmdHF14AMfUInfo(const char *Cmd) { if (ul_auth_select(&card, tagtype, hasAuthKey, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) return PM3_ESOFT; } } - if (len < 1) PrintAndLogEx(WARNING, _YELLOW_("password not known")); + if (len < 1) { + PrintAndLogEx(WARNING, _YELLOW_("password not known")); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen r`") " to get see known pwd gen algo suggestions"); + } + } else { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen r`") " to get see known pwd gen algo suggestions"); } } out: DropField(); - if (locked) PrintAndLogEx(FAILED, "\nTag appears to be locked, try using the key to get more info"); + if (locked) { + PrintAndLogEx(INFO, "\nTag appears to be locked, try using the key to get more info"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen r`") " to get see known pwd gen algo suggestions"); + } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2032,7 +2045,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { uint8_t n = 0; // NTAG has 1 counter, at 0x02 - if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_TT | NTAG_215 | NTAG_216))) { + if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216))) { n = 2; } diff --git a/client/src/cmdhfmfu.h b/client/src/cmdhfmfu.h index 210783874..d59389434 100644 --- a/client/src/cmdhfmfu.h +++ b/client/src/cmdhfmfu.h @@ -58,6 +58,7 @@ typedef enum TAGTYPE_UL { UL_EV1 = 0x1000000, UL_NANO_40 = 0x2000000, NTAG_213_TT = 0x4000000, + NTAG_213_C = 0x8000000, UL_MAGIC = UL | MAGIC, UL_C_MAGIC = UL_C | MAGIC, UL_ERROR = 0xFFFFFF, diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 4c93210eb..7c24bb342 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -715,18 +715,20 @@ void pm3_version(bool verbose, bool oneliner) { PrintAndLogEx(NORMAL, "%s", temp); PrintAndLogEx(NORMAL, " compiled with " PM3CLIENTCOMPILER __VERSION__ PM3HOSTOS PM3HOSTARCH); - if (IfPm3Flash() == false && IfPm3Smartcard() == false && IfPm3FpcUsartHost() == false) { - PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("PROXMARK3") " ]"); - } else { - PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("PROXMARK3 RDV4") " ]"); - PrintAndLogEx(NORMAL, " external flash: %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent")); - PrintAndLogEx(NORMAL, " smartcard reader: %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent")); - PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("PROXMARK3 RDV4 Extras") " ]"); - PrintAndLogEx(NORMAL, " FPC USART for BT add-on support: %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent")); - - if (IfPm3FpcUsartDevFromUsb()) { - PrintAndLogEx(NORMAL, " FPC USART for developer support: %s", _GREEN_("present")); + PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("PROXMARK3") " ]"); + if (IfPm3Rdv4Fw() == false ){ + PrintAndLogEx(NORMAL, " firmware.........................%s", _GREEN_("PM3OTHER")); + if (IfPm3FpcUsartHost()) { + PrintAndLogEx(NORMAL, " FPC USART for BT add-on..........%s", _GREEN_("present")); } + } else { + PrintAndLogEx(NORMAL, " firmware.........................%s", _GREEN_("PM3RDV4")); + PrintAndLogEx(NORMAL, " external flash...................%s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent")); + PrintAndLogEx(NORMAL, " smartcard reader.................%s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent")); + PrintAndLogEx(NORMAL, " FPC USART for BT add-on..........%s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent")); + } + if (IfPm3FpcUsartDevFromUsb()) { + PrintAndLogEx(NORMAL, " FPC USART for developer..........%s", _GREEN_("present")); } PrintAndLogEx(NORMAL, ""); diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 92c78dfa6..8d6c3b8e3 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -29,6 +29,7 @@ #include "fileutils.h" // loadDictionary #include "util_posix.h" #include "cmdlf.h" // for lf sniff +#include "generator.h" // Some defines for readability #define T55XX_DLMODE_FIXED 0 // Default Mode @@ -254,16 +255,18 @@ static int usage_t55xx_chk(void) { PrintAndLogEx(NORMAL, "press " _YELLOW_("'enter'") " to cancel the command"); PrintAndLogEx(NORMAL, _RED_("WARNING:") " this may brick non-password protected chips!"); PrintAndLogEx(NORMAL, "Try to reading block 7 before\n"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx chk [h] [m] [r ] [f <*.dic>]"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx chk [h] [m] [r ] [f <*.dic>] [e ]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - this help"); PrintAndLogEx(NORMAL, " m - use dictionary from flashmemory\n"); print_usage_t55xx_downloadlink(T55XX_DLMODE_ALL, T55XX_DLMODE_ALL); PrintAndLogEx(NORMAL, " f <*.dic> - loads a default keys dictionary file <*.dic>"); + PrintAndLogEx(NORMAL, " e - will try the calculated password from some cloners based on EM4100 ID"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx chk m")); PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx chk f t55xx_default_pwds")); + PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx chk e aa11223344")); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -3003,6 +3006,9 @@ static int CmdT55xxChkPwds(const char *Cmd) { int dl_mode; // to try each downlink mode for each password uint8_t cmdp = 0; bool errors = false; + bool useCardPassword = false; + uint32_t cardPassword = 0x00; + uint64_t cardID = 0x00; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { @@ -3028,6 +3034,14 @@ static int CmdT55xxChkPwds(const char *Cmd) { use_pwd_file = true; cmdp += 2; break; + case 'e': + // White cloner password based on EM4100 ID + useCardPassword = true; + cardID = param_get64ex(Cmd,cmdp + 1,0,16); + uint32_t card32Bit = cardID & 0xFFFFFFFF; + cardPassword = lf_t55xx_white_pwdgen (card32Bit); + cmdp += 2; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -3090,7 +3104,28 @@ static int CmdT55xxChkPwds(const char *Cmd) { goto out; } - if (use_pwd_file) { + // try calculated password + if (useCardPassword) { + + PrintAndLogEx(INFO, "Testing %08"PRIX32" generated ", cardPassword); + for (dl_mode = downlink_mode; dl_mode <= 3; dl_mode++) { + + if (!AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, cardPassword, dl_mode)) { + continue; + } + + found = tryDetectModulationEx(dl_mode, T55XX_PrintConfig, 0, cardPassword); + if (found) { + PrintAndLogEx(SUCCESS, "Found valid password : [ " _GREEN_("%08"PRIX32) " ]", cardPassword); + dl_mode = 4; // Exit other downlink mode checks + } + + if (!try_all_dl_modes) // Exit loop if not trying all downlink modes + dl_mode = 4; + } + } + + if ((!found) && (use_pwd_file)) { uint32_t keycount = 0; int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount); @@ -3135,9 +3170,10 @@ static int CmdT55xxChkPwds(const char *Cmd) { dl_mode = 4; } } - if (!found) PrintAndLogEx(WARNING, "Check pwd failed"); } + if (!found) PrintAndLogEx(WARNING, "Check pwd failed"); + free(keyBlock); out: diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index 93bba22bf..590dee87d 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -26,6 +26,12 @@ bool IfPm3Present(void) { return session.pm3_present; } +bool IfPm3Rdv4Fw(void) { + if (!IfPm3Present()) + return false; + return (pm3_capabilities.compiled_with_flash) || (pm3_capabilities.compiled_with_smartcard); +} + bool IfPm3Flash(void) { if (!IfPm3Present()) return false; diff --git a/client/src/cmdparser.h b/client/src/cmdparser.h index baade0bcb..4e1e37a77 100644 --- a/client/src/cmdparser.h +++ b/client/src/cmdparser.h @@ -24,6 +24,7 @@ typedef struct command_s { // helpers for command_t IsAvailable bool AlwaysAvailable(void); bool IfPm3Present(void); +bool IfPm3Rdv4Fw(void); bool IfPm3Flash(void); bool IfPm3Smartcard(void); bool IfPm3FpcUsart(void); diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 1358137bf..1b074d9fe 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -34,8 +34,8 @@ #include "flash.h" #include "preferences.h" -#define BANNERMSG1 "" -#define BANNERMSG2 " :snowflake: bleeding edge :coffee:" +#define BANNERMSG1 " Iceman :coffee:" +#define BANNERMSG2 " :snowflake: bleeding edge" #define BANNERMSG3 " https://github.com/rfidresearchgroup/proxmark3/" typedef enum LogoMode { UTF8, ANSI, ASCII } LogoMode; diff --git a/common/generator.c b/common/generator.c index 37a1aa4f7..d0644c61a 100644 --- a/common/generator.c +++ b/common/generator.c @@ -479,7 +479,7 @@ int generator_selftest(void) { uint32_t lf_id = lf_t55xx_white_pwdgen(0x00000080); - success = (lf_id = 0x00018383); + success = (lf_id == 0x00018383); if (success) testresult++; PrintAndLogEx(success ? SUCCESS : WARNING, "ID | 0x00000080 | %08"PRIx32 " - %s", lf_id, success ? "OK" : "->00018383<--"); diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 9543bf5d2..ee796b409 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -227,6 +227,7 @@ while true; do if ! CheckExecute "findbits test" "tools/findbits.py 73 0110010101110011" "Match at bit 9: 011001010"; then break; fi if ! CheckExecute "findbits_test test" "tools/findbits_test.py 2>&1" "OK"; then break; fi if ! CheckExecute "pm3_eml_mfd test" "tools/pm3_eml_mfd_test.py 2>&1" "OK"; then break; fi + if ! CheckExecute "recover_pk test" "tools/recover_pk.py selftests 2>&1" "Tests:.*\[OK\]"; then break; fi fi if $TESTALL || $TESTBOOTROM; then echo -e "\n${C_BLUE}Testing bootrom:${C_NC}" diff --git a/tools/recover_pk.py b/tools/recover_pk.py index b2ff2e6c7..465352498 100755 --- a/tools/recover_pk.py +++ b/tools/recover_pk.py @@ -1,26 +1,318 @@ #!/usr/bin/env python3 +# MIT License +# Copyright (c) 2020 @doegox -# @doegox -- 2020 - -import sslcrypto import binascii import sys debug = False +####################################################################### +# Using external sslcrypto library: +# import sslcrypto +# ... sslcrypto.ecc.get_curve() +# But to get this script autonomous, i.e. for CI, we embedded the +# code snippets we needed: +####################################################################### +# code snippets from JacobianCurve: +# This code is public domain. Everyone has the right to do whatever they want with it for any purpose. +# Copyright (c) 2013 Vitalik Buterin + +class JacobianCurve: + def __init__(self, p, n, a, b, g): + self.p = p + self.n = n + self.a = a + self.b = b + self.g = g + self.n_length = len(bin(self.n).replace("0b", "")) + + + def to_jacobian(self, p): + return p[0], p[1], 1 + + + def jacobian_double(self, p): + if not p[1]: + return 0, 0, 0 + ysq = (p[1] ** 2) % self.p + s = (4 * p[0] * ysq) % self.p + m = (3 * p[0] ** 2 + self.a * p[2] ** 4) % self.p + nx = (m ** 2 - 2 * s) % self.p + ny = (m * (s - nx) - 8 * ysq ** 2) % self.p + nz = (2 * p[1] * p[2]) % self.p + return nx, ny, nz + + + def jacobian_add(self, p, q): + if not p[1]: + return q + if not q[1]: + return p + u1 = (p[0] * q[2] ** 2) % self.p + u2 = (q[0] * p[2] ** 2) % self.p + s1 = (p[1] * q[2] ** 3) % self.p + s2 = (q[1] * p[2] ** 3) % self.p + if u1 == u2: + if s1 != s2: + return (0, 0, 1) + return self.jacobian_double(p) + h = u2 - u1 + r = s2 - s1 + h2 = (h * h) % self.p + h3 = (h * h2) % self.p + u1h2 = (u1 * h2) % self.p + nx = (r ** 2 - h3 - 2 * u1h2) % self.p + ny = (r * (u1h2 - nx) - s1 * h3) % self.p + nz = (h * p[2] * q[2]) % self.p + return (nx, ny, nz) + + + def from_jacobian(self, p): + z = inverse(p[2], self.p) + return (p[0] * z ** 2) % self.p, (p[1] * z ** 3) % self.p + + + def jacobian_shamir(self, a, n, b, m): + ab = self.jacobian_add(a, b) + if n < 0 or n >= self.n: + n %= self.n + if m < 0 or m >= self.n: + m %= self.n + res = 0, 0, 1 # point on infinity + for i in range(self.n_length - 1, -1, -1): + res = self.jacobian_double(res) + has_n = n & (1 << i) + has_m = m & (1 << i) + if has_n: + if has_m == 0: + res = self.jacobian_add(res, a) + if has_m != 0: + res = self.jacobian_add(res, ab) + else: + if has_m == 0: + res = self.jacobian_add(res, (0, 0, 1)) # Try not to leak + if has_m != 0: + res = self.jacobian_add(res, b) + return res + + def fast_shamir(self, a, n, b, m): + return self.from_jacobian(self.jacobian_shamir(self.to_jacobian(a), n, self.to_jacobian(b), m)) + +####################################################################### +# code snippets from sslcrypto +# MIT License +# Copyright (c) 2019 Ivan Machugovskiy + +import hmac +import os +import hashlib +import struct + +def int_to_bytes(raw, length): + data = [] + for _ in range(length): + data.append(raw % 256) + raw //= 256 + return bytes(data[::-1]) + + +def bytes_to_int(data): + raw = 0 + for byte in data: + raw = raw * 256 + byte + return raw + +def legendre(a, p): + res = pow(a, (p - 1) // 2, p) + if res == p - 1: + return -1 + else: + return res + +def inverse(a, n): + if a == 0: + return 0 + lm, hm = 1, 0 + low, high = a % n, n + while low > 1: + r = high // low + nm, new = hm - lm * r, high - low * r + lm, low, hm, high = nm, new, lm, low + return lm % n + +def square_root_mod_prime(n, p): + if n == 0: + return 0 + if p == 2: + return n # We should never get here but it might be useful + if legendre(n, p) != 1: + raise ValueError("No square root") + # Optimizations + if p % 4 == 3: + return pow(n, (p + 1) // 4, p) + # 1. By factoring out powers of 2, find Q and S such that p - 1 = + # Q * 2 ** S with Q odd + q = p - 1 + s = 0 + while q % 2 == 0: + q //= 2 + s += 1 + # 2. Search for z in Z/pZ which is a quadratic non-residue + z = 1 + while legendre(z, p) != -1: + z += 1 + m, c, t, r = s, pow(z, q, p), pow(n, q, p), pow(n, (q + 1) // 2, p) + while True: + if t == 0: + return 0 + elif t == 1: + return r + # Use repeated squaring to find the least i, 0 < i < M, such + # that t ** (2 ** i) = 1 + t_sq = t + i = 0 + for i in range(1, m): + t_sq = t_sq * t_sq % p + if t_sq == 1: + break + else: + raise ValueError("Should never get here") + # Let b = c ** (2 ** (m - i - 1)) + b = pow(c, 2 ** (m - i - 1), p) + m = i + c = b * b % p + t = t * b * b % p + r = r * b % p + return r + +# name: (nid, p, n, a, b, (Gx, Gy)), +CURVES = { + "secp128r1": ( + 706, + 0xFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF, + 0xFFFFFFFE0000000075A30D1B9038A115, + 0xFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC, + 0xE87579C11079F43DD824993C2CEE5ED3, + ( + 0x161FF7528B899B2D0C28607CA52C5B86, + 0xCF5AC8395BAFEB13C02DA292DDED7A83 + ) + ), + "secp224r1": ( + 713, + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001, + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D, + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE, + 0xB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4, + ( + 0xB70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21, + 0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34 + ) + ), +} + +def get_curve(name): + if name not in CURVES: + raise ValueError("Unknown curve {}".format(name)) + nid, p, n, a, b, g = CURVES[name] + params = {"p": p, "n": n, "a": a, "b": b, "g": g} + return EllipticCurve(nid, p, n, a, b, g) + +class EllipticCurve: + def __init__(self, nid, p, n, a, b, g): + self.p, self.n, self.a, self.b, self.g = p, n, a, b, g + self.jacobian = JacobianCurve(self.p, self.n, self.a, self.b, self.g) + self.public_key_length = (len(bin(p).replace("0b", "")) + 7) // 8 + self.order_bitlength = len(bin(n).replace("0b", "")) + + + def _int_to_bytes(self, raw, len=None): + return int_to_bytes(raw, len or self.public_key_length) + + + def _subject_to_int(self, subject): + return bytes_to_int(subject[:(self.order_bitlength + 7) // 8]) + + + def recover(self, signature, data, hash="sha256"): + # Sanity check: is this signature recoverable? + if len(signature) != 1 + 2 * self.public_key_length: + raise ValueError("Cannot recover an unrecoverable signature") + subject = self._digest(data, hash) + z = self._subject_to_int(subject) + + recid = signature[0] - 27 if signature[0] < 31 else signature[0] - 31 + r = bytes_to_int(signature[1:self.public_key_length + 1]) + s = bytes_to_int(signature[self.public_key_length + 1:]) + + # Verify bounds + if not 0 <= recid < 2 * (self.p // self.n + 1): + raise ValueError("Invalid recovery ID") + if r >= self.n: + raise ValueError("r is out of bounds") + if s >= self.n: + raise ValueError("s is out of bounds") + + rinv = inverse(r, self.n) + u1 = (-z * rinv) % self.n + u2 = (s * rinv) % self.n + + # Recover R + rx = r + (recid // 2) * self.n + if rx >= self.p: + raise ValueError("Rx is out of bounds") + + # Almost copied from decompress_point + ry_square = (pow(rx, 3, self.p) + self.a * rx + self.b) % self.p + try: + ry = square_root_mod_prime(ry_square, self.p) + except Exception: + raise ValueError("Invalid recovered public key") from None + + # Ensure the point is correct + if ry % 2 != recid % 2: + # Fix Ry sign + ry = self.p - ry + + x, y = self.jacobian.fast_shamir(self.g, u1, (rx, ry), u2) + x, y = self._int_to_bytes(x), self._int_to_bytes(y) + + is_compressed = signature[0] >= 31 + if is_compressed: + return bytes([0x02 + (y[-1] % 2)]) + x + else: + return bytes([0x04]) + x + y + + def _digest(self, data, hash): + if hash is None: + return data + elif callable(hash): + return hash(data) + elif hash == "sha1": + return hashlib.sha1(data).digest() + elif hash == "sha256": + return hashlib.sha256(data).digest() + elif hash == "sha512": + return hashlib.sha512(data).digest() + else: + raise ValueError("Unknown hash/derivation method") + +####################################################################### + def recover(data, signature, alghash=None): recovered = set() if len(signature) == 32: - curve = sslcrypto.ecc.get_curve("secp128r1") + curve = get_curve("secp128r1") recoverable = False elif len(signature) == 33: - curve = sslcrypto.ecc.get_curve("secp128r1") + curve = get_curve("secp128r1") recoverable = True elif len(signature) == 56: - curve = sslcrypto.ecc.get_curve("secp224r1") + curve = get_curve("secp224r1") recoverable = False elif len(signature) == 57: - curve = sslcrypto.ecc.get_curve("secp224r1") + curve = get_curve("secp224r1") recoverable = True else: print("Unsupported signature size %i" % len(signature)) @@ -67,18 +359,93 @@ def recover_multiple(uids, sigs, alghash=None): recovered &= recovered_tmp return recovered -if len(sys.argv) < 3 or len(sys.argv) % 2 == 0: - print("Usage: \n%s UID SIGN [UID SIGN] [...]" % sys.argv[0]) - print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0]) - exit(1) +def selftests(): + tests = [ + {'name': "Mifare Ultralight EV1", + 'samples': ["04C1285A373080", "CEA2EB0B3C95D0844A95B824A7553703B3702378033BF0987899DB70151A19E7", + "04C2285A373080", "A561506723D422D29ED9F93E60D20B9ED1E05CC1BF81DA19FE500CA0B81CC0ED"], + 'pk': "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8" }, + {'name': "NTAG21x", + 'samples': ["04E10CDA993C80", "8B76052EE42F5567BEB53238B3E3F9950707C0DCC956B5C5EFCFDB709B2D82B3", + "04DB0BDA993C80", "6048EFD9417CD10F6B7F1818D471A7FE5B46868D2EABDC6307A1E0AAE139D8D0"], + 'pk': "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61" }, + {'name': "Mifare Classic EV1", + 'samples': ["0433619AB35780", "B9FAE369EC21C980650D87ED9AE9B1610E859131B4B8699C647548AB68D249BB", + "524374E2", "F8758CE30A58553A9985C458FB9C7D340FCFB04847B928A0667939272BC58B5E", + "53424B8A", "B4F533E8C06C021E242EFE8558C1672ED7022E5AE4E7AA2D46113B0AB6928AFC"], + 'pk': "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF" }, + {'name': "DESFire Light", + 'samples': ["0439556ACB6480", "D5BD0978106E1E38B513642335966AB21E9F950DCFCFAB45FF13D0DC3CA4C2AE7E0D671DF1240937D040DAC4601C5F66ED62C546EE03ED08", + "043B156ACB6480", "76B46932BF2FCF4931A24C755F5CB1686B914F1856177686B864BDAD58EFA6A7493E5C2232F3ADDAA434EA4647BFD1D385BDA6115E77D74C"], + 'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D" }, + {'name': "DESFire EV2", + 'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508", + "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33"], + 'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A" }, +# TODO one more Mifare Plus EV1... + {'name': "Mifare Plus EV1", + 'samples': ["042A2B221C5080", "BAC40CD88E9193C58ADA5055350C4F648EB5A7AEC4FCF9BD4CDD7B1C558DE5F59C6636F26286ED48622AAA2331D4DF1CEE23B57B94BDA631"], + 'pk': "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E" }, + {'name': "NTAG413DNA", + 'samples': ["042468222F5C80", "B9211E320F321BD1D0E158E10FF15109B389638BAE15D9909D7725BF1250ED236D66F1AF75C94D60330E4E92535F5E6997675281A5687173", + "042938222F5C80", "18B642797D1FD71806146A7A6EC778D3FDD04F39C4A3B36A592BD1A114DC44E5528380FA766C0B7EA32B284AFBE84300B620369F0686D8CC"], + 'pk': "04bb5d514f7050025c7d0f397310360eec91eaf792e96fc7e0f496cb4e669d414f877b7b27901fe67c2e3b33cd39d1c797715189ac951c2add" }, + {'name': "NTAG424DNA", + 'samples': ["0463474AA26A80", "27E9A50E6CA4BA9037C02F7D20A80D0284D0C1D83C67F5A5AC1D8A4EF86C9508417E4E9C6F85AA7920F0ABDED984CAF20467D66EA54BBF08", + "04C46C222A6380", "344A806EBF704C05C19215D2F840529CE365AAD2D08A469A95896D75D477D9FAB02A0C827E9F215BD8EB0E56A3A9A008FB75D706AABBD4DA"], + 'pk': "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410" }, + {'name': "Vivokey Spark1", +# ! tag signature bytes output by pm3 must be read right to left: echo $sig |sed 's/\(..\)/\1\n/g'|tac|tr -d '\n' (and it uses a SHA256) + 'samples': ["E0040118009C870C", "4B4E03E1211952EF6A5F9D84AB218CD4D7549D0CDF8CA8779F9AD16C9A9CBF3B", + "E0040118009B4D62", "25CF13747C3389EC7889DE916E3747584978511CC78B51CFB1883B494CBED7AB"], + 'pk': "04d64bb732c0d214e7ec580736acf847284b502c25c0f7f2fa86aace1dada4387a" }, +# ! tag UID is considered inversed: E0040118009B5FEE => EE5F9B00180104E0 +# TODO one more ICODE-DNA... + {'name': "ICODE DNA, ICODE SLIX2", + 'samples': ["EE5F9B00180104E0", "32D9E7579CD77E6F1FA11419231E874826984C5F189FDE1421684563A9663377"], + 'pk': "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0" }, + ] + succeeded = True + for t in tests: + print("Testing %-25s" % (t['name']+":"), end="") + recovered = recover_multiple(t['samples'][::2], t['samples'][1::2]) + recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], alghash="sha256") + if (len(recovered) == 1): + pk = recovered.pop() + pk = binascii.hexlify(pk).decode('utf8') + if pk.lower() == t['pk'].lower(): + print("[OK]") + else: + succeeded = False + print("[FAIL]") + elif len(t['samples'])//2 == 1: + pks = [binascii.hexlify(pk).decode('utf8').lower() for pk in list(recovered)] + if t['pk'].lower() in pks: + print("[OK] (partial)") + else: + succeeded = False + print("[FAIL]") + else: + succeeded = False + print("[FAIL]") + print("Tests: [%s]" % ["FAIL", "OK"][succeeded]) -print("Assuming no hash was used in the signature generation:") -recovered = recover_multiple(sys.argv[1:][::2], sys.argv[1:][1::2]) -print("Possible uncompressed Pk(s):") -for pk in list(recovered): - print(binascii.hexlify(pk).decode('utf8')) -print("Assuming SHA-256 was used in the signature generation:") -recovered = recover_multiple(sys.argv[1:][::2], sys.argv[1:][1::2], alghash="sha256") -print("Possible uncompressed Pk(s):") -for pk in list(recovered): - print(binascii.hexlify(pk).decode('utf8')) +if __name__ == "__main__": + if len(sys.argv) == 2 and sys.argv[1] == "selftests": + selftests() + exit(0) + if len(sys.argv) < 3 or len(sys.argv) % 2 == 0: + print("Usage: \n%s UID SIGN [UID SIGN] [...]" % sys.argv[0]) + print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0]) + exit(1) + + print("Assuming no hash was used in the signature generation:") + recovered = recover_multiple(sys.argv[1:][::2], sys.argv[1:][1::2]) + print("Possible uncompressed Pk(s):") + for pk in list(recovered): + print(binascii.hexlify(pk).decode('utf8')) + print("Assuming SHA-256 was used in the signature generation:") + recovered = recover_multiple(sys.argv[1:][::2], sys.argv[1:][1::2], alghash="sha256") + print("Possible uncompressed Pk(s):") + for pk in list(recovered): + print(binascii.hexlify(pk).decode('utf8'))