diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 340b53555..26bac8435 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,6 +45,13 @@ jobs: - name: Install dependencies run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.4-dev liblua5.4-0 lua5.4 sed libssl-dev + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools + python3 -m pip install ansicolors sslcrypto + if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi + - name: Checkout repository uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index aa93e1207..7510597a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ 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] +- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo) +- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo) +- Changed `trace list -t seos` - improved annotation (@iceman1001) +- Added `make commands` to regenerate commands documentation files and autocompletion data independently of `make style` (@doegox) +- Added texecom identification, thanks @en4rab ! (@iceman1001) +- Added `hf 15 slixprotectpage` command +- Fixed `hf mf gload` - missing parameter (@iceman1001) +- Changed `hf mf gload` - now handles 1k ev1 sized dumps (@iceman1001) +- Changed wiegand format unpack functions to clear struct later (@iceman1001) +- Changed `wiegand decode` - now accepts new padding format (@iceman1001) +- Changed `mem spiffs tree` - ID is now shown in decimal (@iceman1001) +- Added sample wiegand format 56bit (@iceman1001) +- Changed Wiegand formats to include number of bits (@iceman1001) +- Fix compilation warning in hitagS (@iceman1001) +- Added new wiegand format H800002 (@jmichelp) +- Changed `Makefile.platform.sample` file - now have clear instructions for generating images for other proxmark3 hardware (@iceman1001) +- Changed `doc/magic_cards_notes.md` - now contains documentation for iKey LLC's MF4 tag (@team-orangeBlue) - Changed `hf mf cload` - now accepts MFC Ev1 sized dumps (@iceman1001) - Changed `hf mfu info` - now properly identify ULEv1 AES 50pF (@iceman1001) - Changed `hf mf info` - now differentiates between full USCUID and cut down ZUID chips (@nvx) diff --git a/Makefile b/Makefile index 960fc7557..8d9ba781c 100644 --- a/Makefile +++ b/Makefile @@ -204,6 +204,7 @@ help: @echo "+ fpga_compress - Make tools/fpga_compress" @echo @echo "+ style - Apply some automated source code formatting rules" + @echo "+ commands - Regenerate commands documentation files and autocompletion data @echo "+ check - Run offline tests. Set CHECKARGS to pass arguments to the test script" @echo "+ .../check - Run offline tests against specific target. See above." @echo "+ miscchecks - Detect various encoding issues in source code" @@ -303,7 +304,7 @@ endif # easy printing of MAKE VARIABLES print-%: ; @echo $* = $($*) -style: +style: commands # Make sure astyle is installed @command -v astyle >/dev/null || ( echo "Please install 'astyle' package first" ; exit 1 ) # Remove spaces & tabs at EOL, add LF at EOF if needed on *.c, *.h, *.cpp. *.lua, *.py, *.pl, Makefile, *.v, pm3 @@ -317,13 +318,14 @@ style: --keep-one-line-blocks --max-continuation-indent=60 \ --style=google --pad-oper --unpad-paren --pad-header \ --align-pointer=name {} \; + +commands: # Update commands.md [ -x client/proxmark3 ] && client/proxmark3 -m | tr -d '\r' > doc/commands.md # Make sure python3 is installed @command -v python3 >/dev/null || ( echo "Please install 'python3' package first" ; exit 1 ) # Update commands.json, patch port in case it was run under Windows [ -x client/proxmark3 ] && client/proxmark3 --fulltext | sed 's#com[0-9]#/dev/ttyACM0#'|python3 client/pyscripts/pm3_help2json.py - - | tr -d '\r' > doc/commands.json - # Update the readline autocomplete autogenerated code [ -x client/proxmark3 ] && client/proxmark3 --fulltext | python3 client/pyscripts/pm3_help2list.py - - | tr -d '\r' > client/src/pm3line_vocabulary.h diff --git a/Makefile.platform.sample b/Makefile.platform.sample index 2661a0720..26cb08a0f 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -1,8 +1,20 @@ # If you want to use it, copy this file as Makefile.platform and adjust it to your needs # Run 'make PLATFORM=' to get an exhaustive list of possible parameters for this file. +# By default PM3 RDV4 image is generated. +# Comment the line below and uncomment further down according to which device you have PLATFORM=PM3RDV4 + +# For PM3 Easy: +# uncomment the line below #PLATFORM=PM3GENERIC + +# For ICOPY-X and PM3 Max: +# uncomment the two lines below +#PLATFORM=PM3ICOPY3 +#PLATFORM_EXTRAS=FLASH + + # If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON #PLATFORM_EXTRAS=FLASH @@ -10,6 +22,7 @@ PLATFORM=PM3RDV4 #PLATFORM_EXTRAS=BTADDON FLASH #STANDALONE=LF_SAMYRUN + # Uncomment the line below to set the correct LED order on board Proxmark3 Easy # Only available with PLATFORM=PM3GENERIC #LED_ORDER=PM3EASY diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index f3f84af13..26af3af0d 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -321,9 +321,10 @@ bool RAMFUNC LogTraceBits(const uint8_t *btBytes, uint16_t bitLen, uint32_t time // Emulator memory int emlSet(const uint8_t *data, uint32_t offset, uint32_t length) { uint8_t *mem = BigBuf_get_EM_addr(); - if (!mem) { + if (mem == NULL) { return PM3_EMALLOC; } + if (offset + length <= CARD_MEMORY_SIZE) { memcpy(mem + offset, data, length); return PM3_SUCCESS; @@ -335,9 +336,10 @@ int emlSet(const uint8_t *data, uint32_t offset, uint32_t length) { int emlGet(uint8_t *out, uint32_t offset, uint32_t length) { uint8_t *mem = BigBuf_get_EM_addr(); - if (!mem) { + if (mem == NULL) { return PM3_EMALLOC; } + if (offset + length <= CARD_MEMORY_SIZE) { memcpy(out, mem + offset, length); return PM3_SUCCESS; diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index ad45cda1a..61c93514b 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -311,7 +311,7 @@ void WriteTagToFlash(uint32_t uid, size_t size) { uint32_t len = size; uint8_t data[(size * (16 * 64)) / 1024]; - emlGetMem(data, 0, (size * 64) / 1024); + emlGetMem_xt(data, 0, (size * 64) / 1024, MIFARE_BLOCK_SIZE); char dest[SPIFFS_OBJ_NAME_LEN]; uint8_t buid[4]; @@ -646,7 +646,7 @@ failtag: emlClearMem(); uint8_t mblock[16]; for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { - emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + emlGetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, MIFARE_BLOCK_SIZE); for (uint8_t t = 0; t < 2; t++) { memcpy(mblock + t * 10, foundKey[t][sectorNo], 6); } @@ -807,7 +807,7 @@ int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { emlSetMem_xt(dataoutbuf, FirstBlockOfSector(s) + blockNo, 1, 16); } else { // sector trailer, keep the keys, set only the AC - emlGetMem(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1); + emlGetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, MIFARE_BLOCK_SIZE); memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4); emlSetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, 16); } @@ -878,7 +878,7 @@ void saMifareMakeTag(void) { int flags = 0; for (int blockNum = 0; blockNum < 16 * 4; blockNum++) { uint8_t mblock[16]; - emlGetMem(mblock, blockNum, 1); + emlGetMem_xt(mblock, blockNum, 1, MIFARE_BLOCK_SIZE); // switch on field and send magic sequence if (blockNum == 0) flags = 0x08 + 0x02; diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index ad1e6b863..615d79718 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -500,7 +500,7 @@ void RunMod(void) { uint8_t mblock[MIFARE_BLOCK_SIZE]; for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; ++sectorNo) { if (validKey[0][sectorNo] || validKey[1][sectorNo]) { - emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + emlGetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, MIFARE_BLOCK_SIZE); for (uint8_t keyType = 0; keyType < 2; ++keyType) { if (validKey[keyType][sectorNo]) { memcpy(mblock + keyType * 10, foundKey[keyType][sectorNo], 6); diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 4ea27b082..711e653a4 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -445,7 +445,7 @@ void RunMod(void) { // received a RATS request } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { prevCmd = 0; - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; } else { if (g_dbglevel == DBG_DEBUG) { diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index 51a30f64e..1b84eb3f7 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -338,7 +338,7 @@ void RunMod() { } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) p_response = &responses[RESP_INDEX_SAKC1]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; resp = 1; } else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension DbpString(_YELLOW_("!!") " Reader accepted time extension!"); diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index 9b42d0fd0..e6f75bc75 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -247,7 +247,7 @@ void RunMod(void) { } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2) p_response = &responses[RESP_INDEX_SAKC2]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; } else if (receivedCmd[0] == ISO14443A_CMD_PPS) { p_response = &responses[RESP_INDEX_PPS]; } else { @@ -425,7 +425,7 @@ void RunMod(void) { } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2) p_response = &responses[RESP_INDEX_SAKC2]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; } else if (receivedCmd[0] == ISO14443A_CMD_PPS) { p_response = &responses[RESP_INDEX_PPS]; } else { diff --git a/armsrc/Standalone/lf_hidbrute.c b/armsrc/Standalone/lf_hidbrute.c index 87878cfa3..822df3bfb 100644 --- a/armsrc/Standalone/lf_hidbrute.c +++ b/armsrc/Standalone/lf_hidbrute.c @@ -257,7 +257,7 @@ void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low // Calculate new high and low base value from card number and facility code, without parity new_low = (fc << 21) | (cardnum << 1); - new_high = 0x28 | ((fc >> 11) & 1); // 0x28 is 101000 + new_high = (fc >> 11) & 1; int n_ones; uint32_t i; @@ -319,6 +319,7 @@ void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low new_high = new_high | 0x4; // Setting new calculated values + add_HID_preamble(0, &new_high, &new_low, 35); *low = new_low; *high = new_high; } diff --git a/armsrc/Standalone/lf_hidfcbrute.c b/armsrc/Standalone/lf_hidfcbrute.c index 75c97e0bf..ef7102fdb 100644 --- a/armsrc/Standalone/lf_hidfcbrute.c +++ b/armsrc/Standalone/lf_hidfcbrute.c @@ -176,8 +176,7 @@ void hid_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t card newlow |= oddparity32((newlow >> 1) & 0xFFF); newlow |= (evenparity32((newlow >> 13) & 0xFFF)) << 25; - newhigh |= 0x20; // Bit 37; standard header - newlow |= 1U << 26; // leading 1: start bit + add_HID_preamble(NULL, &newhigh, &newlow, 26); *low = newlow; *high = newhigh; diff --git a/armsrc/Standalone/lf_prox2brute.c b/armsrc/Standalone/lf_prox2brute.c index 851dd597a..86c06a91a 100644 --- a/armsrc/Standalone/lf_prox2brute.c +++ b/armsrc/Standalone/lf_prox2brute.c @@ -16,8 +16,8 @@ //----------------------------------------------------------------------------- // LF HID ProxII Brutforce v2 by lnv42 - based on Proxbrute by Brad antoniewicz // -// Following code is a trivial brute forcer for when you know the facility -// code and want to find valid(s) card number(s). It will try all card +// Following code is a trivial brute forcer (H10301 26-bit) when you know the +// facility code and want to find valid(s) card number(s). It will try all card // fnumbers rom CARDNUM_START to CARDNUM_END one by one (max. ~65k tries). // This brute force will be a lot faster than Proxbrute that will try all // possibles values for LF low, even those with bad checksum (~4g tries). @@ -46,8 +46,7 @@ void RunMod(void) { StandAloneMode(); Dbprintf(">> LF HID proxII bruteforce v2 a.k.a Prox2Brute Started <<"); FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - - const uint32_t high = 0x20; // LF high value is always 0x20 here + uint32_t high = 0; uint32_t fac = FACILITY_CODE, cardnum = 0; @@ -82,6 +81,7 @@ void RunMod(void) { uint32_t low = (cardnum << 1) | (fac << 17); low |= oddparity32((low >> 1) & 0xFFF); low |= evenparity32((low >> 13) & 0xFFF) << 25; + add_HID_preamble(NULL, &high, &low, 26); Dbprintf("[=] trying Facility = %08x, Card = %08x, raw = %08x%08x", fac, cardnum, high, low); diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e9c1fc48d..a32024223 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1370,7 +1370,11 @@ static void PacketReceived(PacketCommandNG *packet) { // involved in dealing with emulator memory. But if it is called later, it might // destroy the Emulator Memory. //----------------------------------------------------------------------------- - EmlClearIso15693(); + // Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent + // an inconvenient reset in the future by Iso15693InitTag + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); + BigBuf_Clear_EM(); + reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ISO15693_EML_SETMEM: { @@ -1402,7 +1406,7 @@ static void PacketReceived(PacketCommandNG *packet) { return; } - uint8_t *buf = BigBuf_malloc(payload->length); + uint8_t *buf = BigBuf_calloc(payload->length); emlGet(buf, payload->offset, payload->length); LED_B_ON(); reply_ng(CMD_HF_ISO15693_EML_GETMEM, PM3_SUCCESS, buf, payload->length); @@ -1463,6 +1467,17 @@ static void PacketReceived(PacketCommandNG *packet) { WritePasswordSlixIso15693(payload->old_pwd, payload->new_pwd, payload->pwd_id); break; } + case CMD_HF_ISO15693_SLIX_PROTECT_PAGE: { + struct p { + uint8_t read_pwd[4]; + uint8_t write_pwd[4]; + uint8_t divide_ptr; + uint8_t prot_status; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + ProtectPageSlixIso15693(payload->read_pwd, payload->write_pwd, payload->divide_ptr, payload->prot_status); + break; + } case CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY: { struct p { uint8_t pwd[4]; @@ -1677,7 +1692,7 @@ static void PacketReceived(PacketCommandNG *packet) { EMVsim(payload->flags, payload->exitAfter, payload->uid, payload->atqa, payload->sak); break; } -#endif +#endif case CMD_HF_ISO14443A_SIMULATE: { struct p { uint8_t tagtype; @@ -1696,20 +1711,21 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t tagtype; uint16_t flags; uint8_t uid[10]; - uint8_t rats[20]; + uint8_t ats[20]; uint8_t aid[30]; - uint8_t response[100]; - uint8_t apdu[100]; - int aid_len; - int respond_len; - int apdu_len; - bool enumerate; + uint8_t selectaid_response[100]; + uint8_t getdata_response[100]; + uint32_t ats_len; + uint32_t aid_len; + uint32_t selectaid_response_len; + uint32_t getdata_response_len; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; + // ## Simulate iso14443a tag - pass tag type, UID, ATS, AID, responses SimulateIso14443aTagAID(payload->tagtype, payload->flags, payload->uid, - payload->rats, sizeof(payload->rats), payload->aid, payload->response, - payload->apdu, payload->aid_len, payload->respond_len, - payload->apdu_len, payload->enumerate); // ## Simulate iso14443a tag - pass tag type, UID, rats, aid, resp, apdu + payload->ats, payload->ats_len, payload->aid, payload->aid_len, + payload->selectaid_response, payload->selectaid_response_len, + payload->getdata_response, payload->getdata_response_len); break; } case CMD_HF_ISO14443A_ANTIFUZZ: { @@ -1889,36 +1905,65 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_EML_MEMCLR: { - MifareEMemClr(); - reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0); + + //----------------------------------------------------------------------------- + // Work with emulator memory + // + // Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not + // involved in dealing with emulator memory. But if it is called later, it might + // destroy the Emulator Memory. + //----------------------------------------------------------------------------- FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // Not only clears the emulator memory, + // also sets default MIFARE values for sector trailers. + emlClearMem(); + reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_MIFARE_EML_MEMSET: { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); struct p { - uint8_t blockno; + uint16_t blockno; uint8_t blockcnt; uint8_t blockwidth; uint8_t data[]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // backwards compat... default bytewidth - if (payload->blockwidth == 0) - payload->blockwidth = 16; + if (payload->blockwidth == 0) { + payload->blockwidth = MIFARE_BLOCK_SIZE; + } emlSetMem_xt(payload->data, payload->blockno, payload->blockcnt, payload->blockwidth); break; } case CMD_HF_MIFARE_EML_MEMGET: { + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); struct p { - uint8_t blockno; + uint16_t blockno; uint8_t blockcnt; + uint8_t blockwidth; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - MifareEMemGet(payload->blockno, payload->blockcnt); + + // + size_t size = payload->blockno * payload->blockwidth; + if (size > PM3_CMD_DATA_SIZE) { + reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0); + return; + } + + uint8_t *buf = BigBuf_calloc(size); + + emlGetMem_xt(buf, payload->blockno, payload->blockcnt, payload->blockwidth); // data, block num, blocks count (max 4) + + LED_B_ON(); + reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size); + LED_B_OFF(); + BigBuf_free_keep_EM(); break; } case CMD_HF_MIFARE_EML_LOAD: { diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto.c index 24da62747..0e9dc9466 100644 --- a/armsrc/desfire_crypto.c +++ b/armsrc/desfire_crypto.c @@ -133,7 +133,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, ui } void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]) { - if (length % 8) return; + if (length % 8) { + return; + } uint8_t *tin = (uint8_t *) in; uint8_t *tout = (uint8_t *) out; @@ -143,7 +145,9 @@ void aes128_nxp_receive(const void *in, void *out, size_t length, const void *ke } void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]) { - if (length % 8) return; + if (length % 8) { + return; + } uint8_t *tin = (uint8_t *) in; uint8_t *tout = (uint8_t *) out; @@ -152,12 +156,15 @@ void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, length, iv, tin, tout); } -void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key) { - uint8_t data[8]; - memcpy(data, value, 8); - for (int n = 0; n < 8; n++) { +void Desfire_des_key_new(const uint8_t *value, desfirekey_t key) { + + uint8_t data[8] = {0}; + memcpy(data, value, sizeof(data)); + + for (size_t n = 0; n < sizeof(data); n++) { data[n] &= 0xFE; } + Desfire_des_key_new_with_version(data, key); } @@ -246,22 +253,24 @@ void Desfire_key_set_version(desfirekey_t key, uint8_t version) { void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfirekey_t authkey, desfirekey_t key) { - uint8_t buffer[24]; + uint8_t buffer[24] = {0}; switch (authkey->type) { - case T_DES: + case T_DES: { memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); Desfire_des_key_new_with_version(buffer, key); break; - case T_3DES: + } + case T_3DES: { memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 4, 4); memcpy(buffer + 12, rndb + 4, 4); Desfire_3des_key_new_with_version(buffer, key); break; - case T_3K3DES: + } + case T_3K3DES: { memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 6, 4); @@ -270,22 +279,15 @@ void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfire memcpy(buffer + 20, rndb + 12, 4); Desfire_3k3des_key_new(buffer, key); break; - case T_AES: + } + case T_AES: { memcpy(buffer, rnda, 4); memcpy(buffer + 4, rndb, 4); memcpy(buffer + 8, rnda + 12, 4); memcpy(buffer + 12, rndb + 12, 4); Desfire_aes_key_new(buffer, key); break; - } -} - -static size_t key_macing_length(desfirekey_t key); - -// iceman, see memxor inside string.c, dest/src swapped.. -static void xor(const uint8_t *ivect, uint8_t *data, const size_t len) { - for (size_t i = 0; i < len; i++) { - data[i] ^= ivect[i]; + } } } @@ -306,7 +308,7 @@ void cmac_generate_subkeys(desfirekey_t key) { // Used to compute CMAC on complete blocks memcpy(key->cmac_sk1, l, kbs); - txor = l[0] & 0x80; + txor = (l[0] & 0x80); lsl(key->cmac_sk1, kbs); @@ -317,7 +319,7 @@ void cmac_generate_subkeys(desfirekey_t key) { // Used to compute CMAC on the last block if non-complete memcpy(key->cmac_sk2, key->cmac_sk1, kbs); - txor = key->cmac_sk1[0] & 0x80; + txor = (key->cmac_sk1[0] & 0x80); lsl(key->cmac_sk2, kbs); @@ -341,15 +343,14 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le while (len % kbs) { buffer[len++] = 0x00; } - xor(key->cmac_sk2, buffer + len - kbs, kbs); + xor(buffer + len - kbs, key->cmac_sk2, kbs); } else { - xor(key->cmac_sk1, buffer + len - kbs, kbs); + xor(buffer + len - kbs, key->cmac_sk1, kbs); } mifare_cypher_blocks_chained(NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER); memcpy(cmac, ivect, kbs); - //free(buffer); } size_t key_block_size(const desfirekey_t key) { @@ -374,7 +375,7 @@ size_t key_block_size(const desfirekey_t key) { /* * Size of MACing produced with the key. */ -static size_t key_macing_length(const desfirekey_t key) { +size_t key_macing_length(const desfirekey_t key) { size_t mac_length = DESFIRE_MAC_LENGTH; switch (key->type) { case T_DES: @@ -393,10 +394,11 @@ static size_t key_macing_length(const desfirekey_t key) { * Size required to store nbytes of data in a buffer of size n*block_size. */ size_t padded_data_length(const size_t nbytes, const size_t block_size) { - if ((!nbytes) || (nbytes % block_size)) + if ((!nbytes) || (nbytes % block_size)) { return ((nbytes / block_size) + 1) * block_size; - else + } else { return nbytes; + } } /* @@ -412,12 +414,14 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c size_t crc_length = 0; if (!(communication_settings & NO_CRC)) { switch (DESFIRE(tag)->authentication_scheme) { - case AS_LEGACY: + case AS_LEGACY: { crc_length = 2; break; - case AS_NEW: + } + case AS_NEW: { crc_length = 4; break; + } } } @@ -428,18 +432,20 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, size_t offset, int communication_settings) { uint8_t *res = data; - uint8_t mac[4]; + uint8_t mac[4] = {0}; size_t edl; bool append_mac = true; - desfirekey_t key = DESFIRE(tag)->session_key; - if (!key) + desfirekey_t key = DESFIRE(tag)->session_key; + if (!key) { return data; + } switch (communication_settings & MDCM_MASK) { - case MDCM_PLAIN: - if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) + case MDCM_PLAIN: { + if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) { break; + } /* * When using new authentication methods, PLAIN data transmission from @@ -452,13 +458,16 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, */ append_mac = false; - + } /* pass through */ - case MDCM_MACED: + case MDCM_MACED: { switch (DESFIRE(tag)->authentication_scheme) { - case AS_LEGACY: - if (!(communication_settings & MAC_COMMAND)) + case AS_LEGACY: { + + if (!(communication_settings & MAC_COMMAND)) { break; + } + /* pass through */ edl = padded_data_length(*nbytes - offset, key_block_size(DESFIRE(tag)->session_key)) + offset; @@ -475,8 +484,9 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, // Copy again provided data (was overwritten by mifare_cypher_blocks_chained) memcpy(res, data, *nbytes); - if (!(communication_settings & MAC_COMMAND)) + if (!(communication_settings & MAC_COMMAND)) { break; + } // Append MAC size_t bla = maced_data_length(DESFIRE(tag)->session_key, *nbytes - offset) + offset; (void)bla++; @@ -485,9 +495,12 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, *nbytes += 4; break; - case AS_NEW: - if (!(communication_settings & CMAC_COMMAND)) + } + case AS_NEW: { + if (!(communication_settings & CMAC_COMMAND)) { break; + } + cmac(key, DESFIRE(tag)->ivect, res, *nbytes, DESFIRE(tag)->cmac); if (append_mac) { @@ -498,10 +511,11 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, *nbytes += DESFIRE_CMAC_LENGTH; } break; + } } - break; - case MDCM_ENCIPHERED: + } + case MDCM_ENCIPHERED: { /* |<-------------- data -------------->| * |<--- offset -->| | * +---------------+--------------------+-----+---------+ @@ -517,8 +531,10 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, * encypher()/decypher() */ - if (!(communication_settings & ENC_COMMAND)) + if (!(communication_settings & ENC_COMMAND)) { break; + } + edl = enciphered_data_length(tag, *nbytes - offset, communication_settings) + offset; // Fill in the crypto buffer with data ... @@ -526,14 +542,16 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, if (!(communication_settings & NO_CRC)) { // ... CRC ... switch (DESFIRE(tag)->authentication_scheme) { - case AS_LEGACY: + case AS_LEGACY: { AddCrc14A(res + offset, *nbytes - offset); *nbytes += 2; break; - case AS_NEW: + } + case AS_NEW: { crc32_append(res, *nbytes); *nbytes += 4; break; + } } } // ... and padding @@ -543,32 +561,34 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, mifare_cypher_blocks_chained(tag, NULL, NULL, res + offset, *nbytes - offset, MCD_SEND, (AS_NEW == DESFIRE(tag)->authentication_scheme) ? MCO_ENCYPHER : MCO_DECYPHER); break; - default: - + } + default: { *nbytes = -1; res = NULL; break; + } } return res; - } void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings) { + void *res = data; - uint8_t first_cmac_byte = 0x00; - - desfirekey_t key = DESFIRE(tag)->session_key; - - if (!key) { - return data; - } // Return directly if we just have a status code. if (1 == *nbytes) { return res; } + + desfirekey_t key = DESFIRE(tag)->session_key; + if (!key) { + return data; + } + + uint8_t first_cmac_byte = 0x00; + switch (communication_settings & MDCM_MASK) { case MDCM_PLAIN: { @@ -659,11 +679,12 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes break; } case MDCM_ENCIPHERED: { + (*nbytes)--; + bool verified = false; int crc_pos = 0x00; int end_crc_pos = 0x00; - uint8_t x; /* * AS_LEGACY: @@ -742,8 +763,9 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes verified = true; for (int n = end_crc_pos; n < *nbytes - 1; n++) { uint8_t byte = ((uint8_t *)res)[n]; - if (!((0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)))) + if (!((0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)))) { verified = false; + } } } @@ -768,7 +790,7 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes break; } case AS_NEW: { - x = ((uint8_t *)res)[crc_pos - 1]; + uint8_t x = ((uint8_t *)res)[crc_pos - 1]; ((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos]; ((uint8_t *)res)[crc_pos] = x; break; @@ -802,9 +824,10 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size) { + uint8_t ovect[DESFIRE_MAX_CRYPTO_BLOCK_SIZE]; if (direction == MCD_SEND) { - xor(ivect, data, block_size); + xor(data, ivect, block_size); } else { memcpy(ovect, data, block_size); } @@ -812,70 +835,80 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, uint8_t edata[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; switch (key->type) { - case T_DES: + case T_DES: { switch (operation) { - case MCO_ENCYPHER: + case MCO_ENCYPHER: { //DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); des_encrypt(edata, data, key->data); break; - case MCO_DECYPHER: + } + case MCO_DECYPHER: { //DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); des_decrypt(edata, data, key->data); break; + } } break; - case T_3DES: + } + case T_3DES: { switch (operation) { - case MCO_ENCYPHER: + case MCO_ENCYPHER: { mbedtls_des3_set2key_enc(&ctx3, key->data); mbedtls_des3_crypt_ecb(&ctx3, data, edata); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); break; - case MCO_DECYPHER: + } + case MCO_DECYPHER: { mbedtls_des3_set2key_dec(&ctx3, key->data); mbedtls_des3_crypt_ecb(&ctx3, data, edata); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); break; + } } break; - case T_3K3DES: + } + case T_3K3DES: { switch (operation) { - case MCO_ENCYPHER: + case MCO_ENCYPHER: { mbedtls_des3_set3key_enc(&ctx3, key->data); mbedtls_des3_crypt_ecb(&ctx3, data, edata); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_ENCRYPT); break; - case MCO_DECYPHER: + } + case MCO_DECYPHER: { mbedtls_des3_set3key_dec(&ctx3, key->data); mbedtls_des3_crypt_ecb(&ctx3, data, edata); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_DECRYPT); // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); break; + } } break; - case T_AES: + } + case T_AES: { switch (operation) { case MCO_ENCYPHER: { mbedtls_aes_init(&actx); mbedtls_aes_setkey_enc(&actx, key->data, 128); - mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(edata), ivect, data, edata); + mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_ENCRYPT, data, edata); break; } case MCO_DECYPHER: { mbedtls_aes_init(&actx); mbedtls_aes_setkey_dec(&actx, key->data, 128); - mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(edata), ivect, edata, data); + mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_DECRYPT, data, edata); break; } } break; + } } memcpy(data, edata, block_size); @@ -883,7 +916,7 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, if (direction == MCD_SEND) { memcpy(ivect, data, block_size); } else { - xor(ivect, data, block_size); + xor(data, ivect, block_size); memcpy(ivect, ovect, block_size); } } diff --git a/armsrc/desfire_crypto.h b/armsrc/desfire_crypto.h index 3413125d4..10a15fbf9 100644 --- a/armsrc/desfire_crypto.h +++ b/armsrc/desfire_crypto.h @@ -183,7 +183,7 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, ui void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]); void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]); -void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key); +void Desfire_des_key_new(const uint8_t *value, desfirekey_t key); void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key); void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key); void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key); @@ -207,4 +207,6 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c void cmac_generate_subkeys(desfirekey_t key); void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac); +size_t key_macing_length(desfirekey_t key); + #endif diff --git a/armsrc/emvsim.c b/armsrc/emvsim.c index fd50b040d..5b2c2b58e 100644 --- a/armsrc/emvsim.c +++ b/armsrc/emvsim.c @@ -125,7 +125,7 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece Dbprintf(""); // use annotate to give some hints about the command - annotate(&receivedCmd[1], receivedCmd_len-1); + annotate(&receivedCmd[1], receivedCmd_len - 1); // This is a common request from the reader which we can just immediately respond to since we know we can't // handle it. @@ -141,7 +141,7 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece currentState = GENERATE_AC; - memcpy(receivedCmd, (unsigned char[]){ 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6); + memcpy(receivedCmd, (unsigned char[]) { 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6); for (int i = 0; i < 29; i++) { receivedCmd[6 + i] = receivedCmd[12 + i]; @@ -240,7 +240,8 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece 0x20, 0x00, 0x9f, 0x26, 0x08, 0x56, 0xcb, 0x4e, 0xe1, 0xa4, 0xef, 0xac, 0x74, 0x9f, 0x27, 0x01, 0x80, 0x9f, 0x36, 0x02, 0x00, 0x07, 0x9f, 0x6c, 0x02, 0x3e, 0x00, 0x9f, 0x6e, 0x04, - 0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff}; + 0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff + }; // do the replacement template[0] = responseToReader[0]; // class bit 0 diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 18e6866dd..a8f04e8a2 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -821,12 +821,17 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t * // stage 1, got UID if (bCrypto == false) { + uint64_t ui64key = key[0] | + ((uint64_t)key[1]) << 8 | + ((uint64_t)key[2]) << 16 | + ((uint64_t)key[3]) << 24 | + ((uint64_t)key[4]) << 32 | + ((uint64_t)key[5]) << 40; + + uint32_t ui32uid = MemLeToUint4byte(rx); + DBG Dbprintf("hitag2_crypto: key array "); DBG Dbhexdump(6, key, false); - - uint64_t ui64key = key[0] | ((uint64_t)key[1]) << 8 | ((uint64_t)key[2]) << 16 | ((uint64_t)key[3]) << 24 | ((uint64_t)key[4]) << 32 | ((uint64_t)key[5]) << 40; - - uint32_t ui32uid = rx[0] | ((uint32_t)rx[1]) << 8 | ((uint32_t)rx[2]) << 16 | ((uint32_t)rx[3]) << 24; DBG Dbprintf("hitag2_crypto: key=0x%x%x uid=0x%x" , (uint32_t)((REV64(ui64key)) >> 32) , (uint32_t)((REV64(ui64key)) & 0xffffffff) diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index 18039f431..4c4e3d493 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -74,7 +74,7 @@ static uint8_t pwdh0, pwdl0, pwdl1; // password bytes static uint8_t rnd[] = {0x85, 0x44, 0x12, 0x74}; // random number static uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, ~47min -#define TIMESTAMP (AT91C_BASE_TC2->TC_SR &AT91C_TC_COVFS ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0) +#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0) //#define SENDBIT_TEST @@ -538,7 +538,8 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, rotate_uid++; *txlen = 32; // init crypt engine - state = ht2_hitag2_init(REV64(tag.data.s.key), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rx)); + uint32_t le_rx = MemLeToUint4byte(rx); + state = ht2_hitag2_init(REV64(tag.data.s.key), REV32(tag.data.s.uid_le), REV32(le_rx)); DBG Dbhexdump(8, tx, false); for (int i = 0; i < 4; i++) { @@ -776,7 +777,9 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) { if (ledcontrol) LED_B_ON(); // Capture reader cmd start timestamp - if (start_time == 0) start_time = TIMESTAMP - HITAG_T_LOW; + if (start_time == 0) { + start_time = TIMESTAMP - HITAG_T_LOW; + } // Capture reader frame if (rb >= HITAG_T_STOP) { @@ -864,9 +867,6 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 uint32_t rb_i = 0, h2 = 0, h3 = 0, h4 = 0; uint8_t edges[160] = {0}; - // Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RB:%i TIMESTAMP:%u", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV, - // AT91C_BASE_TC1->TC_RB, TIMESTAMP); - // Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) { @@ -1034,8 +1034,9 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t DbpString("htS: response_bit:"); Dbhexdump(*rxlen, response_bit, false); Dbprintf("htS: skipping %d bit SOF", sof_bits); + if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) { - DbpString("htS: Warning, not all bits of SOF are 1"); + DBG DbpString("htS: Warning, not all bits of SOF are 1"); } } @@ -1128,12 +1129,10 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz // if the tag is in authentication mode try the key or challenge if (packet->cmd == HTSF_KEY) { - DBG DbpString("Authenticating using key:"); - DBG Dbhexdump(6, packet->key, false); + key_le = MemLeToUint6byte(packet->key); - key_le = *(uint64_t *)packet->key; - - uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rnd)); + uint32_t le_val = MemLeToUint4byte(rnd); + uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(le_val)); uint8_t auth_ks[4]; for (int i = 0; i < 4; i++) { @@ -1144,6 +1143,8 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz txlen = concatbits(tx, txlen, rnd, 0, 32); txlen = concatbits(tx, txlen, auth_ks, 0, 32); + DBG DbpString("Authenticating using key:"); + DBG Dbhexdump(6, packet->key, false); DBG Dbprintf("%02X %02X %02X %02X %02X %02X %02X %02X", tx[0], tx[1], tx[2], tx[3], tx[4], tx[5], tx[6], tx[7]); } else if (packet->cmd == HTSF_CHALLENGE) { @@ -1225,7 +1226,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz pwdl1 = 0; if (packet->cmd == HTSF_KEY) { - uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rnd)); + uint32_t le_val = MemLeToUint4byte(rnd); + uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(le_val)); + for (int i = 0; i < 4; i++) { ht2_hitag2_byte(&state); } diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 0611256e7..9d282eee6 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -690,8 +690,6 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { if (Demod.bitCount) { // there are some remaining data bits Demod.shiftReg <<= (8 - Demod.bitCount); // left align the decoded bits Demod.output[Demod.len++] = Demod.shiftReg & 0xFF; // and add them to the output - -// Dbprintf("A | len... %u - %u == 0x%02x", Demod.len, Demod.bitCount, Demod.output[0]); return true; } @@ -1077,6 +1075,7 @@ bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffe if (ts->max > max_buffer_size) { Dbprintf("ToSend buffer, Out-of-bound, when modulating bits for tag answer:"); Dbhexdump(response_info->response_n, response_info->response, false); + Dbprintf("Need %i, got %i", ts->max, max_buffer_size); return false; } @@ -1108,7 +1107,7 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_ } bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, - uint8_t *iRATs, size_t irats_len, tag_response_info_t **responses, + uint8_t *ats, size_t ats_len, tag_response_info_t **responses, uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) { uint8_t sak = 0; // The first response contains the ATQA (note: bytes are transmitted in reverse order). @@ -1130,9 +1129,9 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1 // TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us) // TC(1) = 0x02: CID supported, NAD not supported -// static uint8_t rRATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; - static uint8_t rRATS[40] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 }; - uint8_t rRATS_len = 8; +// static uint8_t rATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; + static uint8_t rATS[40] = { 0x06, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 }; + uint8_t rATS_len = 8; // GET_VERSION response for EV1/NTAG static uint8_t rVERSION[10] = { 0x00 }; @@ -1184,8 +1183,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, rATQA[0] = 0x44; rATQA[1] = 0x03; sak = 0x20; - memcpy(rRATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8); - rRATS_len = 8; + memcpy(rATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8); + rATS_len = 8; // including CRC break; } case 4: { // ISO/IEC 14443-4 - javacard (JCOP) @@ -1254,8 +1253,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, } case 11: { // ISO/IEC 14443-4 - javacard (JCOP) / EMV - memcpy(rRATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00", 19); - rRATS_len = 19; + memcpy(rATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00\x00\x00", 21); + rATS_len = 21; // including CRC rATQA[0] = 0x04; sak = 0x20; break; @@ -1271,19 +1270,21 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, } } - // copy the iRATs if supplied. - // iRATs is a pointer to 20 byte array - // rRATS is a 40 byte array - if ((flags & FLAG_RATS_IN_DATA) == FLAG_RATS_IN_DATA) { - memcpy(rRATS, iRATs, irats_len); - // rats len is dictated by the first char of the string, add 2 crc bytes - rRATS_len = (iRATs[0] + 2); - // Since its Varible length we can send value > 40 and overflow our array. - // Even if RATS protocol defined as max 40 bytes doesn't mean people try stuff - if (rRATS_len > sizeof(rRATS)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: iRATS overflow. Max %zu, got %zu", sizeof(rRATS), rRATS_len); + // copy the ats if supplied. + // ats is a pointer to 20 byte array + // rATS is a 40 byte array + if ((flags & FLAG_ATS_IN_DATA) == FLAG_ATS_IN_DATA) { + // Even if RATS protocol defined as max 40 bytes doesn't mean people try stuff. Check for overflow before copy + if (ats_len + 2 > sizeof(rATS)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: ATS overflow. Max %zu, got %zu", sizeof(rATS) - 2, ats_len); return false; } + memcpy(rATS, ats, ats_len); + rATS_len = ats_len + 2; + // ATS length (without CRC) is supposed to match its first byte TL + if (ats_len != ats[0]) { + if (g_dbglevel >= DBG_INFO) Dbprintf("[-] WARNING: actual ATS length (%zu) differs from its TL value (%u).", ats_len, ats[0]); + } } // if uid not supplied then get from emulator memory @@ -1379,7 +1380,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, return false; } - AddCrc14A(rRATS, rRATS_len - 2); + AddCrc14A(rATS, rATS_len - 2); AddCrc14A(rPPS, sizeof(rPPS) - 2); @@ -1407,7 +1408,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, { .response = rSAKc1, .response_n = sizeof(rSAKc1) }, // Acknowledge select - cascade 1 { .response = rSAKc2, .response_n = sizeof(rSAKc2) }, // Acknowledge select - cascade 2 { .response = rSAKc3, .response_n = sizeof(rSAKc3) }, // Acknowledge select - cascade 3 - { .response = rRATS, .response_n = sizeof(rRATS) }, // dummy ATS (pseudo-ATR), answer to RATS + { .response = rATS, .response_n = sizeof(rATS) }, // dummy ATS (pseudo-ATR), answer to RATS { .response = rVERSION, .response_n = sizeof(rVERSION) }, // EV1/NTAG GET_VERSION response { .response = rSIGN, .response_n = sizeof(rSIGN) }, // EV1/NTAG READ_SIG response { .response = rPPS, .response_n = sizeof(rPPS) }, // PPS response @@ -1415,7 +1416,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, }; // since rats len is variable now. - responses_init[RESP_INDEX_RATS].response_n = rRATS_len; + responses_init[RESP_INDEX_ATS].response_n = rATS_len; // "precompiled" responses. // These exist for speed reasons. There are no time in the anti collision phase to calculate responses. @@ -1428,7 +1429,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // 85 bytes normally (rats = 8 bytes) // 77 bytes + ratslen, -#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rRATS_len) * 8) + 77 + rRATS_len + 12 + 12 + 12) +#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rATS_len) * 8) + 77 + rATS_len + 12 + 12 + 12) uint8_t *free_buffer = BigBuf_calloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // modulation buffer pointer and current buffer free space size @@ -1454,8 +1455,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // response to send, and send it. // 'hf 14a sim' //----------------------------------------------------------------------------- -void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads, - uint8_t *iRATs, size_t irats_len) { +void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, + uint8_t *ats, size_t ats_len) { #define ATTACK_KEY_COUNT 16 @@ -1489,7 +1490,17 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ #define DYNAMIC_MODULATION_BUFFER_SIZE 512 uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE); + if (dynamic_response_buffer == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } uint8_t *dynamic_modulation_buffer = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER_SIZE); + if (dynamic_modulation_buffer == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } tag_response_info_t dynamic_response_info = { .response = dynamic_response_buffer, .response_n = 0, @@ -1497,7 +1508,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, data, iRATs, irats_len, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len, &responses, &cuid, counters, tearings, &pages) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; @@ -1671,10 +1682,10 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ EmSend4bit(CARD_NACK_IV); } else { // first blocks of emu are header - uint16_t start = block * 4 + MFU_DUMP_PREFIX_LENGTH; - uint8_t emdata[MAX_MIFARE_FRAME_SIZE]; - emlGet(emdata, start, 16); - AddCrc14A(emdata, 16); + uint16_t start = (block * 4) + MFU_DUMP_PREFIX_LENGTH; + uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0}; + emlGet(emdata, start, MIFARE_BLOCK_SIZE); + AddCrc14A(emdata, MIFARE_BLOCK_SIZE); EmSendCmd(emdata, sizeof(emdata)); numReads++; // Increment number of times reader requested a block @@ -1692,8 +1703,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ p_response = &responses[RESP_INDEX_UIDC1]; } else { // all other tags (16 byte block tags) uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0}; - emlGet(emdata, block, 16); - AddCrc14A(emdata, 16); + emlGet(emdata, block, MIFARE_BLOCK_SIZE); + AddCrc14A(emdata, MIFARE_BLOCK_SIZE); EmSendCmd(emdata, sizeof(emdata)); // We already responded, do not send anything with the EmSendCmd14443aRaw() that is called below p_response = NULL; @@ -1717,13 +1728,14 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE // cmd + block + 4 bytes data + 2 bytes crc if (CheckCrc14A(receivedCmd, len)) { + uint8_t block = receivedCmd[1]; if (block > pages) { // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { // first blocks of emu are header - emlSetMem_xt(&receivedCmd[2], block + MFU_DUMP_PREFIX_LENGTH / 4, 1, 4); + emlSetMem_xt(&receivedCmd[2], block + (MFU_DUMP_PREFIX_LENGTH / 4), 1, 4); // send ACK EmSend4bit(CARD_ACK); } @@ -1820,7 +1832,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ EmSend4bit(CARD_NACK_NA); p_response = NULL; } else { - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; } } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1) { // ULC authentication, or Desfire Authentication LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); @@ -1836,7 +1848,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ } if (memcmp(pwd, "\x00\x00\x00\x00", 4) == 0) { - Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(data)); + Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(useruid)); if (g_dbglevel >= DBG_DEBUG) Dbprintf("Calc pwd... %02X %02X %02X %02X", pwd[0], pwd[1], pwd[2], pwd[3]); } @@ -3941,9 +3953,10 @@ It can also continue after the AID has been selected, and respond to other reque This was forked from the original function to allow for more flexibility in the future, and to increase the processing speed of the original function. /// */ -void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, - uint8_t *iRATs, size_t irats_len, uint8_t *aid, uint8_t *resp, - uint8_t *apdu, int aidLen, int respondLen, int apduLen, bool enumerate) { +void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, + uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len, + uint8_t *selectaid_response, size_t selectaid_response_len, + uint8_t *getdata_response, size_t getdata_response_len) { tag_response_info_t *responses; uint32_t cuid = 0; uint32_t counters[3] = { 0x00, 0x00, 0x00 }; @@ -3954,6 +3967,12 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + // Buffers must be provided by the caller, even if lengths are 0 + // Copy the AID, AID Response, and the GetData APDU response into our variables + if ((aid == NULL) || (selectaid_response == NULL) || (getdata_response == NULL)) { + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINVARG, NULL, 0); + } + // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); @@ -3962,7 +3981,17 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, #define DYNAMIC_MODULATION_BUFFER2_SIZE 1536 uint8_t *dynamic_response_buffer2 = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER2_SIZE); + if (dynamic_response_buffer2 == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } uint8_t *dynamic_modulation_buffer2 = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER2_SIZE); + if (dynamic_modulation_buffer2 == NULL) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } tag_response_info_t dynamic_response_info = { .response = dynamic_response_buffer2, .response_n = 0, @@ -3970,7 +3999,7 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, data, iRATs, irats_len, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, counters, tearings, &pages) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; @@ -3990,25 +4019,9 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, set_tracing(true); LED_A_ON(); - // Filters for when this comes through - static uint8_t aidFilter[30] = { 0x00 }; // Default AID Value - static uint8_t aidResponse[100] = { 0x00 }; // Default AID Response - static uint8_t apduCommand [100] = { 0x00 }; // Default APDU GetData Response - - // Copy the AID, AID Response, and the GetData APDU response into our variables - if (aid != 0) { - memcpy(aidFilter, aid, aidLen); - } - if (resp != 0) { - memcpy(aidResponse, resp, respondLen); - } - if (apdu != 0) { - memcpy(apduCommand, apdu, apduLen); - } - - // main loop bool finished = false; + bool got_rats = false; while (finished == false) { // BUTTON_PRESS check done in GetIso14443aCommandFromReader WDT_HIT(); @@ -4046,24 +4059,32 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); p_response = NULL; - finished = true; + if (got_rats) { + finished = true; + } } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request - p_response = &responses[RESP_INDEX_RATS]; + p_response = &responses[RESP_INDEX_ATS]; + got_rats = true; } else { // clear old dynamic responses dynamic_response_info.response_n = 0; dynamic_response_info.modulation_n = 0; - // Check for ISO 14443A-4 compliant commands, look at left nibble + // Check for ISO 14443A-4 compliant commands, look at left byte (PCB) + uint8_t offset = 0; switch (receivedCmd[0]) { - case 0x0B: - case 0x0A: { // IBlock (command CID) + case 0x0B: // IBlock with CID + case 0x0A: + offset = 1; + case 0x02: // IBlock without CID + case 0x03: { dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x00; - switch (receivedCmd[3]) { // APDU Class Byte - // receivedCmd in this case is expecting to structured with a CID, then the APDU command for SelectFile - // | IBlock (CID) | CID | APDU Command | CRC | + switch (receivedCmd[2 + offset]) { // APDU Class Byte + // receivedCmd in this case is expecting to structured with possibly a CID, then the APDU command for SelectFile + // | IBlock (CID) | CID | APDU Command | CRC | + // or | IBlock (noCID) | APDU Command | CRC | case 0xA4: { // SELECT FILE // Select File AID uses the following format for GlobalPlatform @@ -4072,40 +4093,40 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, // xx in this case is len of the AID value in hex // aid len is found as a hex value in receivedCmd[6] (Index Starts at 0) - int aid_len = receivedCmd[6]; - uint8_t *received_aid = &receivedCmd[7]; + int received_aid_len = receivedCmd[5 + offset]; + uint8_t *received_aid = &receivedCmd[6 + offset]; // aid enumeration flag - if (enumerate == true) { - Dbprintf("Received AID (%d):", aid_len); - Dbhexdump(aid_len, received_aid, false); + if ((flags & FLAG_ENUMERATE_AID) == FLAG_ENUMERATE_AID) { + Dbprintf("Received AID (%d):", received_aid_len); + Dbhexdump(received_aid_len, received_aid, false); } - if (memcmp(aidFilter, received_aid, aid_len) == 0) { // Evaluate the AID sent by the Reader to the AID supplied + if ((received_aid_len == aid_len) && (memcmp(aid, received_aid, aid_len) == 0)) { // Evaluate the AID sent by the Reader to the AID supplied // AID Response will be parsed here - memcpy(dynamic_response_info.response + 2, aidResponse, respondLen + 2); - dynamic_response_info.response_n = respondLen + 2; + memcpy(dynamic_response_info.response + 1 + offset, selectaid_response, selectaid_response_len + 1 + offset); + dynamic_response_info.response_n = selectaid_response_len + 2; } else { // Any other SELECT FILE command will return with a Not Found - dynamic_response_info.response[2] = 0x6A; - dynamic_response_info.response[3] = 0x82; - dynamic_response_info.response_n = 4; + dynamic_response_info.response[1 + offset] = 0x6A; + dynamic_response_info.response[2 + offset] = 0x82; + dynamic_response_info.response_n = 3 + offset; } } break; case 0xDA: { // PUT DATA // Just send them a 90 00 response - dynamic_response_info.response[2] = 0x90; - dynamic_response_info.response[3] = 0x00; - dynamic_response_info.response_n = 4; + dynamic_response_info.response[1 + offset] = 0x90; + dynamic_response_info.response[2 + offset] = 0x00; + dynamic_response_info.response_n = 3 + offset; } break; case 0xCA: { // GET DATA if (sentCount == 0) { // APDU Command will just be parsed here - memcpy(dynamic_response_info.response + 2, apduCommand, apduLen + 2); - dynamic_response_info.response_n = respondLen + 2; + memcpy(dynamic_response_info.response + 1 + offset, getdata_response, getdata_response_len + 2); + dynamic_response_info.response_n = selectaid_response_len + 1 + offset; } else { finished = true; break; @@ -4116,18 +4137,18 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, default : { // Any other non-listed command // Respond Not Found - dynamic_response_info.response[2] = 0x6A; - dynamic_response_info.response[3] = 0x82; - dynamic_response_info.response_n = 4; + dynamic_response_info.response[1 + offset] = 0x6A; + dynamic_response_info.response[2 + offset] = 0x82; + dynamic_response_info.response_n = 3 + offset; } } break; } break; - case 0xCA: - case 0xC2: { // Readers sends deselect command - dynamic_response_info.response[0] = 0xCA; + case 0xCA: // S-Block Deselect with CID + case 0xC2: { // S-Block Deselect without CID + dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x00; dynamic_response_info.response_n = 2; finished = true; @@ -4135,12 +4156,15 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, break; default: { - // Never seen this command before + // Never seen this PCB before LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); if (g_dbglevel >= DBG_DEBUG) { Dbprintf("Received unknown command (len=%d):", len); Dbhexdump(len, receivedCmd, false); } + if ((receivedCmd[0] & 0x10) == 0x10) { + Dbprintf("Warning, reader sent a chained command but we lack support for it. Ignoring command."); + } // Do not respond dynamic_response_info.response_n = 0; } @@ -4149,13 +4173,15 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, if (dynamic_response_info.response_n > 0) { // Copy the CID from the reader query - dynamic_response_info.response[1] = receivedCmd[1]; + if (offset > 0) { + dynamic_response_info.response[1] = receivedCmd[1]; + } // Add CRC bytes, always used in ISO 14443A-4 compliant cards AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n); dynamic_response_info.response_n += 2; - if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) { + if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER2_SIZE) == false) { if (g_dbglevel >= DBG_DEBUG) DbpString("Error preparing tag response"); LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); break; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 24b388252..7c9eed81b 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -105,7 +105,7 @@ typedef enum { RESP_INDEX_SAKC1, RESP_INDEX_SAKC2, RESP_INDEX_SAKC3, - RESP_INDEX_RATS, + RESP_INDEX_ATS, RESP_INDEX_VERSION, RESP_INDEX_SIGNATURE, RESP_INDEX_PPS, @@ -142,12 +142,13 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time); RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time); void RAMFUNC SniffIso14443a(uint8_t param); -void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads, +void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, uint8_t *iRATs, size_t irats_len); -void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, - uint8_t *iRATs, size_t irats_len, uint8_t *aid, uint8_t *resp, - uint8_t *apdu, int aid_len, int respond_len, int apdu_len, bool enumerate); +void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, + uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len, + uint8_t *selectaid_response, size_t selectaid_response_len, + uint8_t *getdata_response, size_t getdata_response_len); bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *iRATs, size_t irats_len, tag_response_info_t **responses, diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index cbf9e8f88..df4f06230 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -2117,14 +2117,6 @@ void Iso15693InitTag(void) { StartCountSspClk(); } -void EmlClearIso15693(void) { - // Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent - // an inconvenient reset in the future by Iso15693InitTag - FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); - BigBuf_Clear_EM(); - reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0); -} - // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg void SimTagIso15693(const uint8_t *uid, uint8_t block_size) { @@ -2775,10 +2767,10 @@ void LockPassSlixIso15693(uint32_t pass_id, uint32_t password) { LED_A_ON(); uint8_t cmd_inventory[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1, 0x01, 0x00, 0x00, 0x00 }; - uint8_t cmd_get_rnd[] = {ISO15693_REQ_DATARATE_HIGH, 0xB2, 0x04, 0x00, 0x00 }; - uint8_t cmd_set_pass[] = {ISO15693_REQ_DATARATE_HIGH, 0xB3, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - //uint8_t cmd_write_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, 0xB4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t cmd_lock_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, 0xB5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }; + uint8_t cmd_get_rnd[] = {ISO15693_REQ_DATARATE_HIGH, ISO15693_GET_RANDOM_NUMBER, 0x04, 0x00, 0x00 }; + uint8_t cmd_set_pass[] = {ISO15693_REQ_DATARATE_HIGH, ISO15693_SET_PASSWORD, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + //uint8_t cmd_write_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, ISO15693_WRITE_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t cmd_lock_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, ISO15693_LOCK_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }; uint16_t crc; uint16_t recvlen = 0; uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; @@ -3028,14 +3020,7 @@ static uint32_t disable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_ti return PM3_SUCCESS; } -static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid) { - - - uint8_t rnd[2]; - if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { - return PM3_ETIMEOUT; - } - +static uint32_t set_pass_15693_SlixRnd(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid, uint8_t *rnd) { // 0x04, == NXP from manufacture id list. uint8_t c[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_SET_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, pass_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -3055,6 +3040,18 @@ static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uin return PM3_SUCCESS; } +static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid) { + + + uint8_t rnd[2]; + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { + return PM3_ETIMEOUT; + } + + return set_pass_15693_SlixRnd(start_time, eof_time, pass_id, password, uid, rnd); +} + + static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, const uint8_t *password) { uint8_t rnd[2]; if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { @@ -3062,7 +3059,7 @@ static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, } // 0x04, == NXP from manufacture id list. - uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xBA, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_ENABLE_PRIVACY, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; init_password_15693_Slix(&c[3], password, rnd); AddCrc15(c, 7); @@ -3096,7 +3093,7 @@ static uint32_t disable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, } // 0x04, == NXP from manufacture id list. - uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xA3, 0x04, 0x00, 0x00}; + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_RESET_EAS, 0x04, 0x00, 0x00}; AddCrc15(c, 3); start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -3127,7 +3124,7 @@ static uint32_t enable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, c } } // 0x04, == NXP from manufacture id list. - uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xA2, 0x04, 0x00, 0x00}; + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_SET_EAS, 0x04, 0x00, 0x00}; //init_password_15693_Slix(&c[3], password, rnd); AddCrc15(c, 3); @@ -3162,6 +3159,26 @@ static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_tim return PM3_SUCCESS; } +static uint32_t protect_page_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t divide_ptr, uint8_t prot_status, const uint8_t *uid) { + + uint8_t protect_cmd[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_PROTECT_PAGE, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, divide_ptr, prot_status, 0x00, 0x00}; + + memcpy(&protect_cmd[3], uid, 8); + + AddCrc15(protect_cmd, 13); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + + int res_wrp = SendDataTag(protect_cmd, sizeof(protect_cmd), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res_wrp != PM3_SUCCESS && recvlen != 3) { + return PM3_EWRONGANSWER; + } + + return PM3_SUCCESS; +} + static uint32_t pass_protect_EASAFI_15693_Slix(uint32_t start_time, uint32_t *eof_time, bool set_option_flag, const uint8_t *password) { uint8_t flags; @@ -3262,6 +3279,37 @@ void WritePasswordSlixIso15693(const uint8_t *old_password, const uint8_t *new_p } +void ProtectPageSlixIso15693(const uint8_t *read_password, const uint8_t *write_password, uint8_t divide_ptr, uint8_t prot_status) { + LED_D_ON(); + Iso15693InitReader(); + StartCountSspClk(); + uint32_t start_time = 0, eof_time = 0; + int res = PM3_SUCCESS; + + uint8_t uid[8], rnd[2]; + get_uid_slix(start_time, &eof_time, uid); + + if (get_rnd_15693_Slix(start_time, &eof_time, rnd) == false) { + reply_ng(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, PM3_ETIMEOUT, NULL, 0); + switch_off(); + return; + } + + if (read_password) + res = set_pass_15693_SlixRnd(start_time, &eof_time, 0x01, read_password, uid, rnd); + + if (res == PM3_SUCCESS && write_password) + res = set_pass_15693_SlixRnd(start_time, &eof_time, 0x02, write_password, uid, rnd); + + if (res == PM3_SUCCESS) + res = protect_page_15693_Slix(start_time, &eof_time, divide_ptr, prot_status, uid); + + reply_ng(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, res, NULL, 0); + + switch_off(); + +} + void DisablePrivacySlixIso15693(const uint8_t *password) { LED_D_ON(); Iso15693InitReader(); diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 0f04fddf9..a4f633252 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -46,7 +46,6 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo //void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(iso15_card_select_t *p_card); // ISO15693 reader -void EmlClearIso15693(void); void SimTagIso15693(const uint8_t *uid, uint8_t block_size); // simulate an ISO15693 tag void BruteforceIso15693Afi(uint32_t flags); // find an AFI of a tag void SendRawCommand15693(iso15_raw_cmd_t *packet); // send arbitrary commands from CLI @@ -69,4 +68,5 @@ void EnableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd); void PassProtextEASSlixIso15693(const uint8_t *password); void PassProtectAFISlixIso15693(const uint8_t *password); void WriteAFIIso15693(const uint8_t *password, bool use_pwd, uint8_t *uid, bool use_uid, uint8_t afi); +void ProtectPageSlixIso15693(const uint8_t *read_password, const uint8_t *write_password, uint8_t divide_ptr, uint8_t prot_status); #endif diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 0716efc60..46c1e0d3f 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -944,6 +944,33 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, int16_t *remainder) { } } +bool add_HID_preamble(uint32_t *hi2, uint32_t *hi, uint32_t *lo, uint8_t length) { + // Invalid value + if (length > 84 || length == 0) + return false; + + if (length == 48) { + *hi |= 1U << (length - 32); // Example leading 1: start bit + return true; + } + if (length >= 64) { + *hi2 |= 0x09e00000; // Extended-length header + *hi2 |= 1U << (length - 64); // leading 1: start bit + } else if (length > 37) { + *hi2 |= 0x09e00000; // Extended-length header + *hi |= 1U << (length - 32); // leading 1: start bit + } else if (length == 37) { + // No header bits added to 37-bit cards + } else if (length >= 32) { + *hi |= 0x20; // Bit 37; standard header + *hi |= 1U << (length - 32); // leading 1: start bit + } else { + *hi |= 0x20; // Bit 37; standard header + *lo |= 1U << length; // leading 1: start bit + } + return true; +} + // prepare a waveform pattern in the buffer based on the ID given then // simulate a HID tag until the button is pressed void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol, int numcycles) { @@ -968,13 +995,7 @@ void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, boo uint16_t n = 8; if (longFMT) { - // Ensure no more than 84 bits supplied - if (hi2 > 0xFFFFF) { - DbpString("Tags can only have 84 bits."); - return; - } bitlen = 8 + 8 * 2 + 84 * 2; - hi2 |= 0x9E00000; // 9E: long format identifier manchesterEncodeUint32(hi2, 16 + 12, bits, &n); manchesterEncodeUint32(hi, 32, bits, &n); manchesterEncodeUint32(lo, 32, bits, &n); @@ -1316,10 +1337,6 @@ int lf_hid_watch(int findone, uint32_t *high, uint32_t *low, bool ledcontrol) { cardnum = (lo >> 1) & 0xFFFF; fac = (lo >> 17) & 0xFF; } - if (bitlen == 37) { - cardnum = (lo >> 1) & 0x7FFFF; - fac = ((hi & 0xF) << 12) | (lo >> 20); - } if (bitlen == 34) { cardnum = (lo >> 1) & 0xFFFF; fac = ((hi & 1) << 15) | (lo >> 17); @@ -2274,15 +2291,10 @@ void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, boo uint8_t last_block = 0; if (longFMT) { - // Ensure no more than 84 bits supplied - if (hi2 > 0xFFFFF) { - DbpString("Tags can only have 84 bits"); - return; - } // Build the 6 data blocks for supplied 84bit ID last_block = 6; - // load preamble (1D) & long format identifier (9E manchester encoded) - data[1] = 0x1D96A900 | (manchesterEncode2Bytes((hi2 >> 16) & 0xF) & 0xFF); + // load preamble (1D) + data[1] = 0x1D000000 | (manchesterEncode2Bytes((hi2 >> 16) & 0xFFFF) & 0xFFFFFF); // load raw id from hi2, hi, lo to data blocks (manchester encoded) data[2] = manchesterEncode2Bytes(hi2 & 0xFFFF); data[3] = manchesterEncode2Bytes(hi >> 16); diff --git a/armsrc/lfops.h b/armsrc/lfops.h index c3f00b4af..5a25a74d8 100644 --- a/armsrc/lfops.h +++ b/armsrc/lfops.h @@ -34,6 +34,7 @@ void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycl void SimulateTagLowFrequency(int period, int gap, bool ledcontrol); void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen); +bool add_HID_preamble(uint32_t *hi2, uint32_t *hi, uint32_t *lo, uint8_t length); void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol, int numcycles); void CmdHIDsimTAG(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 8e9d93bc7..a2aa2450d 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -2223,7 +2223,7 @@ OUT: blockno = (32 * 4 + (i - 32) * 16) ^ 0xF; } // get ST - emlGetMem(block, blockno, 1); + emlGetMem_xt(block, blockno, 1, MIFARE_BLOCK_SIZE); memcpy(block, k_sector[i].keyA, 6); memcpy(block + 10, k_sector[i].keyB, 6); @@ -2376,8 +2376,8 @@ void MifareChkKeys_file(uint8_t *fn) { void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { uint16_t isOK = PM3_EUNDEF; - uint8_t uid[10]; - uint32_t cuid; + uint8_t uid[10] = { 0 }; + uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; @@ -2388,8 +2388,12 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { LED_A_ON(); + uint8_t rec_answer[MAX_MIFARE_FRAME_SIZE] = {0}; + uint8_t rec_answer_par[MAX_MIFARE_PARITY_SIZE] = {0}; + while (true) { - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); break; } @@ -2400,11 +2404,9 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { break; } - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; - int len = mifare_sendcmd_short(pcs, true, MIFARE_EV1_PERSONAL_UID, perso_option, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); - if (len != 1 || receivedAnswer[0] != CARD_ACK) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + int len = mifare_sendcmd_short(pcs, true, MIFARE_EV1_PERSONAL_UID, perso_option, rec_answer, sizeof(rec_answer), rec_answer_par, NULL); + if (len != 1 || rec_answer[0] != CARD_ACK) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", rec_answer[0]); break; } @@ -2427,39 +2429,6 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { } -//----------------------------------------------------------------------------- -// Work with emulator memory -// -// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not -// involved in dealing with emulator memory. But if it is called later, it might -// destroy the Emulator Memory. -//----------------------------------------------------------------------------- - -void MifareEMemClr(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - emlClearMem(); -} - -void MifareEMemGet(uint8_t blockno, uint8_t blockcnt) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - // - size_t size = blockcnt * 16; - if (size > PM3_CMD_DATA_SIZE) { - reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0); - return; - } - - uint8_t *buf = BigBuf_malloc(size); - - emlGetMem(buf, blockno, blockcnt); // data, block num, blocks count (max 4) - - LED_B_ON(); - reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size); - LED_B_OFF(); - BigBuf_free_keep_EM(); -} - //----------------------------------------------------------------------------- // Load a card into the emulator memory // @@ -2471,12 +2440,15 @@ int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { } int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { + if ((keytype > MF_KEY_B) && (key == NULL)) { + if (g_dbglevel >= DBG_ERROR) { Dbprintf("Error, missing key"); } return PM3_EINVARG; } + LED_A_ON(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -2510,10 +2482,10 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { // MFC 1K EV1, skip sector 16 since its lockdown if (s == 16) { // unknown sector trailer, keep the keys, set only the AC - uint8_t st[16] = {0x00}; - emlGetMem(st, FirstBlockOfSector(s) + 3, 1); + uint8_t st[MIFARE_BLOCK_SIZE] = {0x00}; + emlGetMem_xt(st, FirstBlockOfSector(s) + 3, 1, MIFARE_BLOCK_SIZE); memcpy(st + 6, "\x70\xF0\xF8\x69", 4); - emlSetMem_xt(st, FirstBlockOfSector(s) + 3, 1, 16); + emlSetMem_xt(st, FirstBlockOfSector(s) + 3, 1, MIFARE_BLOCK_SIZE); continue; } @@ -2556,7 +2528,8 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (!bd_authenticated) { // no need to select if bd_authenticated with backdoor + + if (bd_authenticated == false) { // no need to select if bd_authenticated with backdoor if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { continue; } @@ -2565,7 +2538,7 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { // Auth if (keytype > MF_KEY_B) { - if (! bd_authenticated) { + if (bd_authenticated == false) { ui64Key = bytes_to_num(key, 6); if (mifare_classic_auth(pcs, cuid, 0, keytype, ui64Key, AUTH_FIRST)) { retval = PM3_EFAILED; @@ -2592,7 +2565,7 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { #define MAX_RETRIES 2 - uint8_t data[16] = {0x00}; + uint8_t data[MIFARE_BLOCK_SIZE] = {0x00}; for (uint8_t b = 0; b < NumBlocksPerSector(s); b++) { memset(data, 0x00, sizeof(data)); @@ -2614,18 +2587,18 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) { } // No need to copy empty - if (memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0) { + if (memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", sizeof(data)) == 0) { break; } if (IsSectorTrailer(b)) { // sector trailer, keep the keys, set only the AC - uint8_t st[16] = {0x00}; - emlGetMem(st, tb, 1); + uint8_t st[MIFARE_BLOCK_SIZE] = {0x00}; + emlGetMem_xt(st, tb, 1, MIFARE_BLOCK_SIZE); memcpy(st + 6, data + 6, 4); - emlSetMem_xt(st, tb, 1, 16); + emlSetMem_xt(st, tb, 1, MIFARE_BLOCK_SIZE); } else { - emlSetMem_xt(data, tb, 1, 16); + emlSetMem_xt(data, tb, 1, MIFARE_BLOCK_SIZE); } break; } @@ -2927,7 +2900,6 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { // variables uint8_t rec[1] = {0x00}; uint8_t recpar[1] = {0x00}; - uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73}; uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; uint8_t gen4gdmAuth[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92}; @@ -2940,6 +2912,8 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE); uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE); uint8_t *uid = BigBuf_calloc(10); + iso14a_card_select_t *card = (iso14a_card_select_t *) BigBuf_calloc(sizeof(iso14a_card_select_t)); + uint16_t flag = MAGIC_FLAG_NONE; uint32_t cuid = 0; int res = 0; @@ -2991,144 +2965,141 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { // reset card mf_reset_card(); - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, false); + res = iso14443a_select_card(uid, card, &cuid, true, 0, false); if (res) { if (cuid == 0xAA55C396) { flag |= MAGIC_FLAG_GEN_UNFUSED; } - ReaderTransmit(rats, sizeof(rats), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (memcmp(card->ats, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10", 9) == 0) { + // test for some MFC gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) { + // test for some MFC 7b gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) { + // test for Ultralight magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) { + // test for Ultralight EV1 magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) { + // test for some other Ultralight EV1 magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) { + // test for some other Ultralight magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } else if (memcmp(card->ats, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) { + // test for NTAG213 magic gen2 + isGen2 = true; + flag |= MAGIC_FLAG_GEN_2; + } - if (res) { - if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10", 9) == 0) { - // test for some MFC gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) { - // test for some MFC 7b gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) { - // test for Ultralight magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) { - // test for Ultralight EV1 magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) { - // test for some other Ultralight EV1 magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) { - // test for some other Ultralight magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; - } else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) { - // test for NTAG213 magic gen2 - isGen2 = true; - flag |= MAGIC_FLAG_GEN_2; + // test for super card + ReaderTransmit(superGen1, sizeof(superGen1), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res == 22) { + uint8_t isGen = MAGIC_FLAG_SUPER_GEN1; + + // check for super card gen2 + // not available after RATS, reset card before executing + mf_reset_card(); + + iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res == 18) { + isGen = MAGIC_FLAG_SUPER_GEN2; } - // test for super card - ReaderTransmit(superGen1, sizeof(superGen1), NULL); + flag |= isGen; + } + } + + if (is_mfc == false) { + // magic ntag test + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res == 2) { + ReaderTransmit(rdblf0, sizeof(rdblf0), NULL); res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 22) { - uint8_t isGen = MAGIC_FLAG_SUPER_GEN1; - - // check for super card gen2 - // not available after RATS, reset card before executing - mf_reset_card(); - - iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 18) { - isGen = MAGIC_FLAG_SUPER_GEN2; - } - - flag |= isGen; + if (res == 18) { + flag |= MAGIC_FLAG_NTAG21X; } } - if (is_mfc == false) { - // magic ntag test + } else { + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + // CUID (with default sector 0 B key) test + // regular cards will NAK the WRITEBLOCK(0) command, while DirectWrite will ACK it + // if we do get an ACK, we immediately abort to ensure nothing is ever actually written + // only perform test if we haven't already identified Gen2. No need test if we have a positive identification already + if (isGen2 == false) { mf_reset_card(); res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res == 2) { - ReaderTransmit(rdblf0, sizeof(rdblf0), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 18) { - flag |= MAGIC_FLAG_NTAG21X; - } - } - } else { + if (res) { - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; + uint64_t tmpkey = bytes_to_num(key, 6); + if (mifare_classic_authex(pcs, cuid, 0, keytype, tmpkey, AUTH_FIRST, NULL, NULL) == 0) { - // CUID (with default sector 0 B key) test - // regular cards will NAK the WRITEBLOCK(0) command, while DirectWrite will ACK it - // if we do get an ACK, we immediately abort to ensure nothing is ever actually written - // only perform test if we haven't already identified Gen2. No need test if we have a positive identification already - if (isGen2 == false) { - mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res) { - - uint64_t tmpkey = bytes_to_num(key, 6); - if (mifare_classic_authex(pcs, cuid, 0, keytype, tmpkey, AUTH_FIRST, NULL, NULL) == 0) { - - if ((mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, 0, buf, PM3_CMD_DATA_SIZE, par, NULL) == 1) && (buf[0] == 0x0A)) { - flag |= MAGIC_FLAG_GEN_2; - // turn off immediately to ensure nothing ever accidentally writes to the block - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - } + if ((mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, 0, buf, PM3_CMD_DATA_SIZE, par, NULL) == 1) && (buf[0] == 0x0A)) { + flag |= MAGIC_FLAG_GEN_2; + // turn off immediately to ensure nothing ever accidentally writes to the block + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); } - crypto1_deinit(pcs); - } - } - - // magic MFC Gen3 test 1 - mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res) { - ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 18) { - flag |= MAGIC_FLAG_GEN_3; - } - } - - // magic MFC Gen4 GDM magic auth test - mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res) { - ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL); - res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); - if (res == 4) { - flag |= MAGIC_FLAG_GDM_AUTH; - } - } - - // QL88 test - mf_reset_card(); - - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res) { - if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) { - flag |= MAGIC_FLAG_QL88; } crypto1_deinit(pcs); } } - }; + + // magic MFC Gen3 test 1 + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res == 18) { + flag |= MAGIC_FLAG_GEN_3; + } + } + + // magic MFC Gen4 GDM magic auth test + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL); + res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par); + if (res == 4) { + flag |= MAGIC_FLAG_GDM_AUTH; + } + } + + // QL88 test + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + + if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) { + flag |= MAGIC_FLAG_QL88; + } + crypto1_deinit(pcs); + } + } // GDM alt magic wakeup (20) ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 2dcfa4e4b..f6d190d82 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -43,8 +43,6 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem); void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareChkKeys_file(uint8_t *fn); -void MifareEMemClr(void); -void MifareEMemGet(uint8_t blockno, uint8_t blockcnt); int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key); int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype, uint8_t *key); diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 65f18bdac..c58025473 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -48,8 +48,8 @@ #include "parity.h" static bool IsKeyBReadable(uint8_t blockNo) { - uint8_t sector_trailer[16]; - emlGetMem(sector_trailer, SectorTrailer(blockNo), 1); + uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0}; + emlGetMem_xt(sector_trailer, SectorTrailer(blockNo), 1, MIFARE_BLOCK_SIZE); uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | ((sector_trailer[8] >> 7) & 0x01); @@ -57,55 +57,64 @@ static bool IsKeyBReadable(uint8_t blockNo) { } static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { - uint8_t sector_trailer[16]; - emlGetMem(sector_trailer, blockNo, 1); + uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0}; + emlGetMem_xt(sector_trailer, blockNo, 1, MIFARE_BLOCK_SIZE); + uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | ((sector_trailer[8] >> 7) & 0x01); + switch (action) { case AC_KEYA_READ: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_KEYA_READ"); + } return false; } case AC_KEYA_WRITE: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_KEYA_WRITE"); + } return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); } case AC_KEYB_READ: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_KEYB_READ"); + } return (keytype == AUTHKEYA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); } case AC_KEYB_WRITE: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_KEYB_WRITE"); + } return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); } case AC_AC_READ: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_AC_READ"); + } return ((keytype == AUTHKEYA) || (keytype == AUTHKEYB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); } case AC_AC_WRITE: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsTrailerAccessAllowed: AC_AC_WRITE"); + } return ((keytype == AUTHKEYA && (AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x03 || AC == 0x05))); } - default: + default: { return false; + } } } static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { - uint8_t sector_trailer[16]; - emlGetMem(sector_trailer, SectorTrailer(blockNo), 1); + uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0}; + emlGetMem_xt(sector_trailer, SectorTrailer(blockNo), 1, MIFARE_BLOCK_SIZE); uint8_t sector_block; if (blockNo <= MIFARE_2K_MAXBLOCK) { @@ -120,54 +129,62 @@ static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action AC = ((sector_trailer[7] >> 2) & 0x04) | ((sector_trailer[8] << 1) & 0x02) | ((sector_trailer[8] >> 4) & 0x01); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed: case 0x00 - %02x", AC); + } break; } case 0x01: { AC = ((sector_trailer[7] >> 3) & 0x04) | ((sector_trailer[8] >> 0) & 0x02) | ((sector_trailer[8] >> 5) & 0x01); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed: case 0x01 - %02x", AC); + } break; } case 0x02: { AC = ((sector_trailer[7] >> 4) & 0x04) | ((sector_trailer[8] >> 1) & 0x02) | ((sector_trailer[8] >> 6) & 0x01); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed: case 0x02 - %02x", AC); + } break; } default: - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed: Error"); + } return false; } switch (action) { case AC_DATA_READ: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed - AC_DATA_READ: OK"); + } return ((keytype == AUTHKEYA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) || (keytype == AUTHKEYB && !(AC == 0x07))); } case AC_DATA_WRITE: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed - AC_DATA_WRITE: OK"); + } return ((keytype == AUTHKEYA && (AC == 0x00)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); } case AC_DATA_INC: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("IsDataAccessAllowed - AC_DATA_INC: OK"); + } return ((keytype == AUTHKEYA && (AC == 0x00)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06))); } case AC_DATA_DEC_TRANS_REST: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("AC_DATA_DEC_TRANS_REST: OK"); + } return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); } @@ -252,29 +269,33 @@ bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag // Length: 4,7,or 10 bytes if (IS_FLAG_UID_IN_EMUL(flags)) { + if (uid == NULL) { uid = uid_tmp; } // Get UID, SAK, ATQA from EMUL - uint8_t block0[16]; - emlGet(block0, 0, 16); + uint8_t block0[MIFARE_BLOCK_SIZE]; + emlGet(block0, 0, MIFARE_BLOCK_SIZE); + // Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA if ((block0[0] ^ block0[1] ^ block0[2] ^ block0[3]) == block0[4] && (block0[6] & 0xc0) == 0) { FLAG_SET_UID_IN_DATA(flags, 4); memcpy(uid, block0, 4); rSAK[0] = block0[5]; memcpy(rATQA, &block0[6], sizeof(rATQA)); - } - // Check for 7 bytes UID: double size uid bits in ATQA - else if ((block0[8] & 0xc0) == 0x40) { + + } else if ((block0[8] & 0xc0) == 0x40) { + // Check for 7 bytes UID: double size uid bits in ATQA FLAG_SET_UID_IN_DATA(flags, 7); memcpy(uid, block0, 7); rSAK[0] = block0[7]; memcpy(rATQA, &block0[8], sizeof(rATQA)); + } else { Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found")); return false; } + } else { if (uid == NULL) { Dbprintf("ERROR: " _RED_("Missing UID")); @@ -288,16 +309,19 @@ bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag memcpy(rATQA, rATQA_Mini, sizeof(rATQA)); rSAK[0] = rSAK_Mini; if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare Mini ATQA/SAK"); + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_1K_MAX_BYTES)) { memcpy(rATQA, rATQA_1k, sizeof(rATQA)); rSAK[0] = rSAK_1k; if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 1K ATQA/SAK"); + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_2K_MAX_BYTES)) { memcpy(rATQA, rATQA_2k, sizeof(rATQA)); rSAK[0] = rSAK_2k; *rats = rRATS; *rats_len = sizeof(rRATS); if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 2K ATQA/SAK with RATS support"); + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_4K_MAX_BYTES)) { memcpy(rATQA, rATQA_4k, sizeof(rATQA)); rSAK[0] = rSAK_4k; @@ -825,8 +849,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t // if key not known and FLAG_NESTED_AUTH_ATTACK and we have nt/nt_enc/parity, send recorded nt_enc and parity if ((flags & FLAG_NESTED_AUTH_ATTACK) == FLAG_NESTED_AUTH_ATTACK) { if (emlGetKey(cardAUTHSC, cardAUTHKEY) == 0) { - uint8_t buf[16] = {0}; - emlGetMem(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + cardAUTHSC, 1); + uint8_t buf[MIFARE_BLOCK_SIZE] = {0}; + emlGetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + cardAUTHSC, 1, MIFARE_BLOCK_SIZE); if (buf[(cardAUTHKEY * 8) + 3] == 0xAA) { // extra check to tell we have nt/nt_enc/par_err running_nested_auth_attack = true; // nt @@ -955,7 +979,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t // first block if (blockNo == 4) { - p_em += blockNo * 16; + p_em += (blockNo * MIFARE_BLOCK_SIZE); // TLV in NDEF, flip length between // 4 | 03 21 D1 02 1C 53 70 91 01 09 54 02 65 6E 4C 69 // 0xFF means long length @@ -970,7 +994,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t } } - emlGetMem(response, blockNo, 1); + emlGetMem_xt(response, blockNo, 1, MIFARE_BLOCK_SIZE); if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK - ISO14443A_CMD_READBLOCK] Data Block[%d]: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", blockNo, @@ -1010,11 +1034,11 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t } } else { if (IsAccessAllowed(blockNo, cardAUTHKEY, AC_DATA_READ) == false) { - memset(response, 0x00, 16); // datablock cannot be read + memset(response, 0x00, MIFARE_BLOCK_SIZE); // datablock cannot be read if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK - IsAccessAllowed] Data block %d (0x%02x) cannot be read", blockNo, blockNo); } } - AddCrc14A(response, 16); + AddCrc14A(response, MIFARE_BLOCK_SIZE); mf_crypto1_encrypt(pcs, response, MAX_MIFARE_FRAME_SIZE, response_par); EmSendCmdPar(response, MAX_MIFARE_FRAME_SIZE, response_par); FpgaDisableTracing(); @@ -1109,7 +1133,9 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t // case MFEMUL_WORK => CMD RATS if (receivedCmd_len == 4 && receivedCmd_dec[0] == ISO14443A_CMD_RATS && (receivedCmd_dec[1] & 0xF0) <= 0x80 && (receivedCmd_dec[1] & 0x0F) <= 0x0e) { + if (rats && rats_len) { + if (encrypted_data) { memcpy(response, rats, rats_len); mf_crypto1_encrypt(pcs, response, rats_len, response_par); @@ -1117,46 +1143,58 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t } else { EmSendCmd(rats, rats_len); } + FpgaDisableTracing(); - if (g_dbglevel >= DBG_EXTENDED) + + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK] RCV RATS => ACK"); + } + } else { EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); FpgaDisableTracing(); cardSTATE_TO_IDLE(); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK] RCV RATS => NACK"); + } } break; } // case MFEMUL_WORK => ISO14443A_CMD_NXP_DESELECT if (receivedCmd_len == 3 && receivedCmd_dec[0] == ISO14443A_CMD_NXP_DESELECT) { + if (rats && rats_len) { + // response back NXP_DESELECT if (encrypted_data) { memcpy(response, receivedCmd_dec, receivedCmd_len); mf_crypto1_encrypt(pcs, response, receivedCmd_len, response_par); EmSendCmdPar(response, receivedCmd_len, response_par); - } else + } else { EmSendCmd(receivedCmd_dec, receivedCmd_len); + } FpgaDisableTracing(); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => ACK"); + } + } else { EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); FpgaDisableTracing(); cardSTATE_TO_IDLE(); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => NACK"); + } } break; } // case MFEMUL_WORK => command not allowed - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("Received command not allowed, nacking"); + } EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); FpgaDisableTracing(); break; @@ -1164,14 +1202,16 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t // AUTH1 case MFEMUL_AUTH1: { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("[MFEMUL_AUTH1] Enter case"); + } if (receivedCmd_len != 8) { cardSTATE_TO_IDLE(); LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("MFEMUL_AUTH1: receivedCmd_len != 8 (%d) => cardSTATE_TO_IDLE())", receivedCmd_len); + } break; } @@ -1191,6 +1231,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t ar_nr_resp[0].state = NESTED; finished = true; } + if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) { for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { @@ -1267,22 +1308,29 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t // WRITE BL2 case MFEMUL_WRITEBL2: { + if (receivedCmd_len == MAX_MIFARE_FRAME_SIZE) { + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); + if (CheckCrc14A(receivedCmd_dec, receivedCmd_len)) { + if (IsSectorTrailer(cardWRBL)) { - emlGetMem(response, cardWRBL, 1); - if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE)) { + + emlGetMem_xt(response, cardWRBL, 1, MIFARE_BLOCK_SIZE); + + if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE) == false) { memcpy(receivedCmd_dec, response, 6); // don't change KeyA } - if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE)) { + if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE) == false) { memcpy(receivedCmd_dec + 10, response + 10, 6); // don't change KeyA } - if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE)) { + if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE) == false) { memcpy(receivedCmd_dec + 6, response + 6, 4); // don't change AC bits } + } else { - if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE)) { + if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE) == false) { memcpy(receivedCmd_dec, response, 16); // don't change anything } } diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index b8cb4838d..d19233f0a 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -756,14 +756,16 @@ uint8_t FirstBlockOfSector(uint8_t sectorNo) { } // work with emulator memory -void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int block_width) { +void emlSetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width) { uint32_t offset = blockNum * block_width; uint32_t len = blocksCount * block_width; emlSet(data, offset, len); } -void emlGetMem(uint8_t *data, int blockNum, int blocksCount) { - emlGet(data, (blockNum * 16), (blocksCount * 16)); +void emlGetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width) { + uint32_t offset = blockNum * block_width; + uint32_t len = blocksCount * block_width; + emlGet(data, offset, len); } bool emlCheckValBl(int blockNum) { @@ -817,10 +819,11 @@ uint64_t emlGetKey(int sectorNum, int keyType) { } void emlClearMem(void) { + + BigBuf_Clear_EM(); + const uint8_t trailer[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0x69, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; const uint8_t uid[] = {0xe6, 0x84, 0x87, 0xf3, 0x16, 0x88, 0x04, 0x00, 0x46, 0x8e, 0x45, 0x55, 0x4d, 0x70, 0x41, 0x04}; - uint8_t *mem = BigBuf_get_EM_addr(); - memset(mem, 0, CARD_MEMORY_SIZE); // fill sectors trailer data for (uint16_t b = 3; b < MIFARE_4K_MAXBLOCK; ((b < MIFARE_2K_MAXBLOCK - 4) ? (b += 4) : (b += 16))) { diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index d2d259c73..28987c363 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -131,8 +131,9 @@ uint8_t SectorTrailer(uint8_t blockNo); // emulator functions void emlClearMem(void); -void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int block_width); -void emlGetMem(uint8_t *data, int blockNum, int blocksCount); +void emlSetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width); +void emlGetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width); + uint64_t emlGetKey(int sectorNum, int keyType); int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum); void emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum); diff --git a/armsrc/pcf7931.c b/armsrc/pcf7931.c index 2d56943af..c1f2e3f7f 100644 --- a/armsrc/pcf7931.c +++ b/armsrc/pcf7931.c @@ -28,105 +28,151 @@ #define T0_PCF 8 //period for the pcf7931 in us #define ALLOC 16 -size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) { +// IIR filter consts +#define IIR_CONST1 0.1f +#define IIR_CONST2 0.9f - // 2021 iceman, memor +// used to decimate samples. this allows DoAcquisition to sample for a longer duration. +// Decimation of 4 makes sure that all blocks can be sampled at once! +#define DECIMATION 4 + +#define CLOCK (64/DECIMATION) // this actually is 64, but since samples are decimated by 2, CLOCK is also /2 +#define TOLERANCE (CLOCK / 8) +#define _16T0 (CLOCK/4) +#define _32T0 (CLOCK/2) +#define _64T0 (CLOCK) + +// calculating the two possible pmc lengths, based on the clock. -4 at the end is to make sure not to increment too far +#define PMC_16T0_LEN ((128 + 127 + 16 + 32 + 33 + 16) * CLOCK/64); +#define PMC_32T0_LEN ((128 + 127 + 16 + 32 + 33 ) * CLOCK/64); + +// theshold for recognition of positive/negative slope +#define THRESHOLD 80 + +size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) { uint8_t bits[256] = {0x00}; uint8_t blocks[8][16]; - uint8_t *dest = BigBuf_get_addr(); - - int g_GraphTraceLen = BigBuf_max_traceLen(); - if (g_GraphTraceLen > 18000) { - g_GraphTraceLen = 18000; - } - - int i = 2, j, lastval, bitidx, half_switch; - int clock = 64; - int tolerance = clock / 8; - int pmc, block_done; - int lc, warnings = 0; - size_t num_blocks = 0; - int lmin = 64, lmax = 192; - uint8_t dir; + uint16_t g_GraphTraceLen = BigBuf_max_traceLen(); + // limit g_GraphTraceLen to a little more than 2 data frames. + // To make sure a complete dataframe is in the dataset. + // 1 Frame is 16 Byte -> 128byte. at a T0 of 64 -> 8129 Samples per frame. + // + PMC -> 384T0 --> 8576 samples required for one block + // to make sure that one complete block is definitely being sampled, we need 2 times that + // which is ~17.xxx samples. round up. and clamp to this value. + + // TODO: Doublecheck why this is being limited? - seems not to be needed. + // g_GraphTraceLen = (g_GraphTraceLen > 18000) ? 18000 : g_GraphTraceLen; BigBuf_Clear_keep_EM(); LFSetupFPGAForADC(LF_DIVISOR_125, true); - DoAcquisition_default(0, true, ledcontrol); + DoAcquisition(DECIMATION, 8, 0, 0, false, 0, 0, 0, ledcontrol); - /* Find first local max/min */ - if (dest[1] > dest[0]) { - while (i < g_GraphTraceLen) { - if (!(dest[i] > dest[i - 1]) && dest[i] > lmax) { - break; - } - i++; - } - dir = 0; - } else { - while (i < g_GraphTraceLen) { - if (!(dest[i] < dest[i - 1]) && dest[i] < lmin) { - break; - } - i++; - } - dir = 1; - } + uint8_t j; + uint8_t half_switch; + uint8_t bitPos; + + uint32_t sample; // to keep track of the current sample that is being analyzed + uint32_t samplePosLastEdge; + uint32_t samplePosCurrentEdge; + uint8_t lastClockDuration; // used to store the duration of the last "clock", for decoding. clock may not be the correct term, maybe bit is better. The duration between two edges is meant + uint8_t beforeLastClockDuration; // store the clock duration of the cycle before the last Clock duration. Basically clockduration -2 + + + uint8_t block_done; + size_t num_blocks = 0; + EdgeType expectedNextEdge = FALLING; // direction in which the next edge is expected should go. - lastval = i++; half_switch = 0; - pmc = 0; + samplePosLastEdge = 0; block_done = 0; + bitPos = 0; + lastClockDuration=0; + + for (sample = 1 ; sample < g_GraphTraceLen-4; sample++) { + // condition is searching for the next edge, in the expected diretion. + //todo: without flouz + dest[sample] = (uint8_t)(dest[sample-1] * IIR_CONST1 + dest[sample] * IIR_CONST2); // apply IIR filter - for (bitidx = 0; i < g_GraphTraceLen; i++) { + if ( ((dest[sample] + THRESHOLD) < dest[sample-1] && expectedNextEdge == FALLING ) || + ((dest[sample] - THRESHOLD) > dest[sample-1] && expectedNextEdge == RISING )) { + //okay, next falling/rising edge found - if ((dest[i - 1] > dest[i] && dir == 1 && dest[i] > lmax) || (dest[i - 1] < dest[i] && dir == 0 && dest[i] < lmin)) { - lc = i - lastval; - lastval = i; + expectedNextEdge = (expectedNextEdge == FALLING) ? RISING : FALLING; //toggle the next expected edge + samplePosCurrentEdge = sample; + beforeLastClockDuration = lastClockDuration; // save the previous clock duration for PMC recognition + lastClockDuration = samplePosCurrentEdge - samplePosLastEdge; + samplePosLastEdge = sample; - // Switch depending on lc length: - // Tolerance is 1/8 of clock rate (arbitrary) - if (ABS(lc - clock / 4) < tolerance) { - // 16T0 - if ((i - pmc) == lc) { // 16T0 was previous one + // Dbprintf("%d, %d, edge found, len: %d, nextEdge: %d", sample, dest[sample], lastClockDuration*DECIMATION, expectedNextEdge); + + // Switch depending on lastClockDuration length: + // 16T0 + if (ABS(lastClockDuration - _16T0) < TOLERANCE) { + + // if the clock before also was 16T0, it is a PMC! + if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) { // It's a PMC - i += (128 + 127 + 16 + 32 + 33 + 16) - 1; - lastval = i; - pmc = 0; + Dbprintf(_GREEN_("PMC 16T0 FOUND:") " bitPos: %d, sample: %d", bitPos, sample); + sample += PMC_16T0_LEN; // move to the sample after PMC + + expectedNextEdge = FALLING; + samplePosLastEdge = sample; block_done = 1; - } else { - pmc = i; } - } else if (ABS(lc - clock / 2) < tolerance) { - // 32TO - if ((i - pmc) == lc) { // 16T0 was previous one + + // 32TO + } else if (ABS(lastClockDuration - _32T0) < TOLERANCE) { + // if the clock before also was 16T0, it is a PMC! + if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) { // It's a PMC ! - i += (128 + 127 + 16 + 32 + 33) - 1; - lastval = i; - pmc = 0; + Dbprintf(_GREEN_("PMC 32T0 FOUND:") " bitPos: %d, sample: %d", bitPos, sample); + + sample += PMC_32T0_LEN; // move to the sample after PMC + + expectedNextEdge = FALLING; + samplePosLastEdge = sample; block_done = 1; + + // if no pmc, then its a normal bit. + // Check if its the second time, the edge changed if yes, then the bit is 0 } else if (half_switch == 1) { - bits[bitidx++] = 0; + bits[bitPos] = 0; + // reset the edge counter to 0 half_switch = 0; + bitPos++; + + // if it is the first time the edge changed. No bit value will be set here, bit if the + // edge changes again, it will be. see case above. } else half_switch++; - } else if (ABS(lc - clock) < tolerance) { - // 64TO - bits[bitidx++] = 1; + + // 64T0 + } else if (ABS(lastClockDuration - _64T0) < TOLERANCE) { + // this means, bit here is 1 + bits[bitPos] = 1; + bitPos++; + + // Error } else { - // Error - if (++warnings > 10) { + // some Error. maybe check tolerances. + // likeley to happen in the first block. + + // In an Ideal world, this can be enabled. However, if only bad antenna field, this print will flood the output + // and one might miss some "good" frames. + //Dbprintf(_RED_("ERROR in demodulation") " Length last clock: %d - check threshold/tolerance/signal. Toss block", lastClockDuration*DECIMATION); - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("Error: too many detection errors, aborting"); - } - - return 0; - } + // Toss this block. + block_done = 1; } if (block_done == 1) { - if (bitidx == 128) { + // Dbprintf(_YELLOW_("Block Done") " bitPos: %d, sample: %d", bitPos, sample); + + // check if it is a complete block. If bitpos <128, it means that we did not receive + // a complete block. E.g. at the first start of a transmission. + // only save if a complete block is being received. + if (bitPos == 128) { for (j = 0; j < 16; ++j) { blocks[num_blocks][j] = 128 * bits[j * 8 + 7] + @@ -141,24 +187,25 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) { } num_blocks++; } - bitidx = 0; + // now start over for the next block / first complete block. + bitPos = 0; block_done = 0; half_switch = 0; } - if (i < g_GraphTraceLen) { - dir = (dest[i - 1] > dest[i]) ? 0 : 1; - } + }else { + // Dbprintf("%d, %d", sample, dest[sample]); } - if (bitidx == 255) { - bitidx = 0; + // one block only holds 16byte (=128 bit) and then comes the PMC. so if more bit are found than 129, there must be an issue and PMC has not been identfied... + // TODO: not sure what to do in such case... + if (bitPos >= 129) { + Dbprintf(_RED_("PMC should have been found...") " bitPos: %d, sample: %d", bitPos, sample); + bitPos = 0; } - if (num_blocks == 4) { - break; - } - } + } + memcpy(outBlocks, blocks, 16 * num_blocks); return num_blocks; } @@ -204,25 +251,32 @@ bool IsBlock1PCF7931(const uint8_t *block) { } void ReadPCF7931(bool ledcontrol) { + + uint8_t maxBlocks = 8; // readable blocks int found_blocks = 0; // successfully read blocks - int max_blocks = 8; // readable blocks - uint8_t memory_blocks[8][17]; // PCF content - uint8_t single_blocks[8][17]; // PFC blocks with unknown position + + // TODO: Why 17 byte len? 16 should be good. + uint8_t memory_blocks[maxBlocks][17]; // PCF content + uint8_t single_blocks[maxBlocks][17]; // PFC blocks with unknown position + uint8_t tmp_blocks[4][16]; // temporary read buffer + int single_blocks_cnt = 0; size_t n; // transmitted blocks - uint8_t tmp_blocks[4][16]; // temporary read buffer - - uint8_t found_0_1 = 0; // flag: blocks 0 and 1 were found + + //uint8_t found_0_1 = 0; // flag: blocks 0 and 1 were found int errors = 0; // error counter int tries = 0; // tries counter + // reuse lenghts and consts to properly clear memset(memory_blocks, 0, 8 * 17 * sizeof(uint8_t)); memset(single_blocks, 0, 8 * 17 * sizeof(uint8_t)); - int i = 0, j = 0; + int i = 0; + //j = 0; do { + Dbprintf("ReadPCF7931() -- Reading Loop =========="); i = 0; memset(tmp_blocks, 0, 4 * 16 * sizeof(uint8_t)); @@ -232,15 +286,13 @@ void ReadPCF7931(bool ledcontrol) { // exit if no block is received if (errors >= 10 && found_blocks == 0 && single_blocks_cnt == 0) { - - if (g_dbglevel >= DBG_INFO) - Dbprintf("[!!] Error, no tag or bad tag"); - + Dbprintf("[!!] Error, no tag or bad tag"); return; } - // exit if too many errors during reading - if (tries > 50 && (2 * errors > tries)) { + // exit if too many tries without finding the first block + if (tries > 10) { + Dbprintf("End after 10 tries"); if (g_dbglevel >= DBG_INFO) { Dbprintf("[!!] Error reading the tag, only partial content"); } @@ -248,93 +300,98 @@ void ReadPCF7931(bool ledcontrol) { goto end; } - // our logic breaks if we don't get at least two blocks - if (n < 2) { - // skip if all 0s block or no blocks - if (n == 0 || !memcmp(tmp_blocks[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) - continue; + // This part was not working properly. + // So currently the blocks are not being sorted, but at least printed. - // add block to single blocks list - if (single_blocks_cnt < max_blocks) { - for (i = 0; i < single_blocks_cnt; ++i) { - if (!memcmp(single_blocks[i], tmp_blocks[0], 16)) { - j = 1; - break; - } - } - if (j != 1) { - memcpy(single_blocks[single_blocks_cnt], tmp_blocks[0], 16); - print_result("got single block", single_blocks[single_blocks_cnt], 16); - single_blocks_cnt++; - } - j = 0; - } - ++tries; - continue; - } + // // our logic breaks if we don't get at least two blocks + // if (n < 2) { + // // skip if all 0s block or no blocks + // if (n == 0 || !memcmp(tmp_blocks[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) + // continue; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (max_blocks == 0 ? found_blocks : max_blocks), tries, errors); + // // add block to single blocks list + // if (single_blocks_cnt < maxBlocks) { + // for (i = 0; i < single_blocks_cnt; ++i) { + // if (!memcmp(single_blocks[i], tmp_blocks[0], 16)) { + // j = 1; + // break; + // } + // } + // if (j != 1) { + // memcpy(single_blocks[single_blocks_cnt], tmp_blocks[0], 16); + // print_result("got single block", single_blocks[single_blocks_cnt], 16); + // single_blocks_cnt++; + // } + // j = 0; + // } + // ++tries; + // continue; + // } + // Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (maxBlocks == 0 ? found_blocks : maxBlocks), tries, errors); + // if (g_dbglevel >= DBG_EXTENDED) + // Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (maxBlocks == 0 ? found_blocks : maxBlocks), tries, errors); + + // print blocks that have been found for (i = 0; i < n; ++i) { - print_result("got consecutive blocks", tmp_blocks[i], 16); + print_result("Block found: ", tmp_blocks[i], 16); } - i = 0; - if (!found_0_1) { - while (i < n - 1) { - if (IsBlock0PCF7931(tmp_blocks[i]) && IsBlock1PCF7931(tmp_blocks[i + 1])) { - found_0_1 = 1; - memcpy(memory_blocks[0], tmp_blocks[i], 16); - memcpy(memory_blocks[1], tmp_blocks[i + 1], 16); - memory_blocks[0][ALLOC] = memory_blocks[1][ALLOC] = 1; - // block 1 tells how many blocks are going to be sent - max_blocks = MAX((memory_blocks[1][14] & 0x7f), memory_blocks[1][15]) + 1; - found_blocks = 2; + // i = 0; + // if (!found_0_1) { + // while (i < n - 1) { + // if (IsBlock0PCF7931(tmp_blocks[i]) && IsBlock1PCF7931(tmp_blocks[i + 1])) { + // found_0_1 = 1; + // memcpy(memory_blocks[0], tmp_blocks[i], 16); + // memcpy(memory_blocks[1], tmp_blocks[i + 1], 16); + // memory_blocks[0][ALLOC] = memory_blocks[1][ALLOC] = 1; + // // block 1 tells how many blocks are going to be sent + // maxBlocks = MAX((memory_blocks[1][14] & 0x7f), memory_blocks[1][15]) + 1; + // found_blocks = 2; - Dbprintf("Found blocks 0 and 1. PCF is transmitting %d blocks.", max_blocks); + // Dbprintf("Found blocks 0 and 1. PCF is transmitting %d blocks.", maxBlocks); - // handle the following blocks - for (j = i + 2; j < n; ++j) { - memcpy(memory_blocks[found_blocks], tmp_blocks[j], 16); - memory_blocks[found_blocks][ALLOC] = 1; - ++found_blocks; - } - break; - } - ++i; - } - } else { - // Trying to re-order blocks - // Look for identical block in memory blocks - while (i < n - 1) { - // skip all zeroes blocks - if (memcmp(tmp_blocks[i], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) { - for (j = 1; j < max_blocks - 1; ++j) { - if (!memcmp(tmp_blocks[i], memory_blocks[j], 16) && !memory_blocks[j + 1][ALLOC]) { - memcpy(memory_blocks[j + 1], tmp_blocks[i + 1], 16); - memory_blocks[j + 1][ALLOC] = 1; - if (++found_blocks >= max_blocks) goto end; - } - } - } - if (memcmp(tmp_blocks[i + 1], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) { - for (j = 0; j < max_blocks; ++j) { - if (!memcmp(tmp_blocks[i + 1], memory_blocks[j], 16) && !memory_blocks[(j == 0 ? max_blocks : j) - 1][ALLOC]) { - if (j == 0) { - memcpy(memory_blocks[max_blocks - 1], tmp_blocks[i], 16); - memory_blocks[max_blocks - 1][ALLOC] = 1; - } else { - memcpy(memory_blocks[j - 1], tmp_blocks[i], 16); - memory_blocks[j - 1][ALLOC] = 1; - } - if (++found_blocks >= max_blocks) goto end; - } - } - } - ++i; - } - } + // // handle the following blocks + // for (j = i + 2; j < n; ++j) { + // memcpy(memory_blocks[found_blocks], tmp_blocks[j], 16); + // memory_blocks[found_blocks][ALLOC] = 1; + // ++found_blocks; + // } + // break; + // } + // ++i; + // } + // } else { + // // Trying to re-order blocks + // // Look for identical block in memory blocks + // while (i < n - 1) { + // // skip all zeroes blocks + // if (memcmp(tmp_blocks[i], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) { + // for (j = 1; j < maxBlocks - 1; ++j) { + // if (!memcmp(tmp_blocks[i], memory_blocks[j], 16) && !memory_blocks[j + 1][ALLOC]) { + // memcpy(memory_blocks[j + 1], tmp_blocks[i + 1], 16); + // memory_blocks[j + 1][ALLOC] = 1; + // if (++found_blocks >= maxBlocks) goto end; + // } + // } + // } + // if (memcmp(tmp_blocks[i + 1], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) { + // for (j = 0; j < maxBlocks; ++j) { + // if (!memcmp(tmp_blocks[i + 1], memory_blocks[j], 16) && !memory_blocks[(j == 0 ? maxBlocks : j) - 1][ALLOC]) { + // if (j == 0) { + // memcpy(memory_blocks[maxBlocks - 1], tmp_blocks[i], 16); + // memory_blocks[maxBlocks - 1][ALLOC] = 1; + // } else { + // memcpy(memory_blocks[j - 1], tmp_blocks[i], 16); + // memory_blocks[j - 1][ALLOC] = 1; + // } + // if (++found_blocks >= maxBlocks) goto end; + // } + // } + // } + // ++i; + // } + // } ++tries; if (BUTTON_PRESS()) { if (g_dbglevel >= DBG_EXTENDED) @@ -342,13 +399,15 @@ void ReadPCF7931(bool ledcontrol) { goto end; } - } while (found_blocks < max_blocks); + } while (found_blocks < maxBlocks); + end: +/* Dbprintf("-----------------------------------------"); Dbprintf("Memory content:"); Dbprintf("-----------------------------------------"); - for (i = 0; i < max_blocks; ++i) { + for (i = 0; i < maxBlocks; ++i) { if (memory_blocks[i][ALLOC]) print_result("Block", memory_blocks[i], 16); else @@ -356,7 +415,7 @@ end: } Dbprintf("-----------------------------------------"); - if (found_blocks < max_blocks) { + if (found_blocks < maxBlocks) { Dbprintf("-----------------------------------------"); Dbprintf("Blocks with unknown position:"); Dbprintf("-----------------------------------------"); @@ -365,39 +424,54 @@ end: Dbprintf("-----------------------------------------"); } +*/ + reply_mix(CMD_ACK, 0, 0, 0, 0, 0); } -static void RealWritePCF7931(uint8_t *pass, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol) { +static void RealWritePCF7931( + uint8_t *pass, + uint16_t init_delay, + int8_t offsetPulseWidth, int8_t offsetPulsePosition, + uint8_t address, uint8_t byte, uint8_t data, + bool ledcontrol){ + uint32_t tab[1024] = {0}; // data times frame uint32_t u = 0; uint8_t parity = 0; - bool comp = 0; //BUILD OF THE DATA FRAME //alimentation of the tag (time for initializing) + // ToDo: This could be optimized/automated. e.g. Read one cycle, find PMC and calculate time. + // I dont understand, why 8192/2 AddPatternPCF7931(init_delay, 0, 8192 / 2 * T0_PCF, tab); + + // why "... + 70"? Why not "... + x * T0"? + // I think he just added 70 to be somewhere in The PMC window, which is 32T0 (=32*8 = 256) + // 3*T0 = PMC width + // 29*T0 = rest of PMC window (total 32T0 = 3+29) + // after the PMC, it directly goes to the password indication bit. AddPatternPCF7931(8192 / 2 * T0_PCF + 319 * T0_PCF + 70, 3 * T0_PCF, 29 * T0_PCF, tab); //password indication bit - AddBitPCF7931(1, tab, l, p); + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); //password (on 56 bits) - AddBytePCF7931(pass[0], tab, l, p); - AddBytePCF7931(pass[1], tab, l, p); - AddBytePCF7931(pass[2], tab, l, p); - AddBytePCF7931(pass[3], tab, l, p); - AddBytePCF7931(pass[4], tab, l, p); - AddBytePCF7931(pass[5], tab, l, p); - AddBytePCF7931(pass[6], tab, l, p); - //programming mode (0 or 1) - AddBitPCF7931(0, tab, l, p); + AddBytePCF7931(pass[0], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[1], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[2], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[3], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[4], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[5], tab, offsetPulseWidth, offsetPulsePosition); + AddBytePCF7931(pass[6], tab, offsetPulseWidth, offsetPulsePosition); + //programming mode (0 or 1) -> 0 = byte wise; 1 = block wise programming + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); //block address on 6 bits for (u = 0; u < 6; ++u) { if (address & (1 << u)) { // bit 1 ++parity; - AddBitPCF7931(1, tab, l, p); + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); } else { // bit 0 - AddBitPCF7931(0, tab, l, p); + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); } } @@ -405,56 +479,48 @@ static void RealWritePCF7931(uint8_t *pass, uint16_t init_delay, int32_t l, int3 for (u = 0; u < 4; ++u) { if (byte & (1 << u)) { // bit 1 parity++; - AddBitPCF7931(1, tab, l, p); + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); } else // bit 0 - AddBitPCF7931(0, tab, l, p); + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); } //data on 8 bits for (u = 0; u < 8; u++) { if (data & (1 << u)) { // bit 1 parity++; - AddBitPCF7931(1, tab, l, p); + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); } else //bit 0 - AddBitPCF7931(0, tab, l, p); + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); } //parity bit if ((parity % 2) == 0) - AddBitPCF7931(0, tab, l, p); //even parity + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); //even parity else - AddBitPCF7931(1, tab, l, p);//odd parity + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition);//odd parity - //time access memory - AddPatternPCF7931(5120 + 2680, 0, 0, tab); - - //conversion of the scale time - for (u = 0; u < 500; ++u) - tab[u] = (tab[u] * 3) / 2; - - //compensation of the counter reload - while (!comp) { - comp = 1; - for (u = 0; tab[u] != 0; ++u) - if (tab[u] > 0xFFFF) { - tab[u] -= 0xFFFF; - comp = 0; - } - } + // time access memory (640T0) + // Not sure why 335*T0, but should not matter. Since programming should be finished at that point + AddPatternPCF7931((640 + 335)* T0_PCF, 0, 0, tab); SendCmdPCF7931(tab, ledcontrol); } /* Write on a byte of a PCF7931 tag * @param address : address of the block to write - @param byte : address of the byte to write - @param data : data to write + * @param byte : address of the byte to write + * @param data : data to write */ -void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol) { +void WritePCF7931( + uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, + uint16_t init_delay, + int8_t offsetPulseWidth, int8_t offsetPulsePosition, + uint8_t address, uint8_t byte, uint8_t data, + bool ledcontrol) { if (g_dbglevel >= DBG_INFO) { Dbprintf("Initialization delay : %d us", init_delay); - Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", l, p); + Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", offsetPulseWidth, offsetPulsePosition); } Dbprintf("Password (LSB first on each byte): %02x %02x %02x %02x %02x %02x %02x", pass1, pass2, pass3, pass4, pass5, pass6, pass7); @@ -464,15 +530,15 @@ void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, ui uint8_t password[7] = {pass1, pass2, pass3, pass4, pass5, pass6, pass7}; - RealWritePCF7931(password, init_delay, l, p, address, byte, data, ledcontrol); + RealWritePCF7931(password, init_delay, offsetPulseWidth, offsetPulsePosition, address, byte, data, ledcontrol); } -/* Send a trame to a PCF7931 tags +/* Send a frame to a PCF7931 tags * @param tab : array of the data frame */ -void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { +void SendCmdPCF7931(uint32_t *tab, bool ledcontrol) { uint16_t u = 0, tempo = 0; if (g_dbglevel >= DBG_INFO) { @@ -484,6 +550,20 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); if (ledcontrol) LED_A_ON(); + + // rescale the values to match the time of the timer below. + for (u = 0; u < 500; ++u) { + tab[u] = (tab[u] * 3) / 2; + } + + // compensation for the counter overflow + // only one overflow should be possible. + for (u = 0; tab[u] != 0; ++u) + if (tab[u] > 0xFFFF) { + tab[u] -= 0xFFFF; + break; + } + // steal this pin from the SSP and use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; @@ -493,7 +573,7 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { AT91C_BASE_PMC->PMC_PCER |= (0x1 << AT91C_ID_TC0); AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz (48Mhz clock, 32 = prescaler (div3)) AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; // Assert a sync signal. This sets all timers to 0 on next active clock edge @@ -503,19 +583,19 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { for (u = 0; tab[u] != 0; u += 3) { // modulate antenna HIGH(GPIO_SSC_DOUT); - while (tempo != tab[u]) { + while ((uint32_t)tempo < tab[u]) { tempo = AT91C_BASE_TC0->TC_CV; } // stop modulating antenna LOW(GPIO_SSC_DOUT); - while (tempo != tab[u + 1]) { + while ((uint32_t)tempo < tab[u + 1]) { tempo = AT91C_BASE_TC0->TC_CV; } // modulate antenna HIGH(GPIO_SSC_DOUT); - while (tempo != tab[u + 2]) { + while ((uint32_t)tempo < tab[u + 2]) { tempo = AT91C_BASE_TC0->TC_CV; } } @@ -528,32 +608,36 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) { } -/* Add a byte for building the data frame of PCF7931 tags +/* Add a byte for building the data frame of PCF7931 tags. + * See Datasheet of PCF7931 diagramm on page 8. This explains pulse widht & positioning + * Normally, no offset should be required. * @param b : byte to add * @param tab : array of the data frame - * @param l : offset on low pulse width - * @param p : offset on low pulse positioning + * @param offsetPulseWidth : offset on low pulse width in µs (default pulse widht is 6T0) + * @param offsetPulsePosition : offset on low pulse positioning in µs */ -bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int32_t l, int32_t p) { +bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition) { uint32_t u; for (u = 0; u < 8; ++u) { if (byte & (1 << u)) { //bit is 1 - if (AddBitPCF7931(1, tab, l, p) == 1) return true; + AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition); } else { //bit is 0 - if (AddBitPCF7931(0, tab, l, p) == 1) return true; + AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); } } return false; } -/* Add a bits for building the data frame of PCF7931 tags +/* Add a bits for building the data frame of PCF7931 tags. + * See Datasheet of PCF7931 diagramm on page 8. This explains pulse widht & positioning + * Normally, no offset should be required. * @param b : bit to add * @param tab : array of the data frame - * @param l : offset on low pulse width - * @param p : offset on low pulse positioning + * @param offsetPulseWidth : offset on low pulse width in µs (default pulse widht is 6T0) + * @param offsetPulsePosition : offset on low pulse positioning in µs */ -bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) { +bool AddBitPCF7931(bool b, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition) { uint8_t u = 0; //we put the cursor at the last value of the array @@ -561,23 +645,22 @@ bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) { if (b == 1) { //add a bit 1 if (u == 0) - tab[u] = 34 * T0_PCF + p; + tab[u] = 34 * T0_PCF + offsetPulsePosition; else - tab[u] = 34 * T0_PCF + tab[u - 1] + p; + tab[u] = 34 * T0_PCF + tab[u - 1] + offsetPulsePosition; - tab[u + 1] = 6 * T0_PCF + tab[u] + l; - tab[u + 2] = 88 * T0_PCF + tab[u + 1] - l - p; - return false; + tab[u + 1] = 6 * T0_PCF + tab[u] + offsetPulseWidth; + tab[u + 2] = 88 * T0_PCF + tab[u + 1] - offsetPulseWidth - offsetPulsePosition; + } else { //add a bit 0 - if (u == 0) - tab[u] = 98 * T0_PCF + p; + tab[u] = 98 * T0_PCF + offsetPulsePosition; else - tab[u] = 98 * T0_PCF + tab[u - 1] + p; + tab[u] = 98 * T0_PCF + tab[u - 1] + offsetPulsePosition; - tab[u + 1] = 6 * T0_PCF + tab[u] + l; - tab[u + 2] = 24 * T0_PCF + tab[u + 1] - l - p; - return false; + tab[u + 1] = 6 * T0_PCF + tab[u] + offsetPulseWidth; + tab[u + 2] = 24 * T0_PCF + tab[u + 1] - offsetPulseWidth - offsetPulsePosition; + } return true; } @@ -592,8 +675,8 @@ bool AddPatternPCF7931(uint32_t a, uint32_t b, uint32_t c, uint32_t *tab) { uint32_t u = 0; for (u = 0; tab[u] != 0; u += 3) {} //we put the cursor at the last value of the array - tab[u] = (u == 0) ? a : a + tab[u - 1]; - tab[u + 1] = b + tab[u]; + tab[u] = (u == 0) ? a : a + tab[u - 1]; // if it is the first value of the array, nothing needs to be added. + tab[u + 1] = b + tab[u]; // otherwise always add up the values, because later on it is compared to a counter tab[u + 2] = c + tab[u + 1]; return true; diff --git a/armsrc/pcf7931.h b/armsrc/pcf7931.h index 3be9ea5be..314fb7e3c 100644 --- a/armsrc/pcf7931.h +++ b/armsrc/pcf7931.h @@ -18,14 +18,20 @@ #include "common.h" + +typedef enum{ + FALLING, + RISING +} EdgeType; + size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol); bool IsBlock0PCF7931(uint8_t *block); bool IsBlock1PCF7931(const uint8_t *block); void ReadPCF7931(bool ledcontrol); -void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol); -bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int32_t l, int32_t p); -bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p); +void SendCmdPCF7931(uint32_t *tab, bool ledcontrol); +bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition); +bool AddBitPCF7931(bool b, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition); bool AddPatternPCF7931(uint32_t a, uint32_t b, uint32_t c, uint32_t *tab); -void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol); +void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int8_t offsetPulseWidth, int8_t offsetPulsePosition, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol); #endif diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c index ebedfc3b4..567520ee3 100644 --- a/armsrc/sam_common.c +++ b/armsrc/sam_common.c @@ -437,10 +437,10 @@ uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) { // 90 00 // NFC req: - // 0C 05 DE 64 + // 0C 05 DE 64 // copy data out of c1->a1>->a1->80 node uint16_t nfc_tx_len = (uint8_t) * (sam_rx_buf + 10); memcpy(nfc_tx_buf, sam_rx_buf + 11, nfc_tx_len); return nfc_tx_len; -} \ No newline at end of file +} diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index 450e46253..270468f66 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -49,9 +49,9 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re if (g_dbglevel >= DBG_DEBUG) DbpString("start sam_send_request_iso14a"); - uint8_t * buf1 = BigBuf_malloc(ISO7816_MAX_FRAME); - uint8_t * buf2 = BigBuf_malloc(ISO7816_MAX_FRAME); - if(buf1 == NULL || buf2 == NULL){ + uint8_t *buf1 = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *buf2 = BigBuf_malloc(ISO7816_MAX_FRAME); + if (buf1 == NULL || buf2 == NULL) { res = PM3_EMALLOC; goto out; } @@ -103,19 +103,19 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); bool is_cmd_check = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK; - if(is_cmd_check && break_on_nr_mac){ + if (is_cmd_check && break_on_nr_mac) { memcpy(response, nfc_tx_buf, nfc_tx_len); *response_len = nfc_tx_len; if (g_dbglevel >= DBG_INFO) { DbpString("NR-MAC: "); - Dbhexdump((*response_len)-1, response+1, false); + Dbhexdump((*response_len) - 1, response + 1, false); } res = PM3_SUCCESS; goto out; } bool is_cmd_update = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE; - if(is_cmd_update && prevent_epurse_update && nfc_tx_buf[0] == 0x87 && nfc_tx_buf[1] == 0x02){ + if (is_cmd_update && prevent_epurse_update && nfc_tx_buf[0] == 0x87 && nfc_tx_buf[1] == 0x02) { // block update(2) command and fake the response to prevent update of epurse // NFC TX BUFFERS PREPARED BY SAM LOOKS LIKE: @@ -124,8 +124,8 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re // NFC RX BUFFERS EXPECTED BY SAM WOULD LOOK LIKE: // #2(FF FF FF FF) #1(C9 FD FF FF) 3A 47 - memcpy(nfc_rx_buf+0, nfc_tx_buf+6, 4); - memcpy(nfc_rx_buf+4, nfc_tx_buf+0, 4); + memcpy(nfc_rx_buf + 0, nfc_tx_buf + 6, 4); + memcpy(nfc_rx_buf + 4, nfc_tx_buf + 0, 4); AddCrc(nfc_rx_buf, 8); nfc_rx_len = 10; @@ -155,7 +155,7 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re } - if (res != PM3_SUCCESS ) { + if (res != PM3_SUCCESS) { res = PM3_ECARDEXCHANGE; goto out; } @@ -358,7 +358,7 @@ int sam_picopass_get_pacs(PacketCommandNG *c) { // implicit StartSspClk() happens here Iso15693InitReader(); - if(!select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod)){ + if (!select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod)) { goto err; } diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c index facdb2545..7d4a018be 100644 --- a/armsrc/sam_seos.c +++ b/armsrc/sam_seos.c @@ -129,9 +129,9 @@ static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t r if (g_dbglevel >= DBG_DEBUG) DbpString("start sam_send_request_iso14a"); - uint8_t * buf1 = BigBuf_malloc(ISO7816_MAX_FRAME); - uint8_t * buf2 = BigBuf_malloc(ISO7816_MAX_FRAME); - if(buf1 == NULL || buf2 == NULL){ + uint8_t *buf1 = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *buf2 = BigBuf_malloc(ISO7816_MAX_FRAME); + if (buf1 == NULL || buf2 == NULL) { res = PM3_EMALLOC; goto out; } diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index 1e589554c..90275de16 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -658,7 +658,7 @@ void rdv40_spiffs_safe_print_tree(void) { } } - Dbprintf("[%04x] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); + Dbprintf("[%04u] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); printed = true; } if (printed == false) { diff --git a/armsrc/spiffs_check.c b/armsrc/spiffs_check.c index 636feb769..e082fefbb 100644 --- a/armsrc/spiffs_check.c +++ b/armsrc/spiffs_check.c @@ -548,7 +548,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_INTERNAL); } // this checks for overflow of the multiplication of block_count+1 with SPIFFS_PAGES_PER_BLOCK(fs) - if (((uint32_t)(-1)) / SPIFFS_PAGES_PER_BLOCK(fs) > (block_count+1)) { + if (((uint32_t)(-1)) / SPIFFS_PAGES_PER_BLOCK(fs) > (block_count + 1)) { // checking with +1 block count to avoid overflow also in inner loop, which adds one page... // would exceed value storable in uint32_t SPIFFS_DBG("Overflow: pages per block %04x with block count "_SPIPRIbl" results in overflow\n", SPIFFS_PAGES_PER_BLOCK(fs), block_count); @@ -556,13 +556,13 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { } // because loop indices are using spiffs_page_ix type, // that type can hold a large enough value - if (total_blocks > ((spiffs_page_ix)-1)) { + if (total_blocks > ((spiffs_page_ix) - 1)) { SPIFFS_DBG("Avoiding infinite loop, total_blocks "_SPIPRIpg" too large for spiffs_page_ix type\n", total_blocks); SPIFFS_CHECK_RES(SPIFFS_ERR_INTERNAL); } // because loop indices are using spiffs_page_ix type, // that type can hold a large enough value - if (total_blocks_plus_one_page > ((spiffs_page_ix)-1) || total_blocks_plus_one_page < total_blocks) { + if (total_blocks_plus_one_page > ((spiffs_page_ix) - 1) || total_blocks_plus_one_page < total_blocks) { SPIFFS_DBG("Avoiding infinite loop, total_blocks_plus_one_page "_SPIPRIpg" too large for spiffs_page_ix type\n", total_blocks_plus_one_page); SPIFFS_CHECK_RES(SPIFFS_ERR_INTERNAL); } @@ -586,7 +586,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { 0); // traverse each page except for lookup pages spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; - while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { + while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block + 1)) { //if ((cur_pix & 0xff) == 0) // SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n", // cur_pix, cur_block, total_blocks, block_count); diff --git a/client/Makefile b/client/Makefile index 46fa40439..cf4b65b5f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -887,7 +887,12 @@ ifneq (,$(INSTALLBIN)) endif ifneq (,$(INSTALLSHARE)) $(Q)$(INSTALLSUDO) $(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + # hack ahead: inject installation path into pm3_resources.py + $(Q)sed -i 's|^TOOLS_PATH \?= \?None|TOOLS_PATH="$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)"|' pyscripts/pm3_resources.py + $(Q)sed -i 's|^DICTS_PATH \?= \?None|DICTS_PATH="$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)/dictionaries"|' pyscripts/pm3_resources.py $(Q)$(INSTALLSUDO) $(CP) $(INSTALLSHARE) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + $(Q)sed -i 's|^TOOLS_PATH \?=.*|TOOLS_PATH = None|' pyscripts/pm3_resources.py + $(Q)sed -i 's|^DICTS_PATH \?=.*|DICTS_PATH = None|' pyscripts/pm3_resources.py endif @true diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index 0ea0bd33b..64de03871 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -180,8 +180,10 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab // parse params for (int i = 0; i < len; i++) { + switch (state) { - case PS_FIRST: // first char + case PS_FIRST: { // first char + if (!clueData || str[i] == '-') { // first char before space is '-' - next element - option OR not "clueData" for not-option fields state = PS_OPTION; @@ -193,24 +195,32 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab } } spaceptr = NULL; - case PS_ARGUMENT: - if (state == PS_FIRST) + } + case PS_ARGUMENT: { + + if (state == PS_FIRST) { state = PS_ARGUMENT; - if (str[i] == '"') { + } + + if (str[i] == '"' || str[i] == '\'') { state = PS_QUOTE; break; } + if (isSpace(str[i])) { spaceptr = bufptr; state = PS_FIRST; } + *bufptr = str[i]; bufptr++; break; - case PS_OPTION: + } + + case PS_OPTION: { + if (isSpace(str[i])) { state = PS_FIRST; - *bufptr = 0x00; bufptr++; argv[argc++] = bufptr; @@ -220,17 +230,22 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab *bufptr = str[i]; bufptr++; break; - case PS_QUOTE: - if (str[i] == '"') { + } + case PS_QUOTE: { + + if (str[i] == '"' || str[i] == '\'') { *bufptr++ = 0x00; state = PS_FIRST; } else { - if (isSpace(str[i]) == false) { - *bufptr++ = str[i]; - } + +// if (isSpace(str[i]) == false) { + *bufptr++ = str[i]; +// } } break; + } } + if (bufptr > bufptrend) { PrintAndLogEx(ERR, "ERROR: Line too long\n"); fflush(stdout); @@ -296,8 +311,9 @@ int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { *datalen = 0; - if (!argstr->count) + if (!argstr->count) { return 0; + } uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0}; int ibuf = 0; @@ -319,8 +335,9 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int ibuf = MIN(ibuf, (sizeof(tmpstr) / 2)); tmpstr[ibuf] = 0; - if (ibuf == 0) + if (ibuf == 0) { return 0; + } if (ibuf > maxdatalen) { PrintAndLogEx(ERR, "Parameter error: string too long (%i chars), expected MAX %i chars\n", ibuf, maxdatalen); diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 48b40a3f6..e5af9c766 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2175,6 +2175,7 @@ D144BD193063 # Brazil transport Sec 8 / A 50d4c54fcdf5 # +# TEKKEN 6 Namco Data Card # Bandai Namco Passport [fka Banapassport] / Sega Aime Card # Dumped on the Flipper Devices Discord Server 6090D00632F5 @@ -2211,6 +2212,40 @@ C8382A233993 7B304F2A12A6 FC9418BF788B # +# Super Street Fighter 4 Capcom NESYS Card +4B6F74696174 +6F746961744B +4176696E7520 +76696E752041 +576C61737265 +6C6173726557 +416962616320 +696261632041 +42622074656E +622074656E42 +416174363030 +617436303041 +5475206F7469 +75206F746954 +41726576696E +726576696E41 +4B63206C6173 +63206C61734B +41656E696261 +656E69626141 +5A3030622074 +30306220745A +557469617436 +746961743655 +48696E75206F +696E75206F48 +496173726576 +617372657649 +52626163206C +626163206C52 +4F2074656E69 +2074656E694F +# # Guest Cashless Prepaid Arcade Payment Cards 168168168168 198407157610 @@ -3072,4 +3107,4 @@ AB921CF0752C 206F7C4C4F36 265A5F32DE73 567D734C403C -2426217B3B3B \ No newline at end of file +2426217B3B3B diff --git a/client/luascripts/t55_chk.lua b/client/luascripts/lf_t55xx_chk.lua similarity index 84% rename from client/luascripts/t55_chk.lua rename to client/luascripts/lf_t55xx_chk.lua index 3133e3a81..6c1da33ce 100644 --- a/client/luascripts/t55_chk.lua +++ b/client/luascripts/lf_t55xx_chk.lua @@ -10,7 +10,7 @@ local green = ac.green author = ' Author: jareckib - created 04.02.2025' version = ' version v1.05' -desc = [[ +desc = [[ A simple script for searching the password for T5577. The script creates a dictionary starting from the entered starting year to the entered ending year. There are two search methods - DDMMYYYY or YYYYMMDD. Checking the entire year @@ -19,7 +19,7 @@ desc = [[ ]] usage = [[ - script run t55_chk [-s start_year] [-e end_year] [-d | -y] + script run lf_t55xx_chk [-s start_year] [-e end_year] [-d | -y] ]] options = [[ -h this help @@ -29,10 +29,10 @@ options = [[ -y search method: YYYYMMDD ]] examples = [[ - script run t55_chk -s 1999 -d -> start 1999, end is current year, method 01011999 - script run t55_chk -s 1999 -y -> start 1999, end is current year, method 19990101 - script run t55_chk -s 1999 -e 2001 -y -> start 1999, end year 2001, method 19990101 - script run t55_chk -s 1999 -e 2001 -d -> start 1999, end year 2001, method 01011999 + script run lf_t55xx_chk -s 1999 -d -> start 1999, end is current year, method 01011999 + script run lf_t55xx_chk -s 1999 -y -> start 1999, end is current year, method 19990101 + script run lf_t55xx_chk -s 1999 -e 2001 -y -> start 1999, end year 2001, method 19990101 + script run lf_t55xx_chk -s 1999 -e 2001 -d -> start 1999, end year 2001, method 01011999 ]] local function help() @@ -102,11 +102,11 @@ local function main(args) for o, a in getopt.getopt(args, 'hs:e:dy') do if o == 'h' then return help() end - if o == 's' then + if o == 's' then start_year = tonumber(a) if not start_year then return oops(' Invalid start year') end end - if o == 'e' then + if o == 'e' then end_year = tonumber(a) if not end_year then return oops(' Invalid end year') end end @@ -115,13 +115,13 @@ local function main(args) end if not start_year then return oops(' Starting year is required') end - if start_year < 1900 or start_year > 2100 then - return oops(' Start year must be between 1900 and 2100') + if start_year < 1900 or start_year > 2100 then + return oops(' Start year must be between 1900 and 2100') end if args[#args] == "-e" then return oops(' Ending year cannot be empty') end if not end_year then end_year = current_year end - if end_year < 1900 or end_year > 2100 then - return oops(' End year must be between 1900 and 2100') + if end_year < 1900 or end_year > 2100 then + return oops(' End year must be between 1900 and 2100') end if end_year < start_year then return oops(' End year cannot be earlier than start year') end @@ -130,9 +130,9 @@ local function main(args) if generate_dictionary(start_year, end_year, mode) then print(ac.green .. " File created: " .. dictionary_path .. res) print(cyan .. " Starting password testing on T5577..." .. res) - core.console('lf t55 chk -f ' .. dictionary_path) + core.console('lf t55 chk -f ' .. dictionary_path) else return oops('Problem saving the file') end end - main(args) \ No newline at end of file + main(args) diff --git a/client/luascripts/t55_chk_date.lua b/client/luascripts/lf_t55xx_chk_date.lua similarity index 95% rename from client/luascripts/t55_chk_date.lua rename to client/luascripts/lf_t55xx_chk_date.lua index a6b217832..29b26d506 100644 --- a/client/luascripts/t55_chk_date.lua +++ b/client/luascripts/lf_t55xx_chk_date.lua @@ -6,7 +6,7 @@ local dash = string.rep('--', 32) author = ' Author: jareckib - created 01.02.2025' version = ' version v1.01' -desc = [[ +desc = [[ A simple script for searching the password for T5577. The script creates a dictionary starting from the entered starting year to the entered ending year. There are two search methods - DDMMYYYY or YYYYMMDD. Checking the entire year @@ -14,10 +14,10 @@ desc = [[ useful if the password is, for example, a date of birth. ]] usage = [[ - script run t55_chk_date + script run lf_t55xx_chk_date ]] arguments = [[ - script run t55_chk_date -h : this help + script run lf_t55xx_chk_date -h : this help ]] local DEBUG = true @@ -84,11 +84,11 @@ local function get_valid_year_input(prompt) local year while true do io.write(prompt) - local input = io.read() + local input = io.read() if input == "" then print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Year cannot be empty') else - year = tonumber(input) + year = tonumber(input) if not year then print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Invalid input (digits only)') elseif year < 1900 then @@ -155,7 +155,7 @@ local function main(args) if generate_dictionary(start_year, end_year, mode) then print(ac.green .. " File created: " .. dictionary_path .. ac.reset) print(ac.cyan .. " Starting password testing on T5577..." .. ac.reset) - core.console('lf t55 chk -f ' .. dictionary_path) + core.console('lf t55 chk -f ' .. dictionary_path) else print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Problem saving the file.') end diff --git a/client/luascripts/lf_t55xx_fix.lua b/client/luascripts/lf_t55xx_fix.lua index 5a4ec8e4e..e9f379c1f 100644 --- a/client/luascripts/lf_t55xx_fix.lua +++ b/client/luascripts/lf_t55xx_fix.lua @@ -141,4 +141,4 @@ local function main(args) print(dash) end -main(args) +main(args) \ No newline at end of file diff --git a/client/luascripts/t55_fix.lua b/client/luascripts/t55_fix.lua deleted file mode 100644 index 354cf8dfc..000000000 --- a/client/luascripts/t55_fix.lua +++ /dev/null @@ -1,101 +0,0 @@ -local getopt = require('getopt') -local utils = require('utils') -local ac = require('ansicolors') -local os = require('os') -local dash = string.rep('--', 32) -local dir = os.getenv('HOME') .. '/.proxmark3/logs/' -local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) -local command = core.console - -author = ' Author: jareckib - 15.02.2025' -version = ' version v1.00' -desc = [[ - This simple script first checks if a password has been set for the T5577. - It uses the dictionary t55xx_default_pwds.dic for this purpose. If a password - is found, it uses the wipe command to erase the T5577. Then the reanimation - procedure is applied. If the password is not found or doesn't exist the script - only performs the reanimation procedure. The script revives 99% of blocked tags. - ]] -usage = [[ - script run t55_fix -]] -arguments = [[ - script run t55_fix -h : this help -]] - -local function help() - print() - print(author) - print(version) - print(desc) - print(ac.cyan..' Usage'..ac.reset) - print(usage) - print(ac.cyan..' Arguments'..ac.reset) - print(arguments) -end - -local function read_log_file(logfile) - local file = io.open(logfile, "r") - if not file then - return nil - end - local content = file:read("*all") - file:close() - return content -end - -local function extract_password(log_content) - for line in log_content:gmatch("[^\r\n]+") do - local password = line:match('%[%+%] found valid password: %[ (%x%x%x%x%x%x%x%x) %]') - if password then - return password - end - end - return nil -end - -local function reanimate_t5577(password) - if password then - command('clear') - print(dash) - print(" Using found password to wipe: " .. password) - print(dash) - command('lf t55 wipe -p ' .. password) - else - command('clear') - print(dash) - print(ac.yellow.." No valid password found, proceeding with reanimation."..ac.reset) - print(dash) - end - command('lf t55 write -b 0 -d 000880E8 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -t -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -t -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -t -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -t -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --r0 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --r1 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --r2 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --r3 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -p 00000000') - command('lf t55 detect') - local file = io.open(logfile, "w+") - file:write("") - file:close() - print(dash) - print('all done!') -end - -local function main(args) - for o, a in getopt.getopt(args, 'h') do - if o == 'h' then return help() end - end - command('lf t55 chk') - local log_content = read_log_file(logfile) - local password = log_content and extract_password(log_content) or nil - reanimate_t5577(password) -end - -main(args) diff --git a/client/pyscripts/fm11rf08s_full.py b/client/pyscripts/fm11rf08s_full.py index 6f3dbc93e..714d1acaf 100644 --- a/client/pyscripts/fm11rf08s_full.py +++ b/client/pyscripts/fm11rf08s_full.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +"""This script recovers Fudan FM11RF08S cards, including functionalities for Bambu tags decoding.""" # ------------------------------------------------------------------------------ # Imports @@ -43,17 +44,18 @@ try: from colors import color except ModuleNotFoundError: def color(s, fg=None): + """Return the string as such, without color.""" _ = fg return str(s) def initlog(): - """Print and Log: init globals + """Print and Log: init globals. -globals: -- logbuffer (W) -- logfile (W) -""" + globals: + - logbuffer (W) + - logfile (W) + """ global logbuffer global logfile logbuffer = '' @@ -61,12 +63,12 @@ globals: def startlog(uid, dpath, append=False): - """Print and Log: set logfile and flush logbuffer + """Print and Log: set logfile and flush logbuffer. -globals: -- logbuffer (RW) -- logfile (RW) -""" + globals: + - logbuffer (RW) + - logfile (RW) + """ global logfile global logbuffer @@ -81,13 +83,12 @@ globals: def lprint(s='', end='\n', flush=False, prompt="[" + color("=", fg="yellow") + "] ", log=True): - """Print and Log - -globals: -- logbuffer (RW) -- logfile (R) -""" + """Print and Log. + globals: + - logbuffer (RW) + - logfile (R) + """ s = f"{prompt}" + f"\n{prompt}".join(s.split('\n')) print(s, end=end, flush=flush) @@ -102,11 +103,11 @@ globals: def main(): - """== MAIN == + """== MAIN ==. -globals: -- p (W) -""" + globals: + - p (W) + """ global p p = pm3.pm3() # console interface initlog() @@ -143,7 +144,7 @@ globals: else: # FIXME: recovery() is only for RF08S. TODO for the other ones with a "darknested" attack keyfile = recoverKeys(uid=uid, kdf=[["Bambu v1", kdfBambu1]]) - if keyfile == False: + if keyfile is False: lprint("Script failed - aborting") return key = loadKeys(keyfile) @@ -181,11 +182,11 @@ globals: def getPrefs(): - """Get PM3 preferences + """Get PM3 preferences. -globals: -- p (R) -""" + globals: + - p (R) + """ p.console("prefs show --json") prefs = json.loads(p.grabbed_output) dpath = prefs['file.default.dumppath'] + os.path.sep @@ -193,7 +194,7 @@ globals: def checkVer(): - """Assert python version""" + """Assert python version.""" required_version = (3, 8) if sys.version_info < required_version: print(f"Python version: {sys.version}") @@ -203,7 +204,7 @@ def checkVer(): def parseCli(): - """Parse the CLi arguments""" + """Parse the CLi arguments.""" parser = argparse.ArgumentParser(description='Full recovery of Fudan FM11RF08S cards.') parser.add_argument('-n', '--nokeys', action='store_true', help='extract data even if keys are missing') @@ -222,15 +223,15 @@ def parseCli(): def getBackdoorKey(): - """Find backdoor key -[=] # | sector 00 / 0x00 | ascii -[=] ----+-------------------------------------------------+----------------- -[=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \\........Y.%._p. + r"""Find backdoor key. -globals: -- p (R) -""" + [=] # | sector 00 / 0x00 | ascii + [=] ----+-------------------------------------------------+----------------- + [=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \........Y.%._p. + globals: + - p (R) + """ # FM11RF08S FM11RF08 FM11RF32 dklist = ["A396EFA4E24F", "A31667A8CEC1", "518b3354E760"] @@ -259,14 +260,14 @@ globals: def getUIDfromBlock0(blk0): - """Extract UID from block 0""" + """Extract UID from block 0.""" uids = blk0[0:11] # UID string : "11 22 33 44" uid = bytes.fromhex(uids.replace(' ', '')) # UID (bytes) : 11223344 return uid def decodeBlock0(blk0): - """Extract data from block 0""" + """Extract data from block 0.""" lprint() lprint(" UID BCC ++---- RF08* ID -----++") lprint(" ! ! SAK !! !!") @@ -346,7 +347,7 @@ def decodeBlock0(blk0): def fudanValidate(blk0, live=False): - """Fudan validation""" + """Fudan validation.""" url = "https://rfid.fm-uivs.com/nfcTools/api/M1KeyRest" hdr = "Content-Type: application/text; charset=utf-8" post = f"{blk0.replace(' ', '')}" @@ -387,10 +388,10 @@ def fudanValidate(blk0, live=False): def loadKeys(keyfile): - """Load keys from file + """Load keys from file. -If keys cannot be loaded AND --recover is specified, then run key recovery -""" + If keys cannot be loaded AND --recover is specified, then run key recovery + """ key = [[b'' for _ in range(2)] for _ in range(17)] # create a fresh array lprint("\nLoad keys from file... " + color(f"{keyfile}", fg="yellow")) @@ -408,15 +409,15 @@ If keys cannot be loaded AND --recover is specified, then run key recovery def recoverKeys(uid, kdf=[[]]): - """Run key recovery script""" + """Run key recovery script.""" badrk = 0 # 'bad recovered key' count (ie. not recovered) - keys = False - lprint(f"\nTrying KDFs:"); + keys = [] + lprint("\nTrying KDFs:") for fn in kdf: lprint(f" {fn[0]:s}", end='') keys = fn[1](uid) - if keys != False: + if len(keys) > 0: lprint(" .. Success", prompt='') break lprint(" .. Fail", prompt='') @@ -427,7 +428,7 @@ def recoverKeys(uid, kdf=[[]]): r = recovery(quiet=False, keyset=keys) lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,') - if r == False: + if r is False: return False keyfile = r['keyfile'] @@ -453,14 +454,28 @@ def recoverKeys(uid, kdf=[[]]): lprint("", prompt='') return keyfile + def kdfBambu1(uid): + """Derive keys from a given UID using the Bambu HKDF algorithm and validates the card data. + + This function generates two keys (keyA and keyB) using the Bambu HKDF algorithm with a predefined salt and context. + It then attempts to read block 13 from sector 3 of the card using keyA. If successful, it decodes the data + and checks if it matches a specific date format. If the data is valid, it returns a list of derived keys. + + Args: + uid (bytes): The UID of the card. + + Returns: + list: A list of derived keys if the card data is valid. + bool: False if any step in the process fails. + """ from Cryptodome.Protocol.KDF import HKDF - from Cryptodome.Hash import SHA256 + from Cryptodome.Hash import SHA256 # Generate all keys try: # extracted from Bambu firmware - salt = bytes([0x9a,0x75,0x9c,0xf2,0xc4,0xf7,0xca,0xff,0x22,0x2c,0xb9,0x76,0x9b,0x41,0xbc,0x96]) + salt = bytes([0x9a, 0x75, 0x9c, 0xf2, 0xc4, 0xf7, 0xca, 0xff, 0x22, 0x2c, 0xb9, 0x76, 0x9b, 0x41, 0xbc, 0x96]) keyA = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-A\0") keyB = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-B\0") except Exception as e: @@ -469,7 +484,7 @@ def kdfBambu1(uid): # --- Grab block 13 (in sector 3) --- cmd = f"hf mf rdbl -c 0 --key {keyA[3].hex()} --blk 12" - #lprint(f" `{cmd}`", flush=True, log=False, end='') + # lprint(f" `{cmd}`", flush=True, log=False, end='') for retry in range(5): p.console(cmd) @@ -502,13 +517,13 @@ def kdfBambu1(uid): return keys + def verifyKeys(key): - """Verify keys - -globals: -- p (R) -""" + """Verify keys. + globals: + - p (R) + """ badk = 0 mad = False @@ -563,16 +578,15 @@ globals: def readBlocks(bdkey, fast=False): + r"""Read all block data - INCLUDING advanced verification blocks. + + [=] # | sector 00 / 0x00 | ascii + [=] ----+-------------------------------------------------+----------------- + [=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \........Y.%._p. + + globals: + - p (R) """ -Read all block data - INCLUDING advanced verification blocks - -[=] # | sector 00 / 0x00 | ascii -[=] ----+-------------------------------------------------+----------------- -[=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \\........Y.%._p. - -globals: -- p (R) -""" data = [] blkn = list(range(0, 63 + 1)) + list(range(128, 135 + 1)) @@ -634,9 +648,10 @@ globals: def patchKeys(data, key): - """Patch keys in to data - 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... -""" + """Patch keys in to data. + + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... + """ lprint("\nPatching keys in to data") for sec in range(0, 16 + 1): @@ -662,7 +677,7 @@ def patchKeys(data, key): def dumpData(data, blkn): - """Dump data""" + """Dump data.""" lprint() lprint("===========") lprint(" Card Data") @@ -708,14 +723,14 @@ def detectBambu(data): def dumpBambu(data): - """Dump bambu details + """Dump bambu details. -https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md + https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md - 6 18 30 42 53 - | | | | | - 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... -""" + 6 18 30 42 53 + | | | | | + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... + """ try: lprint() lprint("===========") @@ -833,14 +848,14 @@ https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md # The Access bits on both (used) Sectors is the same: 78 77 88 -# Let's reorganise that according to the official spec Fig 9. +# Let's reorganize that according to the official spec Fig 9. # Access C1 C2 C3 # ========== =========== # 78 77 88 --> 78 87 87 # ab cd ef --> cb fa ed # The second nybble of each byte is the inverse of the first nybble. -# It is there to trap tranmission errors, so we can just ignore it/them. +# It is there to trap transmission errors, so we can just ignore it/them. # So our Access Control value is : {c, f, e} == {7, 8, 8} @@ -903,13 +918,13 @@ https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md # IF YOU PLAN TO CHANGE ACCESS BITS, RTFM, THERE IS MUCH TO CONSIDER ! # ============================================================================== def dumpAcl(data): - """Dump ACL + """Dump ACL. - 6 18 24 27 30 33 42 53 - | | | | | | | | - 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... - ab cd ef -""" + 6 18 24 27 30 33 42 53 + | | | | | | | | + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... + ab cd ef + """ aclkh = [] # key header aclk = [""] * 8 # key lookup aclkx = [] # key output @@ -1010,10 +1025,10 @@ def dumpAcl(data): def diskDump(data, uid, dpath): - """Full Dump""" + """Full Dump.""" dump18 = f'{dpath}hf-mf-{uid.hex().upper()}-dump18.bin' - lprint(f'\nDump card data to file... ' + color(dump18, fg='yellow')) + lprint('\nDump card data to file... ' + color(dump18, fg='yellow')) bad = False try: @@ -1037,12 +1052,11 @@ def diskDump(data, uid, dpath): def dumpMad(dump18): - """Dump MAD - -globals: -- p (R) -""" + """Dump MAD. + globals: + - p (R) + """ lprint() lprint("====================================") lprint(" MiFare Application Directory (MAD)") diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index b94381117..cbb7246d5 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -1,17 +1,18 @@ #!/usr/bin/env python3 +""" +Combine several attacks to recover all FM11RF08S keys. -# Combine several attacks to recover all FM11RF08S keys -# -# Conditions: -# * Presence of the backdoor with known key -# -# Duration strongly depends on some key being reused and where. -# Examples: -# * 32 random keys: ~20 min -# * 16 random keys with keyA==keyB in each sector: ~30 min -# * 24 random keys, some reused across sectors: <1 min -# -# Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info +Conditions: +* Presence of the backdoor with known key + +Duration strongly depends on some key being reused and where. +Examples: +* 32 random keys: ~20 min +* 16 random keys with keyA==keyB in each sector: ~30 min +* 24 random keys, some reused across sectors: <1 min + +Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info +""" import os import sys @@ -19,13 +20,17 @@ import time import subprocess import argparse import json +import re import pm3 +from pm3_resources import find_tool, find_dict + # optional color support try: # pip install ansicolors from colors import color except ModuleNotFoundError: def color(s, fg=None): + """Return the string as such, without color.""" _ = fg return str(s) @@ -42,41 +47,52 @@ BACKDOOR_KEYS = ["A396EFA4E24F", "A31667A8CEC1", "518B3354E760"] NUM_SECTORS = 16 NUM_EXTRA_SECTORS = 1 -DICT_DEF = "mfc_default_keys.dic" DEFAULT_KEYS = set() -if __name__ == '__main__': - DIR_PATH = os.path.dirname(os.path.abspath(sys.argv[0])) -else: - DIR_PATH = os.path.dirname(os.path.abspath(__file__)) -if os.path.basename(os.path.dirname(DIR_PATH)) == 'client': - # dev setup - TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, - "..", "..", "tools", "mfc", "card_only")) - DICT_DEF_PATH = os.path.normpath(os.path.join(DIR_PATH, - "..", "dictionaries", DICT_DEF)) -else: - # assuming installed - TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, - "..", "tools")) - DICT_DEF_PATH = os.path.normpath(os.path.join(DIR_PATH, - "dictionaries", DICT_DEF)) - -tools = { - "staticnested_1nt": os.path.join(f"{TOOLS_PATH}", "staticnested_1nt"), - "staticnested_2x1nt": os.path.join(f"{TOOLS_PATH}", "staticnested_2x1nt_rf08s"), - "staticnested_2x1nt1key": os.path.join(f"{TOOLS_PATH}", "staticnested_2x1nt_rf08s_1key"), -} -for tool, bin in tools.items(): - if not os.path.isfile(bin): - if os.path.isfile(bin + ".exe"): - tools[tool] = bin + ".exe" - else: - print(f"Cannot find {bin}, abort!") - exit() +staticnested_1nt_path = find_tool("staticnested_1nt") +staticnested_2x1nt_path = find_tool("staticnested_2x1nt_rf08s") +staticnested_2x1nt1key_path = find_tool("staticnested_2x1nt_rf08s_1key") -def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debug=False, supply_chain=False, quiet=True, keyset=False): +def match_key(line): + """ + Extract a 12-character hexadecimal key from a given string. + + Args: + line (str): The input string to search for the hexadecimal key. + + Returns: + str or None: The 12-character hexadecimal key in uppercase if found, otherwise None. + """ + match = re.search(r'([0-9a-fA-F]{12})', line) + if match: + return match.group(1).upper() + else: + return None + + +def recovery(init_check=False, final_check=False, keep=False, no_oob=False, + debug=False, supply_chain=False, quiet=True, keyset=[]): + """ + Perform recovery operation for FM11RF08S cards. + + Args: + init_check (bool): If True, check for default keys initially. + final_check (bool): If True, perform a final check and dump keys. + keep (bool): If True, keep the generated dictionaries after processing. + no_oob (bool): If True, do not include out-of-bounds sectors. + debug (bool): If True, print debug information. + supply_chain (bool): If True, use supply-chain attack data. + quiet (bool): If True, suppress output messages. + keyset (list): A list of key pairs to use for the recovery process. + + Returns: + dict: A dictionary containing the following keys: + - 'keyfile': Path to the generated binary key file. + - 'found_keys': List of found keys for each sector. + - 'dump_file': Path to the generated dump file. + - 'data': List of data blocks for each sector. + """ def show(s='', prompt="[" + color("=", fg="yellow") + "] ", **kwargs): if not quiet: s = f"{prompt}" + f"\n{prompt}".join(s.split('\n')) @@ -107,10 +123,10 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] - if keyset != False: - n = min(len(found_keys),len(keyset)) + if len(keyset) > 0: + n = min(len(found_keys), len(keyset)) show(f"{n} Key pairs supplied: ") - for i in range(0, n): + for i in range(n): found_keys[i] = keyset[i] show(f" Sector {i:2d} : A = {found_keys[i][0]:12s} B = {found_keys[i][1]:12s}") @@ -136,8 +152,9 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu for line in p.grabbed_output.split('\n'): if "Wrong" in line or "error" in line: break - if "Saved" in line: - nonces_with_data = line[line.index("`"):].strip("`") + matched = "Saved to json file " + if matched in line: + nonces_with_data = line[line.index(matched)+len(matched):] if nonces_with_data != "": break @@ -171,8 +188,8 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu data[blk] = dict_nwd["blocks"][f"{blk}"] show("Generating first dump file") - dumpfile = f"{save_path}hf-mf-{uid:08X}-dump.bin" - with (open(dumpfile, "wb")) as f: + dump_file = f"{save_path}hf-mf-{uid:08X}-dump.bin" + with (open(dump_file, "wb")) as f: for sec in range(NUM_SECTORS): for b in range(4): d = data[(sec * 4) + b] @@ -185,7 +202,7 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu kb = "FFFFFFFFFFFF" d = ka + d[12:20] + kb f.write(bytes.fromhex(d)) - show(f"Data has been dumped to `{dumpfile}`") + show(f"Data has been dumped to `{dump_file}`") elapsed_time1 = time.time() - start_time minutes = int(elapsed_time1 // 60) @@ -193,14 +210,18 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu show("----Step 1: " + color(f"{minutes:2}", fg="yellow") + " minutes " + color(f"{seconds:2}", fg="yellow") + " seconds -----------") - if os.path.isfile(DICT_DEF_PATH): - show(f"Loading {DICT_DEF}") - with open(DICT_DEF_PATH, 'r', encoding='utf-8') as file: + dict_def = "mfc_default_keys.dic" + try: + dict_path = find_dict(dict_def) + with open(dict_path, 'r', encoding='utf-8') as file: for line in file: if line[0] != '#' and len(line) >= 12: DEFAULT_KEYS.add(line[:12]) - else: - show(f"Warning, {DICT_DEF} not found.") + show(f"Loaded {dict_def}") + except FileNotFoundError: + show(f"Warning, {dict_def} not found.") + except Exception as e: + raise Exception(f"Error loading {dict_def}: {e}") dict_dnwd = None def_nt = ["" for _ in range(NUM_SECTORS)] @@ -233,12 +254,12 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu continue if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] != nt[sec][1]: for key_type in [0, 1]: - cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", + cmd = [staticnested_1nt_path, f"{uid:08X}", f"{real_sec}", nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] if debug: print(' '.join(cmd)) subprocess.run(cmd, capture_output=True) - cmd = [tools["staticnested_2x1nt"], + cmd = [staticnested_2x1nt_path, f"keys_{uid:08x}_{real_sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][1]}.dic"] if debug: print(' '.join(cmd)) @@ -254,15 +275,16 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu all_keys.update(keys_set) if dict_dnwd is not None and sec < NUM_SECTORS: # Prioritize keys from supply-chain attack - cmd = [tools["staticnested_2x1nt1key"], def_nt[sec], "FFFFFFFFFFFF", + cmd = [staticnested_2x1nt1key_path, def_nt[sec], "FFFFFFFFFFFF", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic"] if debug: print(' '.join(cmd)) result = subprocess.run(cmd, capture_output=True, text=True).stdout keys_def_set = set() for line in result.split('\n'): - if "MATCH:" in line: - keys_def_set.add(line[12:]) + matched = match_key(line) + if matched is not None: + keys_def_set.add(matched) keys_set.difference_update(keys_def_set) else: # Prioritize default keys @@ -285,7 +307,7 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu key_type = 0 else: key_type = 1 - cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", + cmd = [staticnested_1nt_path, f"{uid:08X}", f"{real_sec}", nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] if debug: print(' '.join(cmd)) @@ -299,15 +321,16 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu all_keys.update(keys_set) if dict_dnwd is not None and sec < NUM_SECTORS: # Prioritize keys from supply-chain attack - cmd = [tools["staticnested_2x1nt1key"], def_nt[sec], "FFFFFFFFFFFF", + cmd = [staticnested_2x1nt1key_path, def_nt[sec], "FFFFFFFFFFFF", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic"] if debug: print(' '.join(cmd)) result = subprocess.run(cmd, capture_output=True, text=True).stdout keys_def_set = set() for line in result.split('\n'): - if "MATCH:" in line: - keys_def_set.add(line[12:]) + matched = match_key(line) + if matched is not None: + keys_def_set.add(matched) keys_set.difference_update(keys_def_set) else: # Prioritize default keys @@ -440,8 +463,9 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True - if "found:" in line: - found_keys[sec][key_type] = line[30:].strip() + matched = match_key(line) + if matched is not None: + found_keys[sec][key_type] = matched show_key(real_sec, key_type, found_keys[sec][key_type]) if nt[sec][0] == nt[sec][1] and found_keys[sec][key_type ^ 1] == "": found_keys[sec][key_type ^ 1] = found_keys[sec][key_type] @@ -466,8 +490,9 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True - if "found:" in line: - found_keys[sec][key_type] = line[30:].strip() + matched = match_key(line) + if matched is not None: + found_keys[sec][key_type] = matched show_key(real_sec, key_type, found_keys[sec][key_type]) if abort: break @@ -487,11 +512,12 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True - if "found:" in line: - found_keys[sec][0] = line[30:].strip() - found_keys[sec][1] = line[30:].strip() - show_key(real_sec, 0, found_keys[sec][key_type]) - show_key(real_sec, 1, found_keys[sec][key_type]) + matched = match_key(line) + if matched is not None: + found_keys[sec][0] = matched + found_keys[sec][1] = matched + show_key(real_sec, 0, found_keys[sec][0]) + show_key(real_sec, 1, found_keys[sec][1]) if abort: break @@ -509,14 +535,15 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_filtered.dic" else: dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}.dic" - cmd = [tools["staticnested_2x1nt1key"], nt[sec][key_type_source], found_keys[sec][key_type_source], dic] + cmd = [staticnested_2x1nt1key_path, nt[sec][key_type_source], found_keys[sec][key_type_source], dic] if debug: print(' '.join(cmd)) result = subprocess.run(cmd, capture_output=True, text=True).stdout keys = set() for line in result.split('\n'): - if "MATCH:" in line: - keys.add(line[12:]) + matched = match_key(line) + if matched is not None: + keys.add(matched) if len(keys) > 1: kt = ['a', 'b'][key_type_target] cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} --no-default" @@ -528,8 +555,9 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True - if "found:" in line: - found_keys[sec][key_type_target] = line[30:].strip() + matched = match_key(line) + if matched is not None: + found_keys[sec][key_type_target] = matched elif len(keys) == 1: found_keys[sec][key_type_target] = keys.pop() if found_keys[sec][key_type_target] != "": @@ -551,7 +579,10 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu cmd = f"hf mf fchk -f keys_{uid:08x}.dic --no-default --dump" if debug: print(cmd) - p.console(cmd, capture=False, quiet=False) + p.console(cmd, capture=True, quiet=False) + for line in p.grabbed_output.split('\n'): + if "Found keys have been dumped to" in line: + keyfile = line[line.index("`"):].strip("`") else: show() show(color("found keys:", fg="green"), prompt=plus) @@ -590,22 +621,22 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu if unknown: show(" --[ " + color("FFFFFFFFFFFF", fg="yellow") + " ]-- has been inserted for unknown keys", prompt="[" + color("=", fg="yellow") + "]") - show("Generating final dump file", prompt=plus) - dumpfile = f"{save_path}hf-mf-{uid:08X}-dump.bin" - with (open(dumpfile, "wb")) as f: - for sec in range(NUM_SECTORS): - for b in range(4): - d = data[(sec * 4) + b] - if b == 3: - ka = found_keys[sec][0] - kb = found_keys[sec][1] - if ka == "": - ka = "FFFFFFFFFFFF" - if kb == "": - kb = "FFFFFFFFFFFF" - d = ka + d[12:20] + kb - f.write(bytes.fromhex(d)) - show("Data has been dumped to `" + color(dumpfile, fg="yellow")+"`", prompt=plus) + show("Generating final dump file", prompt=plus) + dump_file = f"{save_path}hf-mf-{uid:08X}-dump.bin" + with (open(dump_file, "wb")) as f: + for sec in range(NUM_SECTORS): + for b in range(4): + d = data[(sec * 4) + b] + if b == 3: + ka = found_keys[sec][0] + kb = found_keys[sec][1] + if ka == "": + ka = "FFFFFFFFFFFF" + if kb == "": + kb = "FFFFFFFFFFFF" + d = ka + d[12:20] + kb + f.write(bytes.fromhex(d)) + show("Data has been dumped to `" + color(dump_file, fg="yellow")+"`", prompt=plus) # Remove generated dictionaries after processing if not keep: @@ -631,11 +662,27 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu seconds = int(elapsed_time % 60) show("---- TOTAL: " + color(f"{minutes:2}", fg="yellow") + " minutes " + color(f"{seconds:2}", fg="yellow") + " seconds -----------") - - return {'keyfile': keyfile, 'found_keys': found_keys, 'dumpfile': dumpfile, 'data': data} + return {'keyfile': keyfile, 'found_keys': found_keys, 'dump_file': dump_file, 'data': data} def main(): + """ + Parse command-line arguments and initiate the recovery process. + + Command-line arguments: + -x, --init-check: Run an initial fchk for default keys. + -y, --final-check: Run a final fchk with the found keys. + -n, --no-oob: Do not save out of bounds keys. + -k, --keep: Keep generated dictionaries after processing. + -d, --debug: Enable debug mode. + -s, --supply-chain: Enable supply-chain mode. Look for hf-mf-XXXXXXXX-default_nonces.json. + + The supply-chain mode json can be produced from the json saved by + "hf mf isen --collect_fm11rf08s --key A396EFA4E24F" on a wiped card, then processed with + jq '{Created: .Created, FileType: "fm11rf08s_default_nonces", nt: .nt | del(.["32"]) | map_values(.a)}'. + + This function calls the recovery function with the parsed arguments. + """ parser = argparse.ArgumentParser(description='A script combining staticnested* tools ' 'to recover all keys from a FM11RF08S card.') parser.add_argument('-x', '--init-check', action='store_true', help='Run an initial fchk for default keys') diff --git a/client/pyscripts/hf_mfu_uscuid.py b/client/pyscripts/hf_mfu_uscuid.py new file mode 100644 index 000000000..a477c1b16 --- /dev/null +++ b/client/pyscripts/hf_mfu_uscuid.py @@ -0,0 +1,198 @@ +### Crappy helper script for USCUID-UL, v0.2.4.2 +## Written and tested by Eltrick +# It is recommended that you are able to backdoor read main blocks +# in case changing from one type to another messes up keys/pwd +# unless you know what you're doing. + +## For the uninitiated, the keys are stored in the following locations +## per the corresponding datasheets +# UL11 - PWD - page 18d +# UL21 - PWD - page 39d +# UL-C - KEY - pages 44d to 47d +# NTAG 213 - PWD - page 43d +# NTAG 215 - PWD - page 133d +# NTAG 216 - PWD - page 229d + +import argparse +import pm3 + +try: + # pip install ansicolors + from colors import color +except ModuleNotFoundError: + def color(s, fg=None): + _ = fg + return str(s) + +HEX_DIGITS = "0123456789ABCDEF" +MEMORY_CONFIG = { "C3": "UL11", "3C": "UL21", "00": "UL-C", "A5": "NTAG 213", "5A": "NTAG 215", "AA": "NTAG 216", "55": "Unknown IC with 238 pages" } +KNOWN_CONFIGS = ["C30004030101000B03", "3C0004030101000E03", "000000000000000000", "A50004040201000F03", "5A0004040201001103", "AA0004040201001303"] + +parser = argparse.ArgumentParser(description='A script to help with raw USCUID-UL commands. Out of everything until -s, only one functionality can be used at a time, prioritised in order listed below.') +parser.add_argument('-r', '--read', action='store_true', help='Read and parse config from card') +parser.add_argument('-t', '--type', help='Type to change to: 1-UL11; 2-UL21; 3-UL-C; 4-NTAG213; 5-NTAG215; 6-NTAG216') +parser.add_argument('-c', '--cfg', help='Config to write') +parser.add_argument('-p', '--parse', help='Config to parse') +parser.add_argument('-b', '--bdr', help='Page num to read with backdoor') +parser.add_argument('-w', '--wbd', help='First page num to write with backdoor') +parser.add_argument('-u', '--uid', help='New UID to write') +parser.add_argument('-d', '--data', help='Page data to write if using -w, multiple of 4 bytes') +parser.add_argument('-s', '--sig', help='Signature to write with backdoor') +parser.add_argument('--gen1a', action='store_true', help='Use gen1a (40/43) magic wakeup') +parser.add_argument('--gdm', action='store_true', help='Use gdm alt (20/23) magic wakeup') + +args = parser.parse_args() +card_config = args.read +ul_type = args.type +config = args.cfg +parse = args.parse +backdoor_block = args.bdr +write_backdoor = args.wbd +data = args.data +signature = args.sig +gen1a = args.gen1a +alt = args.gdm +uid = args.uid + +field_on = False +p = pm3.pm3() + +ERROR = "[" + color("-", "red") + "] " +SUCCESS = "[" + color("+", "green") + "] " + +def verify_config(config: str) -> bool: + if len(config) != 32: + print(ERROR + "Configuration data must be 16 bytes.") + return False + if set(config) > set(HEX_DIGITS): + print(ERROR + "Configuration data must be in hex.") + return False + return True + +def parse_config(config: str): + print(SUCCESS + "" + config) + cfg_magic_wup = config[0:4] + cfg_wup_style = config[4:6] + cfg_regular_available = config[6:8] + cfg_auth_type = config[8:10] + cfg_cuid = config[12:14] + cfg_memory_config = config[14:16] + + log_magic_wup = "Magic wakeup " + ("en" if cfg_magic_wup != "8500" else "dis") + "abled" + (" with config access" if cfg_magic_wup == "7AFF" else "") + log_wup_style = "Magic wakeup style " + ("Gen1a 40(7)/43" if cfg_wup_style == "00" else ("GDM 20(7)/23" if cfg_wup_style == "85" else "unknown")) + log_regular_available = "Config " + ("" if cfg_regular_available == "A0" else "un") + "available in regular mode" + log_auth_type = "Auth type " + ("1B - PWD" if cfg_auth_type == "00" else "1A - 3DES") + log_cuid = "CUID " + ("dis" if cfg_cuid == "A0" else "en") + "abled" + log_memory_config = "Maximum memory configuration: " + (MEMORY_CONFIG[cfg_memory_config] if cfg_memory_config in MEMORY_CONFIG.keys() else "unknown") + + print(SUCCESS + "^^^^............................ " + log_magic_wup) + print(SUCCESS + "....^^.......................... " + log_wup_style) + print(SUCCESS + "......^^........................ " + log_regular_available) + print(SUCCESS + "........^^...................... " + log_auth_type) + print(SUCCESS + "..........^^.................... unknown") + print(SUCCESS + "............^^.................. " + log_cuid) + print(SUCCESS + "..............^^................ " + log_memory_config) + print(SUCCESS + "................^^^^^^^^^^^^^^^^ version info") + +def try_auth_magic(enforced = False): + if enforced and not (gen1a | alt): + print(ERROR + "Magic wakeup required. Please select one.") + exit() + if gen1a ^ alt: + p.console("hf 14a raw -akb 7 " + ("40" if gen1a else "20")) + p.console("hf 14a raw -k " + ("43" if gen1a else "23")) + +def write_config(config: str): + try_auth_magic() + for i in range(4): + p.console("hf 14a raw -" + ("s" if (i == 0 and not (gen1a or alt)) else "") + ("k" if i != 3 else "") + "c" + f" E2{i:02x}" + config[8*i:8*i+8], False, False) + +def grab_config() -> str: + try_auth_magic() + p.console("hf 14a raw -c" + ("s" if not (gen1a or alt) else "") + " E050") + return p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "") + +if gen1a and alt: + print(ERROR + "Please only choose one magic wakeup type.") + exit() + +if card_config: + config_grab = grab_config() + if not verify_config(config_grab): + print(ERROR + "Failed to grab config data from card.") + exit() + parse_config(config_grab) + +elif ul_type != None: + ul_type_num = int(ul_type) - 1 + if ul_type_num < 0 or ul_type_num >= len(KNOWN_CONFIGS): + print(ERROR + "Type specified is non-existent.") + exit() + old_config = grab_config() + new_config = old_config[0:8] + ("0A" if ul_type_num == 2 else "00") + old_config[10:14] + KNOWN_CONFIGS[ul_type_num] + write_config(new_config) + +elif config != None: + config = config.upper() + if not verify_config(config): + exit() + write_config(config) + +elif parse != None: + parse = parse.upper() + if not verify_config(parse): + exit() + parse_config(parse) + +elif backdoor_block != None: + block = int(backdoor_block) + try_auth_magic(True) + p.console(f"hf 14a raw -c 30{block:02x}") + print(p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "")) + +elif write_backdoor != None: + write_backdoor_num = int(write_backdoor) + if data == None: + print(ERROR + "Specify data to write to the block.") + exit() + if len(data) % 8 != 0: + print(ERROR + "Data must be a multiple of 4 bytes.") + exit() + + try_auth_magic(True) + for i in range(len(data) // 8): + p.console("hf 14a raw -" + ("k" if i != (len(data) // 8 - 1) else "") + f"c A2{(write_backdoor_num + i):02x}{data[8*i:8*i+8]}", False, False) + +elif uid != None: + if len(uid) != 14: + print(ERROR + "UID must be 7 bytes.") + exit() + try_auth_magic() + p.console(f"hf 14a raw -kc" + ("s" if not (gen1a or alt) else "") + " 3002") + block_2 = p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "")[:8] + uid_bytes = [int(uid[2*x:2*x+2], 16) for x in range(7)] + + bcc_0 = 0x88 ^ uid_bytes[0] ^ uid_bytes[1] ^ uid_bytes[2] + new_block_0 = "" + for i in range(3): + new_block_0 += f"{uid_bytes[i]:02x}" + new_block_0 += f"{bcc_0:02x}" + + bcc_1 = uid_bytes[3] ^ uid_bytes[4] ^ uid_bytes[5] ^ uid_bytes[6] + new_block_1 = uid[6:] + new_block_2 = f"{bcc_1:02x}" + block_2[2:] + p.console("hf 14a raw -kc A200" + new_block_0, False, False) + p.console("hf 14a raw -kc A201" + new_block_1, False, False) + p.console("hf 14a raw -c A202" + new_block_2, False, False) + +elif signature != None: + if len(signature) != 64: + print(ERROR + "Signature must be 32 bytes.") + exit() + try_auth_magic(True) + signature_pages = [signature[8*x:8*x+8] for x in range(8)] + for i in range(8, 16): + p.console("hf 14a raw -c" + ("k" if i != 15 else "") + f" A2F{i:01x}{signature_pages[i - 8]}", False, False) + +# Always try to HALT +p.console("hf 14a raw -c 5000") diff --git a/client/pyscripts/intertic.py b/client/pyscripts/intertic.py index 7c940238d..2793aee43 100644 --- a/client/pyscripts/intertic.py +++ b/client/pyscripts/intertic.py @@ -194,7 +194,7 @@ def Describe_Usage_2_1(Usage, ContractMediumEndDate, Certificate): EventGeoRoute_Direction = Usage.nom(2) EventGeoVehicleId = Usage.nom(16) EventCountPassengers_mb = Usage.nom(4) - + EventValidityTimeFirstStamp = Usage.nom(11) print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) @@ -284,6 +284,7 @@ FRA_OrganizationalAuthority_Contract_Provider = { }, 0x091: { 1: InterticHelper('Strasbourg', 'CTS', Describe_Usage_4), # More dump needed, not only tram ! + 5: InterticHelper('Strasbourg', 'CTS / new', Describe_Usage_4), # More dump needed, not only tram ! }, 0x502: { 83: InterticHelper('Annecy', 'Sibra', Describe_Usage_2), @@ -445,7 +446,7 @@ def main(): oa = MAR_OrganizationalAuthority_Contract_Provider.get(OrganizationalAuthority) else: oa = None - + if (oa is not None): s = oa.get(ContractProvider) if (s is not None): diff --git a/client/pyscripts/Paxton_convert.py b/client/pyscripts/paxton_convert.py old mode 100644 new mode 100755 similarity index 97% rename from client/pyscripts/Paxton_convert.py rename to client/pyscripts/paxton_convert.py index 9c00d5326..d28f5ae52 --- a/client/pyscripts/Paxton_convert.py +++ b/client/pyscripts/paxton_convert.py @@ -1,5 +1,7 @@ +#!/usr/bin/env python3 + # paxton_convert.py - Convert Paxton Net2 and Switch2 to EM4102 -# Author jareckib +# Author jareckib # Based on Equipter's tutorial - Downgrade Paxton Net to EM410x # # This code is copyright (c) jareckib, 2025, All rights reserved. @@ -19,68 +21,95 @@ import sys def hex_to_bin(hex_string): return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string)) + def remove_last_two_bits(binary_str): return binary_str[:-2] + def split_into_5bit_chunks(binary_str): return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)] + def remove_parity_bit(chunks): return [chunk[1:] for chunk in chunks if len(chunk) == 5] + def convert_to_hex(chunks): return [format(int(chunk, 2), 'X') for chunk in chunks] + def convert_to_decimal(chunks): return [int(chunk, 2) for chunk in chunks] + def find_until_before_f(hex_values): result = [] for value in hex_values: if value == 'F': break result.append(value) + return result + def process_block(block): binary_str = hex_to_bin(block) binary_str = remove_last_two_bits(binary_str) chunks = split_into_5bit_chunks(binary_str) no_parity_chunks = remove_parity_bit(chunks) + return no_parity_chunks + def calculate_id_net(blocks): + all_hex_values = [] for block in blocks: hex_values = convert_to_hex(process_block(block)) all_hex_values.extend(hex_values) + selected_hex_values = find_until_before_f(all_hex_values) + if not selected_hex_values: raise ValueError("Error: No valid data found in blocks 4 and 5.") + combined_hex = ''.join(selected_hex_values) + if not combined_hex.isdigit(): raise ValueError("Error: Invalid data in blocks 4 and 5.") + decimal_id = int(combined_hex) stripped_hex_id = format(decimal_id, 'X').upper() padded_hex_id = stripped_hex_id.zfill(10) + return decimal_id, padded_hex_id + def calculate_id_switch(blocks): + all_decimal_values = [] for block in blocks: decimal_values = convert_to_decimal(process_block(block)) all_decimal_values.extend(decimal_values) + if len(all_decimal_values) < 15: raise ValueError("Error: Not enough data after processing blocks 4, 5, 6, and 7.") + id_positions = [9, 11, 13, 15, 2, 4, 6, 8] id_numbers = [all_decimal_values[pos-1] for pos in id_positions] decimal_id = int(''.join(map(str, id_numbers))) padded_hex_id = format(decimal_id, 'X').upper().zfill(10) + return decimal_id, padded_hex_id + def input_block_data(block_number): + while True: block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip() if len(block_data) != 8 or not all(c in '0123456789abcdefABCDEF' for c in block_data): print("Error: Data must be 4 bytes (8 characters) in hex. Try again.") else: return block_data + block_4 = input_block_data(4) block_5 = input_block_data(5) + if block_5[3] == 'F' or block_5[3] == 'f': print("Identified Paxton Net2") blocks = [block_4, block_5] + try: decimal_id, padded_hex_id = calculate_id_net(blocks) print('Calculations for block 4 and block 5:') @@ -89,11 +118,13 @@ if block_5[3] == 'F' or block_5[3] == 'f': print('Use the following command in Proxmark3: lf em 410x clone --id {}'.format(padded_hex_id)) except ValueError as e: print(e) + else: print("Identified Paxton Switch2") block_6 = input_block_data(6) block_7 = input_block_data(7) blocks = [block_4, block_5, block_6, block_7] + try: decimal_id, padded_hex_id = calculate_id_switch(blocks) print('Calculated data from blocks 4, 5, 6, 7:') @@ -102,4 +133,5 @@ else: print('Use the following command in Proxmark3: lf em 410x clone --id {}'.format(padded_hex_id)) except ValueError as e: print(e) + print('If EM4102 does not work, this option is probably disabled. Sorry for the inconvenience.') diff --git a/client/pyscripts/PAXTON_NET.py b/client/pyscripts/paxton_net.py old mode 100644 new mode 100755 similarity index 97% rename from client/pyscripts/PAXTON_NET.py rename to client/pyscripts/paxton_net.py index 532ca276e..26978b4e7 --- a/client/pyscripts/PAXTON_NET.py +++ b/client/pyscripts/paxton_net.py @@ -1,5 +1,7 @@ +#!/usr/bin/env python3 + # paxton_net.py - Convert Paxton Net2 to EM4102 -# Author jareckib +# Author jareckib # Based on Equipter's tutorial - Downgrade Paxton Net to EM410x # # This code is copyright (c) jareckib, 2025, All rights reserved. @@ -17,16 +19,22 @@ # GNU General Public License for more details. import sys + def hex_to_bin(hex_string): return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string)) + def remove_last_two_bits(binary_str): return binary_str[:-2] + def split_into_5bit_chunks(binary_str): return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)] + def remove_parity_bit(chunks): return [chunk[1:] for chunk in chunks if len(chunk) == 5] + def convert_to_hex(chunks): return [format(int(chunk, 2), 'X') for chunk in chunks] + def find_until_before_f(hex_values): result = [] for value in hex_values: @@ -34,6 +42,7 @@ def find_until_before_f(hex_values): break result.append(value) return result + def process_block(block): binary_str = hex_to_bin(block) binary_str = remove_last_two_bits(binary_str) @@ -41,21 +50,30 @@ def process_block(block): no_parity_chunks = remove_parity_bit(chunks) hex_values = convert_to_hex(no_parity_chunks) return hex_values + def calculate_id(blocks): + all_hex_values = [] + for block in blocks: hex_values = process_block(block) all_hex_values.extend(hex_values) + selected_hex_values = find_until_before_f(all_hex_values) + if not selected_hex_values: raise ValueError("Error: No valid data found in blocks 4 and 5.") + combined_hex = ''.join(selected_hex_values) + if not combined_hex.isdigit(): raise ValueError("Error: Invalid data in blocks 4 and 5.") + decimal_id = int(combined_hex) stripped_hex_id = format(decimal_id, 'X').upper() padded_hex_id = stripped_hex_id.zfill(10) return combined_hex, decimal_id, stripped_hex_id, padded_hex_id + def input_block_data(block_number): while True: block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip() @@ -75,6 +93,7 @@ blocks = [ block_4, block_5, ] + try: result_hex, result_decimal, stripped_hex_id, padded_hex_id = calculate_id(blocks) print('Calculations for block 4 and block 5:') diff --git a/client/pyscripts/Paxton_switch.py b/client/pyscripts/paxton_switch.py old mode 100644 new mode 100755 similarity index 97% rename from client/pyscripts/Paxton_switch.py rename to client/pyscripts/paxton_switch.py index b65b1d39b..639e40402 --- a/client/pyscripts/Paxton_switch.py +++ b/client/pyscripts/paxton_switch.py @@ -1,5 +1,7 @@ +#!/usr/bin/env python3 + # paxton_switch.py - Convert Paxton Switch2 to EM4102 -# Author jareckib +# Author jareckib # Based on Equipter's tutorial - Downgrade Paxton Net to EM410x # # This code is copyright (c) jareckib, 2025, All rights reserved. @@ -16,35 +18,48 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. import sys + def hex_to_bin(hex_string): return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string)) + def remove_last_two_bits(binary_str): return binary_str[:-2] + def split_into_5bit_chunks(binary_str): return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)] + def remove_parity_bit(chunks): return [chunk[1:] for chunk in chunks if len(chunk) == 5] + def convert_to_decimal(chunks): return [int(chunk, 2) for chunk in chunks] + def process_block(block): binary_str = hex_to_bin(block) binary_str = remove_last_two_bits(binary_str) chunks = split_into_5bit_chunks(binary_str) no_parity_chunks = remove_parity_bit(chunks) decimal_values = convert_to_decimal(no_parity_chunks) + return decimal_values + def calculate_id(blocks): + all_decimal_values = [] for block in blocks: decimal_values = process_block(block) all_decimal_values.extend(decimal_values) + if len(all_decimal_values) < 15: raise ValueError("Error: Not enough data after processing blocks 4, 5, 6, and 7.") + id_positions = [9, 11, 13, 15, 2, 4, 6, 8] id_numbers = [all_decimal_values[pos-1] for pos in id_positions] decimal_id = int(''.join(map(str, id_numbers))) padded_hex_id = format(decimal_id, 'X').upper().zfill(10) + return decimal_id, padded_hex_id + def input_block_data(block_number): while True: block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip() @@ -52,6 +67,8 @@ def input_block_data(block_number): print("Error: Data must be 4 bytes (8 characters) in hex. Try again.") else: return block_data + + block_4 = input_block_data(4) block_5 = input_block_data(5) block_6 = input_block_data(6) @@ -62,6 +79,7 @@ blocks = [ block_6, block_7, ] + try: decimal_id, padded_hex_id = calculate_id(blocks) print('Calculated data from blocks 4, 5, 6, 7:') diff --git a/client/pyscripts/pm3_resources.py b/client/pyscripts/pm3_resources.py new file mode 100644 index 000000000..c50ede801 --- /dev/null +++ b/client/pyscripts/pm3_resources.py @@ -0,0 +1,92 @@ +""" +Helper library to locate resources for pm3 scripts. + +This module provides functionality to locate tools and dictionaries required +for pm3 scripts. It determines the paths based on the directory structure +and whether the script is being run in a development setup or an installed setup. + +Functions: + find_tool(tool_name): + Finds the specified tool in the tools directory. + Args: + tool_name (str): The name of the tool to find. + Returns: + str: The full path to the tool if found, otherwise None. + + find_dict(dict_name): + Find the specified dictionary in the dicts directory. + Args: + dict_name (str): The name of the dict to find. + Returns: + str: The full path to the dict if found, otherwise None. +""" + +import os + +# Install script can hardcode paths in the following variables +TOOLS_PATH = None +DICTS_PATH = None + +if __name__ == "__main__": + print("This is a library, don't use it as a script") + exit() + +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) + +if TOOLS_PATH is None: + if os.path.basename(os.path.dirname(DIR_PATH)) == 'client': + # dev setup + DEV_TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "..", "tools", "mfc", "card_only")) + if os.path.isdir(DEV_TOOLS_PATH): + TOOLS_PATH = DEV_TOOLS_PATH + +if TOOLS_PATH is None: + # assuming installed without having defined TOOLS_PATH + TEST_TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "tools")) + if os.path.isdir(TEST_TOOLS_PATH): + TOOLS_PATH = TEST_TOOLS_PATH + + +if DICTS_PATH is None: + DEV_DICTS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "dictionaries")) + if os.path.isdir(DEV_DICTS_PATH): + DICTS_PATH = DEV_DICTS_PATH + + +def find_tool(tool_name): + """Find the specified tool in the tools directory. + + Args: + tool_name (str): The name of the tool to find. + Returns: + str: The full path to the tool if found, otherwise None. + """ + if TOOLS_PATH is not None: + tool = os.path.join(TOOLS_PATH, tool_name) + if os.path.isfile(tool): + return tool + elif os.path.isfile(tool + ".exe"): + return tool + ".exe" + # if not found, search in the user PATH + for path in os.environ["PATH"].split(os.pathsep): + env_tool = os.path.join(path, tool_name) + if os.path.isfile(env_tool): + return env_tool + elif os.path.isfile(env_tool + ".exe"): + return env_tool + ".exe" + raise FileNotFoundError(f"Cannot find {tool_name}, abort!") + + +def find_dict(dict_name): + """Find the specified dictionary in the dicts directory. + + Args: + dict_name (str): The name of the dict to find. + Returns: + str: The full path to the dict if found, otherwise None. + """ + if DICTS_PATH is not None: + dictionary = os.path.join(DICTS_PATH, dict_name) + if os.path.isfile(dictionary): + return dictionary + raise FileNotFoundError(f"Cannot find {dict_name}, abort!") diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 144a3e237..f66e3df4e 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -335,6 +335,14 @@ "Description": "Card Application Directory (CAD)", "Type": "pacs" }, + { + "AID": "F484D1", + "Vendor": "HID", + "Country": "US", + "Name": "Unknown DESFire EV1", + "Description": "Access Control", + "Type": "pacs" + }, { "AID": "F484E3", "Vendor": "HID", @@ -511,6 +519,46 @@ "Description": "Multiservice Student Card", "Type": "student" }, + { + "AID": "764970", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access VID App", + "Description": "Contains issuer-signed static credential information used for KDF & other authentication operations", + "Type": "pacs" + }, + { + "AID": "84D3FC", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access FCD App", + "Description": "Contains static credential information used for KDF & other authentication operations", + "Type": "pacs" + }, + { + "AID": "416343", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access ACC App", + "Description": "Application created after enrollment into the system, containins unique authentication info", + "Type": "pacs" + }, + { + "AID": "454955", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access Touch Pass Apple Wallet Express", + "Description": "AID value is 'UIE' (UniFi Express) reversed. This app is selectable with or without auth", + "Type": "pacs" + }, + { + "AID": "534955", + "Vendor": "Ubiquiti Inc", + "Country": "N/A", + "Name": "UniFi Access Touch Pass Apple Wallet Secure", + "Description": "AID value is 'UIS' (UniFi Secure) reversed. This app is selectable only after manual auth", + "Type": "pacs" + }, { "AID": "535501", "Vendor": "TU Delft", @@ -1034,7 +1082,7 @@ { "AID": "087522", "Vendor": "Umo Mobility via Cubic Transportation Systems", - "Country": "US", + "Country": "US / CA", "Name": "Umo Mobility Card", "Description": "Umo Mobility Card", "Type": "transport" @@ -1159,14 +1207,6 @@ "Description": "DEL Delhi Metro (App 5)", "Type": "transport" }, - { - "AID": "444D05", - "Vendor": "Delhi Metro Rail Corporation Limited", - "Country": "IN", - "Name": "Delhi Metro Travel Card (DEL)", - "Description": "DEL Delhi Metro (App 5)", - "Type": "transport" - }, { "AID": "444D06", "Vendor": "Delhi Metro Rail Corporation Limited", @@ -1219,7 +1259,7 @@ "AID": "578000", "Vendor": "Norwegian Public Roads Administration (NPRA)", "Country": "NO", - "Name": "NORTIC (Norway Public Transport Card)", + "Name": "NORTIC (Norway Public Transport Card) (Card Issuer App)", "Description": "Norwegian Ticketing Interoperable Concept // FID 0C: Card Issuer Header", "Type": "transport" }, @@ -1227,7 +1267,7 @@ "AID": "578001", "Vendor": "Norwegian Public Roads Administration (NPRA)", "Country": "NO", - "Name": "NORTIC (Norway Public Transport Card)", + "Name": "NORTIC (Norway Public Transport Card) (Transport App)", "Description": "FIDs 01: Product Retailer; 02: Service Provider; 03: Special Event; 04: Stored Value; 05: General Event Log; 06: SV Reload Log; 0A: Environment; 0C: Card Holder", "Type": "transport" }, @@ -1242,7 +1282,7 @@ { "AID": "677F8E", "Vendor": "Umo Mobility via Cubic Transportation Systems", - "Country": "US", + "Country": "US / CA", "Name": "Umo Mobility Card", "Description": "Umo Mobility Card", "Type": "transport" @@ -1258,7 +1298,7 @@ { "AID": "992CB5", "Vendor": "Umo Mobility via Cubic Transportation Systems", - "Country": "US", + "Country": "US / CA", "Name": "Umo Mobility Card", "Description": "Umo Mobility Card", "Type": "transport" @@ -1274,7 +1314,7 @@ { "AID": "A4237D", "Vendor": "Umo Mobility via Cubic Transportation Systems", - "Country": "US", + "Country": "US / CA", "Name": "Umo Mobility Card", "Description": "Umo Mobility Card", "Type": "transport" @@ -1282,7 +1322,7 @@ { "AID": "C65B80", "Vendor": "Umo Mobility via Cubic Transportation Systems", - "Country": "US", + "Country": "US / CA", "Name": "Umo Mobility Card", "Description": "Umo Mobility Card", "Type": "transport" @@ -1353,10 +1393,10 @@ }, { "AID": "F21050", - "Vendor": "Metro Christchurch via INIT", - "Country": "NZ", - "Name": "Metrocard (CHC)", - "Description": "FIDs: 00: Backup Data; 01/02: Trip History; 03: Card Balance", + "Vendor": "Metro Christchurch via INIT / Arc", + "Country": "NZ / CA", + "Name": "Metrocard (CHC) / Arc (YEG)", + "Description": "CHC FIDs: 00: Backup Data; 01/02: Trip History; 03: Card Balance", "Type": "transport" }, { @@ -1400,11 +1440,19 @@ "Type": "transport" }, { - "AID": "F21201", - "Vendor": "Green Bay Metro Transit via Genfare", + "AID": "F21191", + "Vendor": "Metropolitan Transportation Commission via Cubic", "Country": "US", - "Name": "Tap-N-Go Card (GRB)", - "Description": "GRB Tap-N-Go Card", + "Name": "Clipper Card (Mobile)", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21201", + "Vendor": "Green Bay Metro Transit via Genfare / Winnipeg Transit", + "Country": "US / CA", + "Name": "Tap-N-Go Card (GRB) / peggo card (YWG)", + "Description": "GRB Tap-N-Go Card / YWG peggo card", "Type": "transport" }, { diff --git a/client/src/cmdcrc.c b/client/src/cmdcrc.c index 7cbee8d2f..8d8969584 100644 --- a/client/src/cmdcrc.c +++ b/client/src/cmdcrc.c @@ -82,20 +82,25 @@ int GetModels(char *Models[], int *count, uint8_t *width) { SETBMP(); if (width[0] == 0) { //reveng -D + *count = mcount(); if (!*count) { PrintAndLogEx(WARNING, "no preset models available"); return 0; } + for (int mode = 0; mode < *count; ++mode) { + mbynum(&model, mode); mcanon(&model); size_t size = (model.name && *model.name) ? strlen(model.name) : 7; + char *tmp = calloc(size + 1, sizeof(char)); if (tmp == NULL) { PrintAndLogEx(WARNING, "out of memory?"); return 0; } + if (model.name != NULL) { memcpy(tmp, model.name, size); Models[mode] = tmp; @@ -113,18 +118,21 @@ int GetModels(char *Models[], int *count, uint8_t *width) { PrintAndLogEx(WARNING, "cannot search for non-Williams compliant models"); return 0; } + praloc(&model.spoly, (unsigned long)width[0]); praloc(&model.init, (unsigned long)width[0]); praloc(&model.xorout, (unsigned long)width[0]); - if (!plen(model.spoly)) + if (!plen(model.spoly)) { palloc(&model.spoly, (unsigned long)width[0]); - else + } else { width[0] = (uint8_t)plen(model.spoly); + } /* special case if qpoly is zero, search to end of range */ - if (!ptst(qpoly)) + if (!ptst(qpoly)) { rflags &= ~R_HAVEQ; + } int pass; @@ -135,31 +143,41 @@ int GetModels(char *Models[], int *count, uint8_t *width) { */ /* scan against preset models */ if (~uflags & C_NOPCK) { + pass = 0; int Cnt = 0; + do { int psets = mcount(); while (psets) { + mbynum(&pset, --psets); /* skip if different width, or refin or refout don't match */ - if (plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT)) + if (plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT)) { continue; + } + /* skip if the preset doesn't match specified parameters */ - if (rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) + if (rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) { continue; - if (rflags & R_HAVEI && psncmp(&model.init, &pset.init)) + } + + if (rflags & R_HAVEI && psncmp(&model.init, &pset.init)) { continue; - if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) + } + + if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) { continue; + } //for additional args (not used yet, maybe future?) apoly = pclone(pset.xorout); - if (pset.flags & P_REFOUT) + if (pset.flags & P_REFOUT) { prev(&apoly); - + } for (qptr = apolys; qptr < pptr; ++qptr) { crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0); @@ -183,6 +201,7 @@ int GetModels(char *Models[], int *count, uint8_t *width) { PrintAndLogEx(WARNING, "out of memory?"); return 0; } + width[Cnt] = width[0]; memcpy(tmp, pset.name, size); Models[Cnt++] = tmp; @@ -199,6 +218,7 @@ int GetModels(char *Models[], int *count, uint8_t *width) { prevch(qptr, ibperhx); } } + } while (~rflags & R_HAVERI && ++pass < 2); } //got everything now free the memory... @@ -208,6 +228,7 @@ int GetModels(char *Models[], int *count, uint8_t *width) { pfree(qptr); } } + if (uflags & C_NOBFS && ~rflags & R_HAVEP) { PrintAndLogEx(WARNING, "no models found"); return 0; @@ -217,24 +238,30 @@ int GetModels(char *Models[], int *count, uint8_t *width) { PrintAndLogEx(WARNING, "cannot search for crossed-endian models"); return 0; } + pass = 0; int args = 0; do { + model_t *candmods = reveng(&model, qpoly, rflags, args, apolys); model_t *mptr = candmods; if (mptr && plen(mptr->spoly)) { uflags |= C_RESULT; } + while (mptr && plen(mptr->spoly)) { mfree(mptr++); } + free(candmods); + if (~rflags & R_HAVERI) { model.flags ^= P_REFIN | P_REFOUT; for (qptr = apolys; qptr < pptr; ++qptr) { prevch(qptr, ibperhx); } } + } while (~rflags & R_HAVERI && ++pass < 2); for (qptr = apolys; qptr < pptr; ++qptr) { diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index c627f4504..7bd292489 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1207,76 +1207,79 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen } uint16_t cmdc = 0; - if (chainingin) + if (chainingin) { cmdc = ISO14A_SEND_CHAINING; + } // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length PM3_CMD_DATA_SIZE=512 // timeout must be authomatically set by "get ATS" - if (datain) + if (datain) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0x1FF), 0, datain, datainlen & 0x1FF); - else + } else { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0); + } PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, timeout)) { - const uint8_t *recv = resp.data.asBytes; - int iLen = resp.oldarg[0]; - uint8_t res = resp.oldarg[1]; - - int dlen = iLen - 2; - if (dlen < 0) - dlen = 0; - *dataoutlen += dlen; - - if (maxdataoutlen && *dataoutlen > maxdataoutlen) { - PrintAndLogEx(DEBUG, "ERR: APDU: Buffer too small(%d), needs %d bytes", *dataoutlen, maxdataoutlen); - return PM3_EAPDU_FAIL; - } - - // I-block ACK - if ((res & 0xF2) == 0xA2) { - *dataoutlen = 0; - *chainingout = true; - return PM3_SUCCESS; - } - - if (!iLen) { - PrintAndLogEx(DEBUG, "ERR: APDU: No APDU response"); - return PM3_EAPDU_FAIL; - } - - // check apdu length - if (iLen < 2 && iLen >= 0) { - PrintAndLogEx(DEBUG, "ERR: APDU: Small APDU response, len %d", iLen); - return PM3_EAPDU_FAIL; - } - - // check block TODO - if (iLen == -2) { - PrintAndLogEx(DEBUG, "ERR: APDU: Block type mismatch"); - return PM3_EAPDU_FAIL; - } - - memcpy(dataout, recv, dlen); - - // chaining - if ((res & 0x10) != 0) { - *chainingout = true; - } - - // CRC Check - if (iLen == -1) { - PrintAndLogEx(DEBUG, "ERR: APDU: ISO 14443A CRC error"); - return PM3_EAPDU_FAIL; - } - } else { + if (WaitForResponseTimeout(CMD_ACK, &resp, timeout) == false) { PrintAndLogEx(DEBUG, "ERR: APDU: Reply timeout"); return PM3_EAPDU_FAIL; } + const uint8_t *recv = resp.data.asBytes; + int iLen = resp.oldarg[0]; + uint8_t res = resp.oldarg[1]; + + int dlen = iLen - 2; + if (dlen < 0) { + dlen = 0; + } + *dataoutlen += dlen; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLogEx(DEBUG, "ERR: APDU: Buffer too small(%d), needs %d bytes", *dataoutlen, maxdataoutlen); + return PM3_EAPDU_FAIL; + } + + // I-block ACK + if ((res & 0xF2) == 0xA2) { + *dataoutlen = 0; + *chainingout = true; + return PM3_SUCCESS; + } + + if (iLen == 0) { + PrintAndLogEx(DEBUG, "ERR: APDU: No APDU response"); + return PM3_EAPDU_FAIL; + } + + // check apdu length + if (iLen < 2 && iLen >= 0) { + PrintAndLogEx(DEBUG, "ERR: APDU: Small APDU response, len %d", iLen); + return PM3_EAPDU_FAIL; + } + + // check block TODO + if (iLen == -2) { + PrintAndLogEx(DEBUG, "ERR: APDU: Block type mismatch"); + return PM3_EAPDU_FAIL; + } + + memcpy(dataout, recv, dlen); + + // chaining + if ((res & 0x10) != 0) { + *chainingout = true; + } + + // CRC Check + if (iLen == -1) { + PrintAndLogEx(DEBUG, "ERR: APDU: ISO 14443A CRC error"); + return PM3_EAPDU_FAIL; + } + return PM3_SUCCESS; } @@ -1300,18 +1303,18 @@ int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bo *dataoutlen = 0; res = CmdExchangeAPDU(chainBlockNotLast, &datain[clen], vlen, vActivateField, dataout, maxdataoutlen, dataoutlen, &chaining); if (res != PM3_SUCCESS) { - if (leaveSignalON == false) + if (leaveSignalON == false) { DropField(); - + } return 200; } // check R-block ACK // TODO check this one... if ((*dataoutlen == 0) && (chaining != chainBlockNotLast)) { - if (leaveSignalON == false) + if (leaveSignalON == false) { DropField(); - + } return 201; } @@ -1323,6 +1326,7 @@ int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bo } break; } + } while (clen < datainlen); } else { @@ -2358,7 +2362,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { tc1 = (card.ats[1] & 0x40) == 0x40; int16_t fsci = card.ats[1] & 0x0f; - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ T0 TA1 is%s present, TB1 is%s present, " + PrintAndLogEx(INFO, " ..." _YELLOW_("%02X") "............ T0 TA1 is%s present, TB1 is%s present, " "TC1 is%s present, FSCI is %d (FSC = %d)", card.ats[1], (ta1 ? "" : _RED_(" NOT")), @@ -2380,7 +2384,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (card.ats[pos] & 0x04) strcat(dr, "8, "); if (strlen(ds) != 0) ds[strlen(ds) - 2] = '\0'; if (strlen(dr) != 0) dr[strlen(dr) - 2] = '\0'; - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... TA1 different divisors are%s supported, " + PrintAndLogEx(INFO, " ......" _YELLOW_("%02X") "......... TA1 different divisors are%s supported, " "DR: [%s], DS: [%s]", card.ats[pos], ((card.ats[pos] & 0x80) ? _RED_(" NOT") : ""), @@ -2395,7 +2399,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { uint32_t sfgi = card.ats[pos] & 0x0F; uint32_t fwi = card.ats[pos] >> 4; - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... TB1 SFGI = %d (SFGT = %s%d/fc), FWI = " _YELLOW_("%d") " (FWT = %d/fc)", + PrintAndLogEx(INFO, " ........." _YELLOW_("%02X") "...... TB1 SFGI = %d (SFGT = %s%d/fc), FWI = " _YELLOW_("%d") " (FWT = %d/fc)", card.ats[pos], (sfgi), sfgi ? "" : "(not needed) ", @@ -2407,7 +2411,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } if (tc1 && (card.ats_len > pos + 2)) { - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... TC1 NAD is%s supported, CID is%s supported", + PrintAndLogEx(INFO, " ............" _YELLOW_("%02X") "... TC1 NAD is%s supported, CID is%s supported", card.ats[pos], (card.ats[pos] & 0x01) ? "" : _RED_(" NOT"), (card.ats[pos] & 0x02) ? "" : _RED_(" NOT") @@ -2546,11 +2550,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { , sprint_ascii(card.ats + pos, calen) ); } - - PrintAndLogEx(NORMAL, ""); } } - } if (do_aid_search) { @@ -2781,7 +2782,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if ((isMagic & MAGIC_FLAG_GEN_2) == MAGIC_FLAG_GEN_2) { PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf") "` commands"); } else { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf`") " commands"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf info`")); } } @@ -3715,58 +3716,61 @@ int CmdHF14AAIDSim(const char *Cmd) { CLIParserInit(&ctx, "hf 14a simaid", "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID, and filter for AID Values\n" "These AID Values can be responded to and include extra APDU commands on GetData after response\n", - "hf 14a simaid -t 3 -> MIFARE Desfire\n" - "hf 14a simaid -t 4 -> ISO/IEC 14443-4\n" - "hf 14a simaid -t 11 -> Javacard (JCOP)\n" - "hf 14a simaid -t 3 --aid a000000000000000000000 --response 9000 --apdu 9000 -> AID, Response and APDU\n" - "hf 14a simaid -t 3 --rats 05788172220101 --response 01009000 --apdu 86009000 -> Custom RATS Added\n" - "hf 14a simaid -t 3 --rats 05788172220101 -x -> Enumerate AID Values\n" + "hf 14a simaid -t 3 -> MIFARE Desfire\n" + "hf 14a simaid -t 4 -> ISO/IEC 14443-4\n" + "hf 14a simaid -t 11 -> Javacard (JCOP)\n" + "hf 14a simaid -t 3 --aid a000000000000000000000 --selectaid_response 9000 --getdata_response 9000 -> Custom AID and responses\n" + "hf 14a simaid -t 3 --ats 0578817222 --selectaid_response 01009000 --getdata_response 86009000 -> Custom ATS and responses\n" + "hf 14a simaid -t 3 --ats 0578817222 -x -> Enumerate AID Values\n" ); void *argtable[] = { arg_param_begin, arg_int1("t", "type", "<1-12> ", "Simulation type to use"), arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), - arg_str0("r", "rats", "", "<0-20> hex bytes RATS"), - arg_str0("a", "aid", "", "<0-100> hex bytes for AID to respond to (Default: A000000000000000000000)"), - arg_str0("e", "response", "", "<0-100> hex bytes for APDU Response to AID Select (Default: 9000)"), - arg_str0("p", "apdu", "", "<0-100> hex bytes for APDU Response to Get Data request after AID (Default: 9000)"), + arg_str0("r", "ats", "", "<0-20> hex bytes ATS"), + arg_str0("a", "aid", "", "<0-30> hex bytes for AID to respond to (Default: A000000000000000000000)"), + arg_str0("e", "selectaid_response", "", "<0-100> hex bytes for APDU Response to AID Select (Default: 9000)"), + arg_str0("p", "getdata_response", "", "<0-100> hex bytes for APDU Response to Get Data request after AID (Default: 9000)"), arg_lit0("x", "enumerate", "Enumerate all AID values via returning Not Found and print them to console "), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int tagtype = arg_get_int_def(ctx, 1, 1); - - bool enumerate = arg_get_lit(ctx, 7); - int uid_len = 0; - int rats_len = 0; + int ats_len = 0; int aid_len = 0; - int respond_len = 0; - int apdu_len = 0; + int selectaid_response_len = 0; + int getdata_response_len = 0; uint8_t uid[10] = {0}; - uint8_t rats[20] = {0}; - uint8_t aid[30] = {0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t response[100] = {0x90, 0x00}; - uint8_t apdu[100] = {0x90, 0x00}; + uint8_t ats[20] = {0}; + uint8_t aid[30] = {0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t default_aid_len = 11; + uint8_t selectaid_response[100] = {0x90, 0x00}; + uint8_t default_selectaid_response_len = 2; + uint8_t getdata_response[100] = {0x90, 0x00}; + uint8_t default_getdata_response_len = 2; + int tagtype = arg_get_int_def(ctx, 1, 1); CLIGetHexWithReturn(ctx, 2, uid, &uid_len); - CLIGetHexWithReturn(ctx, 3, rats, &rats_len); + CLIGetHexWithReturn(ctx, 3, ats, &ats_len); CLIGetHexWithReturn(ctx, 4, aid, &aid_len); - CLIGetHexWithReturn(ctx, 5, response, &respond_len); - CLIGetHexWithReturn(ctx, 6, apdu, &apdu_len); + CLIGetHexWithReturn(ctx, 5, selectaid_response, &selectaid_response_len); + CLIGetHexWithReturn(ctx, 6, getdata_response, &getdata_response_len); + bool enumerate = arg_get_lit(ctx, 7); + CLIParserFree(ctx); - // default value fill for the AID, response, and apdu + // default value fill for the AID, selectaid_response, and getdata_response if (aid_len == 0) { - aid_len = 11; + aid_len = default_aid_len; } - if (respond_len == 0) { - respond_len = 2; + + if (selectaid_response_len == 0) { + selectaid_response_len = default_selectaid_response_len; } - if (apdu_len == 0) { - apdu_len = 2; + if (getdata_response_len == 0) { + getdata_response_len = default_getdata_response_len; } uint16_t flags = 0; @@ -3776,19 +3780,39 @@ int CmdHF14AAIDSim(const char *Cmd) { FLAG_SET_UID_IN_DATA(flags, uid_len); if (IS_FLAG_UID_IN_EMUL(flags)) { PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); - CLIParserFree(ctx); return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uid_len, sprint_hex(uid, uid_len)); useUIDfromEML = false; } - if (rats_len > 0) { - flags |= FLAG_RATS_IN_DATA; + if (ats_len > sizeof(ats)) { + PrintAndLogEx(ERR, "Provided ATS too long"); + return PM3_EINVARG; } + if (aid_len > sizeof(aid)) { + PrintAndLogEx(ERR, "Provided AID too long"); + return PM3_EINVARG; + } - CLIParserFree(ctx); + if (selectaid_response_len > sizeof(selectaid_response)) { + PrintAndLogEx(ERR, "Provided SelectAID response too long"); + return PM3_EINVARG; + } + + if (getdata_response_len > sizeof(getdata_response)) { + PrintAndLogEx(ERR, "Provided GetData response too long"); + return PM3_EINVARG; + } + + if (ats_len > 0) { + flags |= FLAG_ATS_IN_DATA; + } + + if (enumerate) { + flags |= FLAG_ENUMERATE_AID; + } if (tagtype > 12) { PrintAndLogEx(ERR, "Undefined tag %d", tagtype); @@ -3803,31 +3827,31 @@ int CmdHF14AAIDSim(const char *Cmd) { uint8_t tagtype; uint16_t flags; uint8_t uid[10]; - uint8_t rats[20]; + uint8_t ats[20]; uint8_t aid[30]; - uint8_t response[100]; - uint8_t apdu[100]; - int aid_len; - int respond_len; - int apdu_len; - bool enumerate; + uint8_t selectaid_response[100]; + uint8_t getdata_response[100]; + uint32_t ats_len; + uint32_t aid_len; + uint32_t selectaid_response_len; + uint32_t getdata_response_len; } PACKED payload; payload.tagtype = tagtype; payload.flags = flags; - payload.enumerate = enumerate; // Copy data to payload memcpy(payload.uid, uid, uid_len); - memcpy(payload.rats, rats, rats_len); + memcpy(payload.ats, ats, ats_len); memcpy(payload.aid, aid, aid_len); - memcpy(payload.response, response, respond_len); - memcpy(payload.apdu, apdu, apdu_len); + memcpy(payload.selectaid_response, selectaid_response, selectaid_response_len); + memcpy(payload.getdata_response, getdata_response, getdata_response_len); // copy the lengths data to the payload - memcpy(&payload.aid_len, &aid_len, sizeof(aid_len)); - memcpy(&payload.respond_len, &respond_len, sizeof(respond_len)); - memcpy(&payload.apdu_len, &apdu_len, sizeof(apdu_len)); + payload.ats_len = ats_len; + payload.aid_len = aid_len; + payload.selectaid_response_len = selectaid_response_len; + payload.getdata_response_len = getdata_response_len; clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443A_SIM_AID, (uint8_t *)&payload, sizeof(payload)); @@ -3837,14 +3861,17 @@ int CmdHF14AAIDSim(const char *Cmd) { bool keypress = kbd_enter_pressed(); while (keypress == false) { - if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) { continue; + } - if (resp.status != PM3_SUCCESS) + if (resp.status != PM3_SUCCESS) { break; + } - if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { break; + } keypress = kbd_enter_pressed(); } diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index e2295c697..fa5fb266a 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -282,9 +282,9 @@ static const productName_t uidmapping[] = { static int CmdHF15Help(const char *Cmd); static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { + int reason = 0; - int index = -1; - index = originality_check_verify(uid, 8, signature, 32, PK_MFC); + int index = originality_check_verify(uid, 8, signature, 32, PK_MFC); if (index >= 0) { reason = 1; } else { @@ -306,11 +306,12 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { } } } - PrintAndLogEx(NORMAL, ""); + int ret = originality_check_print(signature, 32, index); if (ret != PM3_SUCCESS) { return ret; } + switch (reason) { case 1: PrintAndLogEx(INFO, " Params used: UID and signature, plain"); @@ -825,6 +826,8 @@ static int NxpSysInfo(uint8_t *uid) { PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, _CYAN_(" Password protection configuration")); + PrintAndLogEx(INFO, " Page prot. ptr. " _YELLOW_("%d"), d[1]); + PrintAndLogEx(INFO, " Page L read.... %s" , (d[2] & 0x01) ? _RED_("password") : _GREEN_("no password") ); @@ -3202,6 +3205,101 @@ static int CmdHF15SlixWritePassword(const char *Cmd) { return resp.status; } +static int CmdHF15SlixProtectPage(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 slixprotectpage", + "Defines protection pointer address of user mem and access cond. for pages", + "hf 15 slixprotectpage -w deadbeef -p 3 -h 3"); + + void *argtable[] = { + arg_param_begin, + arg_str0("r", "readpw", "", "read password, 4 hex bytes"), + arg_str0("w", "writepw", "", "write password, 4 hex bytes"), + arg_int0("p", "ptr", "", "protection pointer page (0-78), if 0 entire user mem"), + arg_int1("l", "lo", "", "page protection flags of lo page (0-None, 1-ReadPR, 2-WritePR)"), + arg_int1("i", "hi", "", "page protection flags of hi page (0-None, 1-ReadPR, 2-WritePR)"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + + struct p { + uint8_t read_pwd[4]; + uint8_t write_pwd[4]; + uint8_t divide_ptr; + uint8_t prot_status; + } PACKED payload = {0}; + int pwdlen = 0; + + CLIGetHexWithReturn(ctx, 1, payload.read_pwd, &pwdlen); + + if (pwdlen > 0 && pwdlen != 4) { + PrintAndLogEx(WARNING, "read password must be 4 hex bytes if provided"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + CLIGetHexWithReturn(ctx, 2, payload.write_pwd, &pwdlen); + + if (pwdlen > 0 && pwdlen != 4) { + PrintAndLogEx(WARNING, "write password must be 4 hex bytes if provided"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + payload.divide_ptr = (uint8_t)arg_get_int_def(ctx, 3, 0); + if (payload.divide_ptr > 78) { + PrintAndLogEx(WARNING, "protection pointer page is invalid (is %d but should be <=78).", payload.divide_ptr); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + pwdlen = arg_get_int_def(ctx, 4, 0); + if (pwdlen > 3) { + PrintAndLogEx(WARNING, "page protection flags must be between 0 and 3"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + payload.prot_status = (uint8_t)pwdlen; + + pwdlen = arg_get_int_def(ctx, 5, 0); + if (pwdlen > 3) { + PrintAndLogEx(WARNING, "page protection flags must be between 0 and 3"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + payload.prot_status |= (uint8_t)pwdlen << 4; + + PrintAndLogEx(INFO, "Trying to set page protection pointer to " _YELLOW_("%d"), payload.divide_ptr); + PrintAndLogEx(INFO, _YELLOW_("LO") " page access %s%s", (payload.prot_status & 0x01) ? _RED_("R") : _GREEN_("r"), (payload.prot_status & 0x02) ? _RED_("W") : _GREEN_("w")); + PrintAndLogEx(INFO, _YELLOW_("HI") " page access %s%s", (payload.prot_status & 0x10) ? _RED_("R") : _GREEN_("r"), (payload.prot_status & 0x20) ? _RED_("W") : _GREEN_("w")); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ESOFT; + } + + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + PrintAndLogEx(WARNING, "Protection flags were not accepted, locked? ( " _RED_("fail") " )"); + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "Page protection written ( " _GREEN_("ok") " ) "); + break; + } + } + return resp.status; +} + static int CmdHF15AFIPassProtect(const char *Cmd) { CLIParserContext *ctx; @@ -3512,6 +3610,7 @@ static command_t CommandTable[] = { {"slixeasenable", CmdHF15SlixEASEnable, IfPm3Iso15693, "Enable EAS mode on SLIX ISO-15693 tag"}, {"slixprivacydisable", CmdHF15SlixDisable, IfPm3Iso15693, "Disable privacy mode on SLIX ISO-15693 tag"}, {"slixprivacyenable", CmdHF15SlixEnable, IfPm3Iso15693, "Enable privacy mode on SLIX ISO-15693 tag"}, + {"slixprotectpage", CmdHF15SlixProtectPage, IfPm3Iso15693, "Protect pages on SLIX ISO-15693 tag"}, {"passprotectafi", CmdHF15AFIPassProtect, IfPm3Iso15693, "Password protect AFI - Cannot be undone"}, {"passprotecteas", CmdHF15EASPassProtect, IfPm3Iso15693, "Password protect EAS - Cannot be undone"}, {"-----------", CmdHF15Help, IfPm3Iso15693, "-------------------------- " _CYAN_("afi") " ------------------------"}, diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index af480c5f0..2160c9b06 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -767,23 +767,35 @@ static bool emrtd_select_and_read(uint8_t *dataout, size_t *dataoutlen, uint16_t static const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 }; static const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 }; +static const uint8_t jpeg2k_cs_header[4] = { 0xFF, 0x4F, 0xFF, 0x51 }; static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path) { size_t offset; int datalen = 0; + char suffix[5] = { '\0' }; // This is a hacky impl that just looks for the image header. I'll improve it eventually. // based on mrpkey.py // Note: Doing file_length - 6 to account for the longest data we're checking. // Checks first byte before the rest to reduce overhead for (offset = 0; offset < file_length - 6; offset++) { - if ((file_contents[offset] == 0xFF && memcmp(jpeg_header, file_contents + offset, 4) == 0) || - (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0)) { + if (file_contents[offset] == 0xFF) { + if (memcmp(jpeg_header, file_contents + offset, 4) == 0) { + datalen = file_length - offset; + strcpy(suffix, ".jpg"); + break; + } else if (memcmp(jpeg2k_cs_header, file_contents + offset, 4) == 0) { + datalen = file_length - offset; + // no standardized extension for codestream data, using .jpc + strcpy(suffix, ".jpc"); + break; + } + } else if (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0) { + strcpy(suffix, ".jp2"); datalen = file_length - offset; break; } } - // If we didn't get any data, return false. if (datalen == 0) { return PM3_ESOFT; @@ -797,7 +809,7 @@ static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const c strncat(filepath, PATHSEP, 2); strcat(filepath, dg_table[EF_DG2].filename); - saveFile(filepath, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen); + saveFile(filepath, suffix, file_contents + offset, datalen); free(filepath); return PM3_SUCCESS; @@ -1865,14 +1877,13 @@ static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_s PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_SOD") " ------------------------"); PrintAndLogEx(INFO, "Document Security Object"); PrintAndLogEx(INFO, "contains the digital signatures of the passport data"); + PrintAndLogEx(INFO, ""); if (hash_algo == -1) { PrintAndLogEx(SUCCESS, "Hash algorithm... " _YELLOW_("Unknown")); - PrintAndLogEx(INFO, ""); } else { PrintAndLogEx(SUCCESS, "Hash algorithm... " _YELLOW_("%s"), hashalg_table[hash_algo].name); - PrintAndLogEx(INFO, ""); uint8_t all_zeroes[64] = { 0x00 }; diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 22b90834e..aa73c7e1c 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -66,7 +66,6 @@ static uint8_t empty[PICOPASS_BLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, static uint8_t zeros[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static int CmdHelp(const char *Cmd); -static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len); static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][PICOPASS_BLOCK_SIZE] = { { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }, @@ -1346,10 +1345,7 @@ static int CmdHFiClassEView(const char *Cmd) { PrintAndLogEx(NORMAL, ""); printIclassDumpContents(dump, 1, blocks, bytes, dense_output); - - if (verbose) { - print_iclass_sio(dump, bytes); - } + print_iclass_sio(dump, bytes, verbose); free(dump); return PM3_SUCCESS; @@ -1400,7 +1396,7 @@ static int CmdHFiClassESetBlk(const char *Cmd) { static bool iclass_detect_new_pacs(uint8_t *d) { uint8_t n = 0; - while (n++ < (PICOPASS_BLOCK_SIZE / 2)) { + while (n++ < (PICOPASS_BLOCK_SIZE >> 1)) { if (d[n] && d[n + 1] == 0xA6) { return true; } @@ -1418,7 +1414,7 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) { uint8_t pad = d[offset]; - PrintAndLogEx(INFO, "%u , %u", offset, pad); + PrintAndLogEx(DEBUG, "%u , %u", offset, pad); char *binstr = (char *)calloc((PICOPASS_BLOCK_SIZE * 8) + 1, sizeof(uint8_t)); if (binstr == NULL) { @@ -1428,17 +1424,16 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) { uint8_t n = PICOPASS_BLOCK_SIZE - offset - 2; bytes_2_binstr(binstr, d + offset + 2, n); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n)); - PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + PrintAndLogEx(DEBUG, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n)); + PrintAndLogEx(DEBUG, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); binstr[strlen(binstr) - pad] = '\0'; - PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + PrintAndLogEx(DEBUG, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); size_t hexlen = 0; uint8_t hex[16] = {0}; binstr_2_bytes(hex, &hexlen, binstr); - PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(DEBUG, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); uint32_t top = 0, mid = 0, bot = 0; if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { @@ -1450,9 +1445,8 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) { free(binstr); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - Wiegand") " ----------------------------"); + decode_wiegand(top, mid, bot, 0); return PM3_SUCCESS; } @@ -1473,8 +1467,7 @@ static void iclass_decode_credentials(uint8_t *data) { if (has_values && encryption == None) { // todo: remove preamble/sentinel - PrintAndLogEx(INFO, "Block 7 decoder"); - + PrintAndLogEx(INFO, "------------------------ " _CYAN_("Block 7 decoder") " --------------------------"); if (has_new_pacs) { iclass_decode_credentials_new_pacs(b7); } else { @@ -1489,11 +1482,8 @@ static void iclass_decode_credentials(uint8_t *data) { char *pbin = binstr; while (strlen(pbin) && *(++pbin) == '0'); - PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), pbin); - - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + PrintAndLogEx(SUCCESS, "Binary... %zu - " _GREEN_("%s"), strlen(pbin), pbin); + decode_wiegand(top, mid, bot, 0); } } else { @@ -1712,11 +1702,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } printIclassDumpContents(decrypted, 1, (decryptedlen / 8), decryptedlen, dense_output); - - if (verbose) { - print_iclass_sio(decrypted, decryptedlen); - } - + print_iclass_sio(decrypted, decryptedlen, verbose); PrintAndLogEx(NORMAL, ""); // decode block 6 @@ -1747,7 +1733,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } } - PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + PrintAndLogEx(INFO, "-------------------------------------------------------------------"); free(decrypted); } @@ -2865,8 +2851,9 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { return PM3_SUCCESS; bool use_sc = IsCardHelperPresent(verbose); - if (use_sc == false) + if (use_sc == false) { return PM3_SUCCESS; + } // crypto helper available. PrintAndLogEx(INFO, "----------------------------- " _CYAN_("Cardhelper") " -----------------------------"); @@ -2916,8 +2903,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { PrintAndLogEx(SUCCESS, " bin : %s", pbin); PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, "------------------------------ " _CYAN_("Wiegand") " -------------------------------"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + decode_wiegand(top, mid, bot, 0); } } else { PrintAndLogEx(INFO, "no credential found"); @@ -3187,7 +3173,7 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le picopass_hdr_t *hdr = (picopass_hdr_t *)iclass_dump; - if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + if (memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE) == 0) { // Legacy AIA *is_legacy = true; @@ -3209,7 +3195,7 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le } } } - } else if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + } else if (memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE) == 0) { // SE AIA *is_se = true; @@ -3241,28 +3227,32 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le } // print ASN1 decoded array in TLV view -static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len) { +void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose) { + bool is_legacy, is_se, is_sr; uint8_t *sio_start; size_t sio_length; detect_credential(iclass_dump, dump_len, &is_legacy, &is_se, &is_sr, &sio_start, &sio_length); + // sanity checks if (sio_start == NULL) { return; } if (dump_len < sio_length + (sio_start - iclass_dump)) { - // SIO length exceeds the size of the dump we have, bail + // SIO length exceeds the size of the dump return; } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "---------------------------- " _CYAN_("SIO - RAW") " ----------------------------"); + PrintAndLogEx(INFO, "--------------------------- " _CYAN_("SIO - RAW") " -----------------------------"); print_hex_noascii_break(sio_start, sio_length, 32); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - ASN1 TLV") " --------------------------"); - asn1_print(sio_start, sio_length, " "); - PrintAndLogEx(NORMAL, ""); + if (verbose) { + PrintAndLogEx(INFO, "----------------------- " _CYAN_("SIO - ASN1 TLV") " ---------------------------"); + asn1_print(sio_start, sio_length, " "); + PrintAndLogEx(NORMAL, ""); + } } void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize, bool dense_output) { @@ -3460,8 +3450,9 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e if (is_legacy) PrintAndLogEx(HINT, _YELLOW_("yellow") " = legacy credential"); - if (is_se) + if (is_se) { PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SE credential"); + } if (is_sr) PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SR credential"); @@ -3519,10 +3510,7 @@ static int CmdHFiClassView(const char *Cmd) { print_picopass_info((picopass_hdr_t *) dump); printIclassDumpContents(dump, startblock, endblock, bytes_read, dense_output); iclass_decode_credentials(dump); - - if (verbose) { - print_iclass_sio(dump, bytes_read); - } + print_iclass_sio(dump, bytes_read, verbose); free(dump); return PM3_SUCCESS; @@ -3880,7 +3868,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { arg_lit0(NULL, "vb6kdf", "use the VB6 elite KDF instead of a file"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIExecWithReturn(ctx, Cmd, argtable, false); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; @@ -4661,11 +4649,11 @@ static int CmdHFiClassLookUp(const char *Cmd) { memcpy(CCNR + 8, macs, 4); memcpy(MAC_TAG, macs + 4, 4); - PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, sizeof(csn))); - PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, sizeof(epurse))); - PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(macs, sizeof(macs))); - PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); - PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); + PrintAndLogEx(SUCCESS, "CSN....... " _GREEN_("%s"), sprint_hex(csn, sizeof(csn))); + PrintAndLogEx(SUCCESS, "Epurse.... %s", sprint_hex(epurse, sizeof(epurse))); + PrintAndLogEx(SUCCESS, "MACS...... %s", sprint_hex(macs, sizeof(macs))); + PrintAndLogEx(SUCCESS, "CCNR...... " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); + PrintAndLogEx(SUCCESS, "TAG MAC... %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); // Run time uint64_t t1 = msclock(); @@ -4744,7 +4732,7 @@ typedef struct { uint8_t use_raw; uint8_t use_elite; uint32_t keycnt; - uint8_t csn[8]; + uint8_t csn[PICOPASS_BLOCK_SIZE]; uint8_t cc_nr[12]; uint8_t *keys; union { @@ -4821,8 +4809,9 @@ void GenerateMacFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elite, } } - for (int i = 0; i < iclass_tc; i++) + for (int i = 0; i < iclass_tc; i++) { pthread_join(threads[i], NULL); + } } static void *bf_generate_mackey(void *thread_arg) { @@ -5436,7 +5425,7 @@ static int CmdHFiClassSAM(const char *Cmd) { data[0] = flags; int cmdlen = 0; - if (CLIParamHexToBuf(arg_get_str(ctx, 8), data+1, PM3_CMD_DATA_SIZE-1, &cmdlen) != PM3_SUCCESS){ + if (CLIParamHexToBuf(arg_get_str(ctx, 8), data + 1, PM3_CMD_DATA_SIZE - 1, &cmdlen) != PM3_SUCCESS) { CLIParserFree(ctx); return PM3_ESOFT; } @@ -5448,7 +5437,7 @@ static int CmdHFiClassSAM(const char *Cmd) { } clearCommandBuffer(); - SendCommandNG(CMD_HF_SAM_PICOPASS, data, cmdlen+1); + SendCommandNG(CMD_HF_SAM_PICOPASS, data, cmdlen + 1); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_SAM_PICOPASS, &resp, 4000) == false) { PrintAndLogEx(WARNING, "SAM timeout"); @@ -5506,11 +5495,11 @@ static int CmdHFiClassSAM(const char *Cmd) { const uint8_t *mediaType = oid + 2 + oid_length; const uint8_t mediaType_data = mediaType[2]; PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data)); - } else if(breakOnNrMac && d[0] == 0x05) { - PrintAndLogEx(SUCCESS, "Nr-MAC: " _GREEN_("%s"), sprint_hex_inrow(d+1, 8)); - if(verbose){ + } else if (breakOnNrMac && d[0] == 0x05) { + PrintAndLogEx(SUCCESS, "Nr-MAC: " _GREEN_("%s"), sprint_hex_inrow(d + 1, 8)); + if (verbose) { PrintAndLogEx(INFO, "Replay Nr-MAC to dump SIO:"); - PrintAndLogEx(SUCCESS, " hf iclass dump -k \"%s\" --nr", sprint_hex_inrow(d+1, 8)); + PrintAndLogEx(SUCCESS, " hf iclass dump -k \"%s\" --nr", sprint_hex_inrow(d + 1, 8)); } } else { print_hex(d, resp.length); diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index 6fac5cee5..b4fcd0524 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -43,4 +43,5 @@ uint32_t picopass_elite_rng(void); uint32_t picopass_elite_lcg(void); uint8_t picopass_elite_nextByte(void); void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uint8_t *keyBlock); +void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose); #endif diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index 911b12b8d..d23a02507 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -31,6 +31,7 @@ #include "protocols.h" #include "cmdhficlass.h" #include "mifare/mifaredefault.h" // mifare consts +#include "cmdhfseos.h" enum MifareAuthSeq { masNone, @@ -70,8 +71,14 @@ static uint8_t *gs_mfuc_key = NULL; */ uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { - if (n < 3) return 2; - if (isResponse && (n == 5)) return 2; + if (n < 3) { + return 2; + } + + if (isResponse && (n == 5)) { + return 2; + } + if (d[1] == 0x50 && d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { @@ -80,6 +87,25 @@ uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { return check_crc(CRC_14443_A, d, n); } +uint8_t seos_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { + if (n < 3) { + return 2; + } + + // 5 bytes response Card busy 0xFA have crc, the rest is most likely 14a anticollision + if ((n == 5) && (d[0] != 0xFA)) { + return 2; + } + + if (d[1] == 0x50 && + d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && + d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { + return 2; + } + return check_crc(CRC_14443_A, d, n); +} + + uint8_t mifare_CRC_check(bool isResponse, uint8_t *data, uint8_t len) { switch (MifareAuthState) { case masNone: @@ -273,9 +299,11 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "HALT"); MifareAuthState = masNone; break; - case ISO14443A_CMD_RATS: - snprintf(exp, size, "RATS - FSDI=%x, CID=%x", (cmd[1] & 0xF0) >> 4, (cmd[1] & 0x0F)); + case ISO14443A_CMD_RATS: { + uint16_t fsdi2fsd[] = {16, 24, 32, 40, 48, 64, 96, 128, 256, 512, 1024, 2048, 4096, 4096, 4096, 4096}; + snprintf(exp, size, "RATS - FSDI=%x (FSD=%u), CID=%x", (cmd[1] & 0xF0) >> 4, fsdi2fsd[(cmd[1] & 0xF0) >> 4], (cmd[1] & 0x0F)); break; + } /* Actually, PPSS is Dx case ISO14443A_CMD_PPS: snprintf(exp, size, "PPS"); @@ -421,14 +449,14 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "?"); break; } - case NTAG_I2C_FASTWRITE: + case NTAG_I2C_FASTWRITE: { if (cmdsize == 69) snprintf(exp, size, "FAST WRITE (" _MAGENTA_("%d-%d") ")", cmd[1], cmd[2]); else snprintf(exp, size, "?"); break; - - default: + } + default: { if ((cmd[0] & 0xF0) == 0xD0 && (cmdsize == 4 || cmdsize == 5)) { snprintf(exp, size, "PPS - CID=%x", cmd[0] & 0x0F) ; } else if ((cmd[0] & 0xF0) == 0x60 && (cmdsize == 4)) { @@ -437,8 +465,11 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i } else { return PM3_ESOFT; } + } } + } else { + if (gs_mfuc_state == 1) { if ((cmd[0] == 0xAF) && (cmdsize == 11)) { // register RndB @@ -448,6 +479,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i gs_mfuc_state = 0; } } + if (gs_mfuc_state == 3) { if ((cmd[0] == 0x00) && (cmdsize == 11)) { // register RndA' @@ -1746,49 +1778,115 @@ void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { } } -void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { +void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool isResponse) { + + if (cmd[0] == 0xFA && cmdsize == 5) { + snprintf(exp, size, (isResponse) ? "BUSY" : "DONE?"); + return; + } // it's basically a ISO14443a tag, so try annotation from there if (applyIso14443a(exp, size, cmd, cmdsize, false) != PM3_SUCCESS) { int pos = 0; switch (cmd[0]) { - case 2: - case 3: - pos = 2; - break; case 0: + case 2: + case 3: { pos = 1; break; - default: + } + default: { pos = 2; break; + } } - if (memcmp(cmd + pos, "\x00\xa4\x04\x00\x0a", 5) == 0) { - snprintf(exp, size, "SELECT AID"); + if (memcmp(cmd + pos, "\x00\xA4\x04\x00", 4) == 0) { + uint8_t n = cmd[pos + 4]; + snprintf(exp, size, "SELECT AID " _WHITE_("%s"), sprint_hex_inrow(cmd + pos + 4 + 1, n)); + return; + } + + if (memcmp(cmd + pos, "\x80\xA5\x00\x00", 4) == 0) { + snprintf(exp, size, "SELECT GDF"); + return; } if (memcmp(cmd + pos, "\x80\xA5\x04\x00", 4) == 0) { - snprintf(exp, size, "SELECT ADF / OID"); + uint8_t n = cmd[pos + 4 + 2]; + snprintf(exp, size, "SELECT OID " _WHITE_("%s"), sprint_hex_inrow(cmd + pos + 4 + 2 + 1, n)); + return; } - if (memcmp(cmd + pos, "\x00\x87\x00\x01\x04\x7c\x02\x81\x00", 9) == 0) { - snprintf(exp, size, "GET CHALLENGE"); + if (memcmp(cmd + pos, "\x80\xA5\x07", 3) == 0) { + uint8_t ks = cmd[pos + 3]; + snprintf(exp, size, "SELECT GDF " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + return; } - if (memcmp(cmd + pos, "\x00\x87\x00\x01\x2c", 5) == 0) { - snprintf(exp, size, "MUTUAL AUTHENTICATION"); + if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) { + uint8_t ks = cmd[pos + 3]; + if (memcmp(cmd + pos + 3 + 1, "\x04\x7c\x02\x81\x00", 5) == 0) { + snprintf(exp, size, "GET CHALLENGE " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + } + return; } - if (memcmp(cmd + pos, "\x0c\xcb\x3f\xff", 4) == 0) { + if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) { + uint8_t ks = cmd[pos + 3]; + if (memcmp(cmd + pos + 3 + 1, "\x2C\x7C\x2A\x82\x28", 5) == 0) { + snprintf(exp, size, "MUTUAL AUTHENTICATION " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + } + return; + } + + if (memcmp(cmd + pos, "\x0C\xCB\x3F\xFF", 4) == 0) { snprintf(exp, size, "GET DATA"); + return; } - // apply ISO7816 annotations? -// if (annotateIso7816(exp, size, cmd, cmdsize) == 0) { -// } - // apply SEOS annotations? + if (memcmp(cmd + pos, "\x0C\xDB\x3F\xFF", 4) == 0) { + snprintf(exp, size, "UPDATE DATA"); + return; + } + + if (memcmp(cmd + pos, "\x0C\xED\x06\x00", 4) == 0) { + snprintf(exp, size, "DELETE DATA"); + return; + } + + if (memcmp(cmd + pos, "\x0C\x41\x0C\x03", 4) == 0) { + snprintf(exp, size, "CREATE ADF"); + return; + } + + if (isResponse) { + + if (memcmp(cmd + pos, "\xCD\x02", 2) == 0) { + + uint8_t ea = cmd[pos + 2]; + uint8_t ha = cmd[pos + 3]; + + char eas[10] = {0}; + if (ea == SEOS_ENCRYPTION_2K3DES) { + strcat(eas, "2K3DES"); + } else if (ea == SEOS_ENCRYPTION_3K3DES) { + strcat(eas, "3K3DES"); + } else if (ea == SEOS_ENCRYPTION_AES) { + strcat(eas, "AES"); + } + + char has[10] = {0}; + if (ha == SEOS_HASHING_SHA1) { + strcat(has, "SHA1"); + } else if (ha == SEOS_HASHING_SHA256) { + strcat(has, "SHA256"); + } + snprintf(exp, size, "%s / %s", eas, has); + return; + } + } } } @@ -1828,11 +1926,13 @@ void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { uint16_t address = (cmd[2] << 7) | cmd[1] >> 1; - if (cmdBit == LEGIC_READ) + if (cmdBit == LEGIC_READ) { snprintf(exp, size, "READ Byte(%d)", address); + } - if (cmdBit == LEGIC_WRITE) + if (cmdBit == LEGIC_WRITE) { snprintf(exp, size, "WRITE Byte(%d)", address); + } break; } case 21: { @@ -1852,8 +1952,9 @@ void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { break; } case 12: - default: + default: { break; + } } } diff --git a/client/src/cmdhflist.h b/client/src/cmdhflist.h index 92f54e4af..98bf27318 100644 --- a/client/src/cmdhflist.h +++ b/client/src/cmdhflist.h @@ -46,6 +46,7 @@ uint8_t felica_CRC_check(uint8_t *d, uint8_t n); uint8_t mifare_CRC_check(bool isResponse, uint8_t *data, uint8_t len); uint8_t iso15693_CRC_check(uint8_t *d, uint8_t n); uint8_t iclass_CRC_check(bool isResponse, uint8_t *d, uint8_t n); +uint8_t seos_CRC_check(bool isResponse, uint8_t *d, uint8_t n); int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response); @@ -68,7 +69,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); -void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); +void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool isResponse); bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount); bool NTParityChk(AuthData_t *ad, uint32_t ntx); diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index 6ffe115c0..7542114b1 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -682,7 +682,7 @@ static int CmdHfLTOWriteBlock(const char *Cmd) { int res = wrblLTO(blk, block_data, true); if (res == PM3_SUCCESS) - PrintAndLogEx(HINT, "Try use 'hf lto rdbl' for verification"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf lto rdbl") "` to verify"); return res; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 043ce9655..c2d20cb4c 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -70,7 +70,6 @@ static int usage_hf14_keybrute(void) { int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFC); - PrintAndLogEx(NORMAL, ""); return originality_check_print(signature, signature_len, index); } @@ -122,13 +121,6 @@ static char *GenerateFilename(const char *prefix, const char *suffix) { // Each entry also stores whether the key was "found", defaults to false (0) static int initSectorTable(sector_t **src, size_t items) { - - // typedef struct { - // uint64_t Key[2]; - // uint8_t foundKey[2]; - // } sector_t; - - // This allocates based on the size of a single item (*src) = calloc(items, sizeof(sector_t)); if (*src == NULL) { return PM3_EMALLOC; @@ -306,44 +298,44 @@ static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); if (blockno >= MIFARE_1K_MAXBLOCK) { - PrintAndLogEx(INFO, - _BACK_BLUE_("%s| %3d | " _YELLOW_("%s")) - _BACK_BLUE_(_MAGENTA_("%s")) - _BACK_BLUE_("%02X ") - _BACK_BLUE_(_YELLOW_("%s")) - _BACK_BLUE_("| " _YELLOW_("%s")) - , - secstr, - blockno, - keya, - acl, - d[9], - keyb, - ascii - ); + PrintAndLogEx(INFO, + _BACK_BLUE_("%s| %3d | " _YELLOW_("%s")) + _BACK_BLUE_(_MAGENTA_("%s")) + _BACK_BLUE_("%02X ") + _BACK_BLUE_(_YELLOW_("%s")) + _BACK_BLUE_("| " _YELLOW_("%s")) + , + secstr, + blockno, + keya, + acl, + d[9], + keyb, + ascii + ); } else { PrintAndLogEx(INFO, "%s| %3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"), - secstr, - blockno, - keya, - acl, - d[9], - keyb, - ascii - ); + secstr, + blockno, + keya, + acl, + d[9], + keyb, + ascii + ); } } else { - int32_t value = 0; - if (verbose && mfc_value(d, &value)) { - PrintAndLogEx(INFO, "%s| %3d | " _CYAN_("%s") " %"PRIi32, secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value); - } - if (blockno >= MIFARE_1K_MAXBLOCK) { // MFC Ev1 signature blocks. PrintAndLogEx(INFO, _BACK_BLUE_("%s| %3d | %s"), secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); } else { - PrintAndLogEx(INFO, "%s| %3d | %s", secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + int32_t value = 0; + if (verbose && mfc_value(d, &value)) { + PrintAndLogEx(INFO, "%s| %3d | " _CYAN_("%s") " %"PRIi32, secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value); + } else { + PrintAndLogEx(INFO, "%s| %3d | %s", secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + } } } } @@ -746,6 +738,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n free(fptr); free(keyA); free(keyB); + PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks"); return PM3_SUCCESS ; } @@ -2138,7 +2131,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); - // mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); + // mf_check_keys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; default : PrintAndLogEx(ERR, "unknown error.\n"); @@ -2542,8 +2535,8 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); int outfnlen = 0; - char outfilename[127] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)outfilename, 127, &outfnlen); + char outfilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)outfilename, FILE_PATH_SIZE, &outfnlen); bool slow = arg_get_lit(ctx, 7); @@ -2739,7 +2732,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // read uid to generate a filename for the key file - char suffix[FILE_PATH_SIZE]; + char suffix[FILE_PATH_SIZE + strlen(outfilename)]; if (outfnlen) { snprintf(suffix, sizeof(suffix) - strlen(outfilename), "-key-%s.bin", outfilename); } else { @@ -2955,7 +2948,7 @@ noValidKeyFound: // Try the found keys are reused if (bytes_to_num(tmp_key, MIFARE_KEY_SIZE) != 0) { - // The fast check --> mfCheckKeys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false, verbose); + // The fast check --> mf_check_keys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false, verbose); // Returns false keys, so we just stick to the slower mfchk. for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { @@ -4251,6 +4244,7 @@ static int CmdHF14AMfSim(const char *Cmd) { PrintAndLogEx(INFO, "Note: option -e implies -i"); flags |= FLAG_INTERACTIVE; } + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { PrintAndLogEx(WARNING, "Option -e requires -x or -y"); return PM3_EINVARG; @@ -4263,8 +4257,9 @@ static int CmdHF14AMfSim(const char *Cmd) { , (uidlen == 0) ? "n/a" : sprint_hex(uid, uidlen) ); - PrintAndLogEx(INFO, "Options [ numreads: %d, flags: 0x%04x ]" + PrintAndLogEx(INFO, "Options [ numreads: %d, flags: %d (0x%04x) ]" , exitAfterNReads + , flags , flags); struct { @@ -4277,46 +4272,56 @@ static int CmdHF14AMfSim(const char *Cmd) { payload.flags = flags; payload.exitAfter = exitAfterNReads; + memcpy(payload.uid, uid, uidlen); + payload.atqa = (atqa[1] << 8) | atqa[0]; payload.sak = sak[0]; clearCommandBuffer(); - if (flags & FLAG_INTERACTIVE) { + if ((flags & FLAG_INTERACTIVE) == FLAG_INTERACTIVE) { PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or a key to abort simulation"); } else { PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or send another cmd to abort simulation"); } + bool cont; do { + cont = false; SendCommandNG(CMD_HF_MIFARE_SIMULATE, (uint8_t *)&payload, sizeof(payload)); - if (flags & FLAG_INTERACTIVE) { + + if ((flags & FLAG_INTERACTIVE) == FLAG_INTERACTIVE) { PacketResponseNG resp; sector_t *k_sector = NULL; bool keypress = kbd_enter_pressed(); while (keypress == false) { - if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) { + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) { keypress = kbd_enter_pressed(); continue; } - if (resp.status != PM3_SUCCESS) + if (resp.status != PM3_SUCCESS) { break; + } - if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { break; + } const nonces_t *data = (nonces_t *)resp.data.asBytes; readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); + if (setEmulatorMem) { cont = true; } + break; } + if (keypress) { if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) { // inform device to break the sim loop since client has exited @@ -5485,7 +5490,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { return PM3_EFILE; } - PrintAndLogEx(INFO, "Copying to magic gen1a card"); + PrintAndLogEx(INFO, "Copying to magic gen1a MIFARE Classic " _GREEN_("%s"), s); PrintAndLogEx(INFO, "." NOLF); int blockno = 0; @@ -5533,7 +5538,11 @@ static int CmdHF14AMfCLoad(const char *Cmd) { return PM3_EFILE; } - PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from file", block_cnt); + PrintAndLogEx(SUCCESS, + "Card loaded " _YELLOW_("%d") " blocks from %s" + , block_cnt + , (fill_from_emulator ? "emulator memory" : "file") + ); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -6293,8 +6302,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin); PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + decode_wiegand(top, mid, bot, 0); } } @@ -7851,7 +7859,7 @@ static int CmdHF14AMfView(const char *Cmd) { if (bytes_read == MIFARE_MINI_MAX_BYTES) block_cnt = MIFARE_MINI_MAXBLOCK; else if (bytes_read == MIFARE_1K_EV1_MAX_BYTES) - block_cnt = MIFARE_1K_EV1_MAXBLOCK; + block_cnt = MIFARE_1K_EV1_MAXBLOCK; else if (bytes_read == MIFARE_2K_MAX_BYTES) block_cnt = MIFARE_2K_MAXBLOCK; else if (bytes_read == MIFARE_4K_MAX_BYTES) @@ -8252,13 +8260,14 @@ static int CmdHF14AGen4Load(const char *cmd) { arg_param_begin, arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "1k+", "MIFARE Classic Ev1 1k / S50"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_str0("p", "pwd", "", "password 4bytes"), arg_lit0("v", "verbose", "verbose output"), arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "from emulator memory"), - arg_int0(NULL, "start", "", "index of block to start writing (default 0)"), + arg_int0(NULL, "start", "", "index of block to start writing (def 0)"), arg_int0(NULL, "end", "", "index of block to end writing (default last block)"), arg_param_end }; @@ -8266,23 +8275,24 @@ static int CmdHF14AGen4Load(const char *cmd) { CLIExecWithReturn(ctx, cmd, argtable, false); bool m0 = arg_get_lit(ctx, 1); bool m1 = arg_get_lit(ctx, 2); - bool m2 = arg_get_lit(ctx, 3); - bool m4 = arg_get_lit(ctx, 4); + bool m1ev1 = arg_get_lit(ctx, 3); + bool m2 = arg_get_lit(ctx, 4); + bool m4 = arg_get_lit(ctx, 5); int pwd_len = 0; uint8_t pwd[4] = {0}; - CLIGetHexWithReturn(ctx, 5, pwd, &pwd_len); + CLIGetHexWithReturn(ctx, 6, pwd, &pwd_len); - bool verbose = arg_get_lit(ctx, 6); + bool verbose = arg_get_lit(ctx, 7); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 8), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - bool fill_from_emulator = arg_get_lit(ctx, 8); + bool fill_from_emulator = arg_get_lit(ctx, 9); - int start = arg_get_int_def(ctx, 9, 0); - int end = arg_get_int_def(ctx, 10, -1); + int start = arg_get_int_def(ctx, 10, 0); + int end = arg_get_int_def(ctx, 11, -1); CLIParserFree(ctx); @@ -8292,14 +8302,14 @@ static int CmdHF14AGen4Load(const char *cmd) { return PM3_EINVARG; } - if ((m0 + m1 + m2 + m4) > 1) { + if ((m0 + m1 + m2 + m4 + m1ev1) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; - } else if ((m0 + m1 + m2 + m4) == 0) { + } else if ((m0 + m1 + m2 + m4 + m1ev1) == 0) { m1 = true; } - char s[6]; + char s[8]; memset(s, 0, sizeof(s)); uint16_t block_cnt = MIFARE_1K_MAXBLOCK; if (m0) { @@ -8308,6 +8318,9 @@ static int CmdHF14AGen4Load(const char *cmd) { } else if (m1) { block_cnt = MIFARE_1K_MAXBLOCK; strncpy(s, "1K", 3); + } else if (m1ev1) { + block_cnt = MIFARE_1K_EV1_MAXBLOCK; + strncpy(s, "1K Ev1", 7); } else if (m2) { block_cnt = MIFARE_2K_MAXBLOCK; strncpy(s, "2K", 3); @@ -8386,16 +8399,13 @@ static int CmdHF14AGen4Load(const char *cmd) { } if (verbose) { - if (fnlen != 0) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); - PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); - } else { + if (fnlen == 0) { PrintAndLogEx(INFO, "Read %d blocks from emulator memory", block_cnt); } } PrintAndLogEx(INFO, "Copying to magic gen4 GTU MIFARE Classic " _GREEN_("%s"), s); - PrintAndLogEx(INFO, "Starting block: %d. Ending block: %d.", start, end); + PrintAndLogEx(INFO, "Block... %d - %d", start, end); // copy to card for (uint16_t blockno = start; blockno <= end; blockno++) { @@ -8423,10 +8433,15 @@ static int CmdHF14AGen4Load(const char *cmd) { } PrintAndLogEx(NORMAL, "\n"); - if (data != NULL) free(data); + if (data != NULL) { + free(data); + } - PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from %s", end - start + 1, - (fill_from_emulator ? "emulator memory" : "file")); + PrintAndLogEx(SUCCESS, + "Card loaded " _YELLOW_("%d") " blocks from %s" + , end - start + 1 + , (fill_from_emulator ? "emulator memory" : "file") + ); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -9623,7 +9638,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); - return 0; + return PM3_ETIMEOUT; } iso14a_card_select_t card; @@ -9721,9 +9736,9 @@ static int CmdHF14AMfInfo(const char *Cmd) { } } - uint8_t k08s[6] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; - uint8_t k08[6] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; - uint8_t k32[6] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; + uint8_t k08s[MIFARE_KEY_SIZE] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; + uint8_t k08[MIFARE_KEY_SIZE] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; + uint8_t k32[MIFARE_KEY_SIZE] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; if (mf_read_block(0, 4, k08s, blockdata) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k08s, sizeof(k08s))); fKeyType = MF_KEY_BD; @@ -9818,7 +9833,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { } if (e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) { - PrintAndLogEx(SUCCESS, "Dorma Kaba SAFLOK detected"); + PrintAndLogEx(SUCCESS, "dormakaba Saflok detected"); } } else { @@ -9841,7 +9856,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { res = detect_classic_static_nonce(); if (res == NONCE_STATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes")); } @@ -10219,7 +10234,6 @@ static command_t CommandTable[] = { {"gdmparsecfg", CmdHF14AGen4_GDM_ParseCfg, AlwaysAvailable, "Parse config block to card"}, {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, -// {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"}, {"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, {"ndefwrite", CmdHFMFNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"}, diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 6483cf9c9..123760fcd 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -175,14 +175,26 @@ typedef struct aidhdr { } PACKED aidhdr_t; typedef struct { + const uint32_t aidnum; const char *aid; const char *comment; } mfdesCommonAID_t; +/* +PACS application id(s) - HID Factory, CP1000 Standard, Mobile, Custom and Elite +We have HID Factory, Field Encoder == CP1000 (?) +No mobile, Custom or Elite +*/ + static const mfdesCommonAID_t commonAids[] = { - // AID, name/comment - { "\xf4\x81\x2f", "Gallagher card data application" }, - { "\xf4\x81\x20", "Gallagher card application directory" }, // Can be 0xF48120 - 0xF4812B, but I've only ever seen 0xF48120 + { 0x53494F, "\x53\x49\x4F", "SIO DESFire EV1 - HID Factory" }, + { 0xD3494F, "\xD3\x49\x4F", "SIO DESFire EV1 - Field Encoder" }, + { 0xD9494F, "\xD9\x49\x4F", "SIO DESFire EV1 - Field Encoder" }, + { 0xF484E3, "\xF4\x84\xE3", "SE Enhanced" }, + { 0xF484E4, "\xF4\x84\xE4", "SE Enhanced" }, + { 0xF4812F, "\xf4\x81\x2f", "Gallagher card data application" }, + { 0xF48120, "\xf4\x81\x20", "Gallagher card application directory" }, // Can be 0xF48120 - 0xF4812B, but I've only ever seen 0xF48120 + { 0xF47300, "\xf4\x73\x00", "Inner Range card application" }, }; static int CmdHelp(const char *Cmd); @@ -311,15 +323,13 @@ static char *getTypeStr(uint8_t type) { return buf; } - -static char noCommentStr[1] = { 0x00 }; -static const char *getAidCommentStr(uint8_t *aid) { +static const char *getAidCommentStr(uint32_t aid) { for (int i = 0; i < ARRAYLEN(commonAids); i++) { - if (memcmp(aid, commonAids[i].aid, 3) == 0) { + if (aid == commonAids[i].aidnum) { return commonAids[i].comment; } } - return noCommentStr; + return ""; } static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { @@ -355,6 +365,10 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { if (type == 0x81 && major == 0x42 && minor == 0x00) return DESFIRE_EV2; + // Apple Wallet DESFire Applet + if (type == 0x91 && major == 0x62 && minor == 0x01) + return DESFIRE_EV2; + // Plus EV1 if (type == 0x02 && major == 0x11 && minor == 0x00) return PLUS_EV1; @@ -377,7 +391,7 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { // ref: https://www.nxp.com/docs/en/application-note/AN12343.pdf p7 static nxp_producttype_t getProductType(const uint8_t *versionhw) { - uint8_t product = versionhw[2]; + uint8_t product = versionhw[1]; if (product == 0x01) return DESFIRE_PHYSICAL; @@ -394,7 +408,7 @@ static nxp_producttype_t getProductType(const uint8_t *versionhw) { static const char *getProductTypeStr(const uint8_t *versionhw) { - uint8_t product = versionhw[2]; + uint8_t product = versionhw[1]; if (product == 0x01) return "MIFARE DESFire native IC (physical card)"; @@ -454,7 +468,6 @@ int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, si } int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFDES); - PrintAndLogEx(NORMAL, ""); return originality_check_print(signature, signature_len, index); } @@ -752,7 +765,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { if (major == 2 && minor == 2) PrintAndLogEx(INFO, "\t2.2 - DESFire Ev2 XL, Originality check, proximity check, EAL5"); if (major == 3 && minor == 0) - PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6 ?"); + PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6"); if (major == 0xA0 && minor == 0) PrintAndLogEx(INFO, "\tx.x - DUOX, Originality check, proximity check, EAL6++"); @@ -801,12 +814,16 @@ static int CmdHF14ADesInfo(const char *Cmd) { } if (aidbuflen > 2) { + + uint8_t j = aidbuflen / 3; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list")); - PrintAndLogEx(SUCCESS, "AIDs: " NOLF); - for (int i = 0; i < aidbuflen; i += 3) - PrintAndLogEx(NORMAL, "%s %06x" NOLF, (i == 0) ? "" : ",", DesfireAIDByteToUint(&aidbuf[i])); - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list") " ( " _YELLOW_("%u") " found )", j); + + j = 0; + for (int i = 0; i < aidbuflen; i += 3, j++) { + uint32_t aid = DesfireAIDByteToUint(&aidbuf[i]); + PrintAndLogEx(SUCCESS, _YELLOW_("%06X") ", %s", aid, getAidCommentStr(aid)); + } } DesfireFillPICCInfo(&dctx, &PICCInfo, true); @@ -817,7 +834,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Free memory")); if (PICCInfo.freemem != 0xffffffff) { - PrintAndLogEx(SUCCESS, " Available free memory on card : " _GREEN_("%d bytes"), PICCInfo.freemem); + PrintAndLogEx(SUCCESS, " Available free memory on card... " _GREEN_("%d") " bytes", PICCInfo.freemem); } else { PrintAndLogEx(SUCCESS, " Card doesn't support 'free mem' cmd"); } @@ -1805,7 +1822,7 @@ static int CmdHF14aDesMAD(const char *Cmd) { AppListS AppList = {{0}}; DesfireFillAppList(&dctx, &PICCInfo, AppList, false, false, false); // no deep scan, no scan files - PrintAndLogEx(SUCCESS, "# Applications... " _GREEN_("%zu"), PICCInfo.appCount); + PrintAndLogEx(SUCCESS, "# Applications.... " _GREEN_("%zu"), PICCInfo.appCount); if (PICCInfo.freemem == 0xffffffff) { PrintAndLogEx(SUCCESS, "Free memory...... " _YELLOW_("n/a")); } else { @@ -3265,11 +3282,8 @@ static int CmdHF14ADesGetAIDs(const char *Cmd) { if (buflen >= 3) { PrintAndLogEx(INFO, "---- " _CYAN_("AID list") " ----"); for (int i = 0; i < buflen; i += 3) { - const char *commentStr = getAidCommentStr(&buf[i]); - if ((void *) commentStr == &noCommentStr) - PrintAndLogEx(INFO, "AID: %06x", DesfireAIDByteToUint(&buf[i])); - else - PrintAndLogEx(INFO, "AID: %06x (%s)", DesfireAIDByteToUint(&buf[i]), commentStr); + uint32_t aid = DesfireAIDByteToUint(&buf[i]); + PrintAndLogEx(SUCCESS, _YELLOW_("%06X") " %s", aid, getAidCommentStr(aid)); } } else { PrintAndLogEx(INFO, "There is no applications on the card"); @@ -5590,7 +5604,7 @@ static int CmdHF14ADesLsApp(const char *Cmd) { SetAPDULogging(APDULogging); CLIParserFree(ctx); - PrintAndLogEx(INPLACE, _YELLOW_("It may take up to 15 seconds. Processing....")); + PrintAndLogEx(INFO, "It may take up to " _YELLOW_("15") " seconds. Processing..."); res = DesfireSelectAndAuthenticateEx(&dctx, securechann, 0x000000, noauth, verbose); if (res != PM3_SUCCESS) { @@ -5602,7 +5616,6 @@ static int CmdHF14ADesLsApp(const char *Cmd) { AppListS AppList = {{0}}; DesfireFillAppList(&dctx, &PICCInfo, AppList, !nodeep, scanfiles, true); - printf("\33[2K\r"); // clear current line before printing PrintAndLogEx(NORMAL, ""); // print zone diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 218b87504..37e76a0f7 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -192,7 +192,6 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { // --- GET SIGNATURE static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFP); - PrintAndLogEx(NORMAL, ""); return originality_check_print(signature, signature_len, index); } @@ -1813,7 +1812,7 @@ static int CmdHFMFPMAD(const char *Cmd) { } uint8_t sector0[16 * 4] = {0}; - uint8_t sector10[16 * 4] = {0}; + uint8_t sector16[16 * 4] = {0}; if (mfpReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector0, verbose)) { PrintAndLogEx(NORMAL, ""); @@ -1833,19 +1832,19 @@ static int CmdHFMFPMAD(const char *Cmd) { MAD1DecodeAndPrint(sector0, swapmad, verbose, &haveMAD2); if (haveMAD2) { - if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector10, verbose)) { + if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector16, verbose)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector " _YELLOW_("0x10") ". Card doesn't have MAD or doesn't have MAD on default keys"); return PM3_ESOFT; } - MAD2DecodeAndPrint(sector10, swapmad, verbose); + MAD2DecodeAndPrint(sector16, swapmad, verbose); } if (aidlen == 2 || decodeholder) { uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; - if (MADDecode(sector0, sector10, mad, &madlen, swapmad)) { + if (MADDecode(sector0, sector16, mad, &madlen, swapmad)) { PrintAndLogEx(ERR, "can't decode MAD"); return PM3_EWRONGANSWER; } @@ -1990,9 +1989,9 @@ int CmdHFMFPNDEFRead(const char *Cmd) { memcpy(ndefkey, key, 16); } - uint8_t sector0[16 * 4] = {0}; - uint8_t sector10[16 * 4] = {0}; - uint8_t data[4096] = {0}; + uint8_t sector0[MIFARE_1K_MAXBLOCK] = {0}; + uint8_t sector16[MIFARE_1K_MAXBLOCK] = {0}; + uint8_t data[MIFARE_4K_MAX_BYTES] = {0}; int datalen = 0; if (verbose) @@ -2016,7 +2015,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) { if (verbose) PrintAndLogEx(INFO, "reading MAD v2 sector"); - if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector10, verbose)) { + if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector16, verbose)) { PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key"); return PM3_ESOFT; @@ -2025,7 +2024,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) { uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; - res = MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen, false); + res = MADDecode(sector0, (haveMAD2 ? sector16 : NULL), mad, &madlen, false); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "can't decode MAD"); return res; @@ -2034,7 +2033,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) { PrintAndLogEx(INFO, "reading data from tag"); for (int i = 0; i < madlen; i++) { if (ndefAID == mad[i]) { - uint8_t vsector[16 * 4] = {0}; + uint8_t vsector[MIFARE_1K_MAXBLOCK] = {0}; if (mfpReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector, false)) { PrintAndLogEx(ERR, "error, reading sector %d", i + 1); return PM3_ESOFT; diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 1fe6a5cd0..dc6a982b4 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -68,7 +68,7 @@ static uint8_t default_aes_keys[][16] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 { 0x47, 0x45, 0x4D, 0x58, 0x50, 0x52, 0x45, 0x53, 0x53, 0x4F, 0x53, 0x41, 0x4D, 0x50, 0x4C, 0x45 }, // gemalto - { 0x56, 0x4c, 0x67, 0x56, 0x99, 0x69, 0x64, 0x9f, 0x17, 0xC6, 0xC6, 0x16, 0x01, 0x10, 0x4D, 0xCA } // Virtual Dorma Kaba + { 0x56, 0x4c, 0x67, 0x56, 0x99, 0x69, 0x64, 0x9f, 0x17, 0xC6, 0xC6, 0x16, 0x01, 0x10, 0x4D, 0xCA } // Virtual dormakaba }; static uint8_t default_3des_keys[][16] = { @@ -669,6 +669,9 @@ static int try_default_aes_keys(bool override) { } static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool hasAuthKey, uint8_t *authkey, uint8_t *pack, uint8_t packSize) { + if (ul_select(card) == false) { + return PM3_ESOFT; + } if (hasAuthKey && (tagtype & MFU_TT_UL_C)) { //will select card automatically and close connection on error @@ -676,12 +679,7 @@ static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool has PrintAndLogEx(WARNING, "Authentication Failed UL-C"); return PM3_ESOFT; } - } else { - if (ul_select(card) == false) { - return PM3_ESOFT; - } - if (hasAuthKey) { if (ulev1_requestAuthentication(authkey, pack, packSize) == PM3_EWRONGANSWER) { DropField(); @@ -1419,7 +1417,6 @@ static int ulev1_print_signature(uint64_t tagtype, uint8_t *uid, uint8_t *signat } else if (signature_len == 48) { index = originality_check_verify(uid, 7, signature, signature_len, PK_MFULAES); } - PrintAndLogEx(NORMAL, ""); return originality_check_print(signature, signature_len, index); } @@ -2069,6 +2066,7 @@ uint64_t GetHF14AMfU_Type(void) { uint8_t version[10] = {0x00}; int len = ulev1_getVersion(version, sizeof(version)); DropField(); + switch (len) { case 0x0A: { /* @@ -2160,6 +2158,7 @@ uint64_t GetHF14AMfU_Type(void) { tagtype = MFU_TT_UNKNOWN; break; } + // This is a test from cards that doesn't answer to GET_VERSION command // UL vs UL-C vs NTAG203 vs FUDAN FM11NT021 (which is NTAG213 compatiable) if (tagtype & (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203)) { @@ -2451,7 +2450,6 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // check signature int index = originality_check_verify_ex(card.uid, 7, signature, sizeof(signature), PK_ST25TN, false, true); - PrintAndLogEx(NORMAL, ""); originality_check_print(signature, sizeof(signature), index); } @@ -4071,8 +4069,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { PacketResponseNG resp; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READBL, 2, 0, 0, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - PrintAndLogEx(WARNING, "command execution time out"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } @@ -4084,8 +4082,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { // block 1 write and block2 write hf14a_config config; SendCommandNG(CMD_HF_ISO14443A_GET_CONFIG, NULL, 0); - if (!WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000)) { - PrintAndLogEx(WARNING, "command execution time out"); + if (WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "command execute timeout"); return PM3_ETIMEOUT; } memcpy(&config, resp.data.asBytes, sizeof(hf14a_config)); @@ -4103,8 +4101,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { data[3] = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 0, 0, 0, data, sizeof(data)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - PrintAndLogEx(WARNING, "command execution time out"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } @@ -4115,8 +4113,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { data[3] = uid[6]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 1, 0, 0, data, sizeof(data)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - PrintAndLogEx(WARNING, "command execution time out"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } @@ -4127,8 +4125,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { data[3] = oldblock2[3]; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 2, 0, 0, data, sizeof(data)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - PrintAndLogEx(WARNING, "command execution time out"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 65a00000d..a416b24da 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -453,7 +453,7 @@ static int seos_challenge_get(uint8_t *RNDICC, uint8_t RNDICC_len, uint8_t keysl char getChallengePre[21]; strcpy(getChallengePre, "008700"); - + // const char keyslot_str[3] = "01"; //strcat(getChallengePre, keyslot_str); snprintf(getChallengePre + strlen(getChallengePre), 3, "%02u", keyslot); @@ -547,7 +547,7 @@ static int select_DF_verify(uint8_t *response, uint8_t response_length, uint8_t // Response is an ASN.1 encoded structure // Extract everything before the 8E tag - int res = PM3_EWRONGANSWER; + int res = PM3_EWRONGANSWER; for (int i = 0; i < response_length; i++) { // extract MAC if (response[i] == 0x8E) { @@ -579,7 +579,7 @@ static int select_DF_verify(uint8_t *response, uint8_t response_length, uint8_t // PrintAndLogEx(INFO, "Supp MAC......................... " _YELLOW_("%s"), sprint_hex_inrow(MAC_value, MAC_value_len)); // PrintAndLogEx(INFO, "Calc MAC......................... " _YELLOW_("%s"), sprint_hex_inrow(cmac, sizeof(cmac))); -out: +out: PrintAndLogEx(INFO, "--- " _CYAN_("MAC") " ---------------------------"); PrintAndLogEx(ERR, _RED_("MAC Verification Failed")); return PM3_ESOFT; @@ -753,16 +753,15 @@ static int select_ADF_decrypt(const char *selectADFOID, uint8_t *CRYPTOGRAM_encr } } - return PM3_SUCCESS; + return PM3_ESOFT; }; static int seos_mutual_auth(uint8_t *randomICC, uint8_t *CRYPTOGRAM_Diversifier, uint8_t diversifier_len, uint8_t *mutual_auth_randomIFD, uint8_t *mutual_auth_keyICC, uint8_t *randomIFD, uint8_t randomIFD_len, uint8_t *keyIFD, uint8_t keyIFD_len, int encryption_algorithm, int hash_algorithm, int key_index) { uint8_t response[PM3_CMD_DATA_SIZE]; // ---------------- Diversify Keys ---------------- - uint8_t undiversified_key[16] = { 0x00 }; - memcpy(undiversified_key, keys[key_index].readKey, 16); - + uint8_t mk[16] = { 0x00 }; + memcpy(mk, keys[key_index].readKey, 16); uint8_t keyslot = 0x01; // up to 0x0F uint8_t AES_key[24] = {0x00}; uint8_t MAC_key[24] = {0x00}; @@ -776,8 +775,8 @@ static int seos_mutual_auth(uint8_t *randomICC, uint8_t *CRYPTOGRAM_Diversifier, return PM3_ESOFT; } - seos_kdf(true, undiversified_key, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, AES_key, encryption_algorithm, hash_algorithm); - seos_kdf(false, undiversified_key, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, MAC_key, encryption_algorithm, hash_algorithm); + seos_kdf(true, mk, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, AES_key, encryption_algorithm, hash_algorithm); + seos_kdf(false, mk, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, MAC_key, encryption_algorithm, hash_algorithm); memcpy(&MAC_key[16], &MAC_key[0], 8); memcpy(&AES_key[16], &AES_key[0], 8); @@ -843,7 +842,7 @@ static int seos_mutual_auth(uint8_t *randomICC, uint8_t *CRYPTOGRAM_Diversifier, bool activate_field = false; bool keep_field_on = true; - uint8_t aMUTUAL_AUTH[102]; + uint8_t aMUTUAL_AUTH[102] = {0}; int aMUTUAL_AUTH_n = 0; param_gethex_to_eol(mutual_auth, 0, aMUTUAL_AUTH, sizeof(aMUTUAL_AUTH), &aMUTUAL_AUTH_n); int res = ExchangeAPDU14a(aMUTUAL_AUTH, aMUTUAL_AUTH_n, activate_field, keep_field_on, response, sizeof(response), &resplen); @@ -976,7 +975,9 @@ static int seos_aid_select(void) { // if we made it here, its a success and we break :) break; } - + if (i == ARRAYLEN(known_AID_map)) { + return PM3_ESOFT; + } return res; }; @@ -1000,7 +1001,7 @@ static int seos_pacs_adf_select(char *oid, int oid_len, uint8_t *get_data, int g uint16_t selectedOIDLen = strlen(selectedOID); char selectedOIDLenHex[3]; - snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen) / 2); + snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen >> 1) & 0xFF); char selectedADF[strlen(ADFprefix) + strlen(selectedOIDLenHex) + selectedOIDLen + 1]; snprintf(selectedADF, sizeof(selectedADF), "%s%s%s", ADFprefix, selectedOIDLenHex, selectedOID); @@ -1112,10 +1113,9 @@ static int seos_adf_select(char *oid, int oid_len, int key_index) { const char *ADFprefix = "06"; char selectedOID[100]; snprintf(selectedOID, sizeof(selectedOID), "%s", oid); - uint16_t selectedOIDLen = strlen(selectedOID); char selectedOIDLenHex[3]; - snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen) / 2); + snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen >> 1) & 0xFF); char selectedADF[strlen(ADFprefix) + strlen(selectedOIDLenHex) + selectedOIDLen + 1]; snprintf(selectedADF, sizeof(selectedADF), "%s%s%s", ADFprefix, selectedOIDLenHex, selectedOID); @@ -1381,7 +1381,7 @@ static int CmdHfSeosPACS(const char *Cmd) { uint8_t get_data[] = {0x5c, 0x02, 0xff, 0x00}; int oid_len = 0; - uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02}; + uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02, 0x02}; CLIGetHexWithReturn(ctx, 1, oid_hex, &oid_len); int key_index = arg_get_int_def(ctx, 2, 0); @@ -1390,7 +1390,7 @@ static int CmdHfSeosPACS(const char *Cmd) { // Fall back to default OID if (oid_len == 0) { - oid_len = 16; + oid_len = 17; } // convert OID hex to literal string @@ -1440,7 +1440,7 @@ static int CmdHfSeosADF(const char *Cmd) { CLIGetHexWithReturn(ctx, 1, get_data, &get_data_len); int oid_len = 0; - uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02}; + uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02, 0x02}; CLIGetHexWithReturn(ctx, 2, oid_hex, &oid_len); int key_index = arg_get_int_def(ctx, 3, 0); @@ -1453,7 +1453,7 @@ static int CmdHfSeosADF(const char *Cmd) { // Catching when the OID value is not supplied if (oid_len == 0) { - oid_len = 16; + oid_len = 17; } // convert OID hex to literal string @@ -1669,7 +1669,7 @@ static int CmdHfSeosSAM(const char *Cmd) { data[0] = flags; int cmdlen = 0; - if (CLIParamHexToBuf(arg_get_str(ctx, 5), data+1, PM3_CMD_DATA_SIZE-1, &cmdlen) != PM3_SUCCESS){ + if (CLIParamHexToBuf(arg_get_str(ctx, 5), data + 1, PM3_CMD_DATA_SIZE - 1, &cmdlen) != PM3_SUCCESS) { CLIParserFree(ctx); return PM3_ESOFT; } @@ -1681,7 +1681,7 @@ static int CmdHfSeosSAM(const char *Cmd) { } clearCommandBuffer(); - SendCommandNG(CMD_HF_SAM_SEOS, data, cmdlen+1); + SendCommandNG(CMD_HF_SAM_SEOS, data, cmdlen + 1); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_SAM_SEOS, &resp, 4000) == false) { PrintAndLogEx(WARNING, "SAM timeout"); @@ -1756,7 +1756,7 @@ static command_t CommandTable[] = { {"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"}, {"sam", CmdHfSeosSAM, IfPm3Smartcard, "SAM tests"}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"}, - {"info", CmdHfSeosInfo, IfPm3NfcBarcode, "Tag information"}, + {"info", CmdHfSeosInfo, IfPm3Iso14443a, "Tag information"}, {"pacs", CmdHfSeosPACS, AlwaysAvailable, "Extract PACS Information from card"}, {"adf", CmdHfSeosADF, AlwaysAvailable, "Read an ADF from the card"}, {"gdf", CmdHfSeosGDF, AlwaysAvailable, "Read an GDF from card"}, diff --git a/client/src/cmdhfseos.h b/client/src/cmdhfseos.h index f1dfe99a0..46ce3d32e 100644 --- a/client/src/cmdhfseos.h +++ b/client/src/cmdhfseos.h @@ -21,6 +21,12 @@ #include "common.h" +#define SEOS_ENCRYPTION_2K3DES 0x02 +#define SEOS_ENCRYPTION_3K3DES 0x03 +#define SEOS_ENCRYPTION_AES 0x09 + +#define SEOS_HASHING_SHA1 0x06 +#define SEOS_HASHING_SHA256 0x07 int infoSeos(bool verbose); int CmdHFSeos(const char *Cmd); int seos_kdf(bool encryption, uint8_t *masterKey, uint8_t keyslot, diff --git a/client/src/cmdhfst25ta.c b/client/src/cmdhfst25ta.c index 7dff65067..b2846b689 100644 --- a/client/src/cmdhfst25ta.c +++ b/client/src/cmdhfst25ta.c @@ -150,7 +150,6 @@ static void print_st25ta_system_info(uint8_t *d, uint8_t n) { static int print_st25ta_signature(uint8_t *uid, uint8_t *signature) { int index = originality_check_verify_ex(uid, 7, signature, 32, PK_ST25TA, false, true); - PrintAndLogEx(NORMAL, ""); return originality_check_print(signature, 32, index); } diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index ecb252ffc..145392eb7 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -108,6 +108,8 @@ static void em410x_construct_emul_graph(uint8_t *uid, uint8_t clock, uint8_t gap // print 64 bit EM410x ID in multiple formats void printEM410x(uint32_t hi, uint64_t id, bool verbose, int type) { + if (!id && !hi) return; + if (verbose == false) { if (type & 0x1) { // Short ID PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id); @@ -252,6 +254,11 @@ static int ask_em410x_binary_decode(bool verbose, uint32_t *hi, uint64_t *lo, ui return PM3_ESOFT; } + if (!lo && !hi) { + PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x decoded to all zeros"); + return PM3_ESOFT; + } + PrintAndLogEx(DEBUG, "DEBUG: Em410x idx: %zu, Len: %zu, Printing DemodBuffer:", *idx, *size); if (g_debugMode) { printDemodBuff(0, false, false, true); diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index e1ee8181f..bda13661f 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -30,12 +30,10 @@ static int CmdHelp(const char *Cmd); -// Each record is 4 bytes long ... a single line in the dump output -// Reads each record from `data`, reverses the four bytes, and writes to `words` -static void em4x50_prepare_result(const uint8_t *data, int first_record_inclusive, int last_record_inclusive, em4x50_word_t *words) { +static void em4x50_prepare_result(const uint8_t *data, int fwr, int lwr, em4x50_word_t *words) { // restructure received result in "em4x50_word_t" structure - for (int i = first_record_inclusive; i <= last_record_inclusive; i++) { + for (int i = fwr; i <= lwr; i++) { for (int j = 0; j < 4; j++) { words[i].byte[j] = data[i * 4 + (3 - j)]; } @@ -641,8 +639,10 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { return PM3_ESOFT; } + em4x50_read_data_response_t *o = (em4x50_read_data_response_t *)resp.data.asBytes; + em4x50_word_t words[EM4X50_NO_WORDS] = {0}; - em4x50_prepare_result(resp.data.asBytes, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); + em4x50_prepare_result((uint8_t *)o->words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); if (out != NULL) { memcpy(out, &words, sizeof(em4x50_word_t) * EM4X50_NO_WORDS); diff --git a/client/src/cmdlfguard.c b/client/src/cmdlfguard.c index ea567499a..f8e3a5112 100644 --- a/client/src/cmdlfguard.c +++ b/client/src/cmdlfguard.c @@ -98,7 +98,7 @@ static int demod_guard_raw(uint8_t *raw, uint8_t rlen) { // but will leave the g_GraphBuffer intact. // if successful it will push askraw data back to g_DemodBuffer ready for emulation int demodGuard(bool verbose) { - (void) verbose; // unused so far + (void) verbose; //Differential Biphase //get binary from ask wave if (ASKbiphaseDemod(0, 64, 0, 0, false) != PM3_SUCCESS) { @@ -285,7 +285,7 @@ static int CmdGuardClone(const char *Cmd) { return PM3_EINVARG; } - fmtlen &= 0x7f; + fmtlen &= 0x7F; uint32_t facilitycode = (fc & 0x000000FF); uint32_t cardnumber = (cn & 0x00FFFFFF); @@ -317,7 +317,7 @@ static int CmdGuardClone(const char *Cmd) { free(bs); - PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with Facility Code: " _GREEN_("%u") " Card Number: " _GREEN_("%u") " xorKey: " _GREEN_("%u") + PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with fc: " _GREEN_("%u") " cn: " _GREEN_("%u") " xor: " _GREEN_("%u") , cardtype , facilitycode , cardnumber @@ -375,7 +375,7 @@ static int CmdGuardSim(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - xorKey: " _YELLOW_("%u") " Facility Code: " _YELLOW_("%u") " CardNumber: " _YELLOW_("%u") + PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - xorKey: " _YELLOW_("%u") " fc: " _YELLOW_("%u") " cn: " _YELLOW_("%u") , xorval , facilitycode , cardnumber diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index 4ad3cbc66..6b651dccc 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -160,8 +160,7 @@ int demodHID(bool verbose) { return PM3_ESOFT; } - wiegand_message_t packed = initialize_message_object(hi2, hi, lo, 0); - if (HIDTryUnpack(&packed) == false) { + if (!decode_wiegand(hi2, hi, lo, 0)) { // if failed to unpack wiegand printDemodBuff(0, false, false, true); } PrintAndLogEx(INFO, "raw: " _GREEN_("%08x%08x%08x"), hi2, hi, lo); @@ -544,6 +543,7 @@ static int CmdHIDBrute(const char *Cmd) { } wiegand_card_t card_hi, card_low; + cardformatdescriptor_t card_descriptor = HIDGetCardFormat(format_idx).Fields; memset(&card_hi, 0, sizeof(wiegand_card_t)); char field[3] = {0}; @@ -575,11 +575,13 @@ static int CmdHIDBrute(const char *Cmd) { PrintAndLogEx(INFO, "Facility code.... %u", card_hi.FacilityCode); PrintAndLogEx(INFO, "Card number...... %" PRIu64, card_hi.CardNumber); PrintAndLogEx(INFO, "Delay............ " _YELLOW_("%d"), delay); + if (strcmp(field, "fc") == 0) { PrintAndLogEx(INFO, "Field............ " _YELLOW_("fc")); } else if (strcmp(field, "cn") == 0) { PrintAndLogEx(INFO, "Field............ " _YELLOW_("cn")); } + switch (direction) { case 0: PrintAndLogEx(INFO, "Direction........ " _YELLOW_("both")); @@ -594,6 +596,7 @@ static int CmdHIDBrute(const char *Cmd) { break; } } + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Started bruteforcing HID Prox reader"); PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to abort simulation"); @@ -623,13 +626,13 @@ static int CmdHIDBrute(const char *Cmd) { return PM3_ESOFT; } if (strcmp(field, "fc") == 0) { - if (card_hi.FacilityCode < 0xFF) { + if (card_hi.FacilityCode < card_descriptor.MaxFC) { card_hi.FacilityCode++; } else { fin_hi = true; } } else if (strcmp(field, "cn") == 0) { - if (card_hi.CardNumber < 0xFFFF) { + if (card_hi.CardNumber < card_descriptor.MaxCN) { card_hi.CardNumber++; } else { fin_hi = true; diff --git a/client/src/cmdlfpcf7931.c b/client/src/cmdlfpcf7931.c index 53bc19f03..8b7e73def 100644 --- a/client/src/cmdlfpcf7931.c +++ b/client/src/cmdlfpcf7931.c @@ -105,8 +105,8 @@ static int CmdLFPCF7931Config(const char *Cmd) { arg_lit0("r", "reset", "Reset configuration to default values"), arg_str0("p", "pwd", "", "Password, 7bytes, LSB-order"), arg_u64_0("d", "delay", "", "Tag initialization delay (in us)"), - arg_int0(NULL, "lw", "", "offset, low pulses width (in us)"), - arg_int0(NULL, "lp", "", "offset, low pulses position (in us)"), + arg_int0(NULL, "lw", "", "offset, low pulses width (in us), optional!"), + arg_int0(NULL, "lp", "", "offset, low pulses position (in us), optional!"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index dcc87e1fa..f61914e66 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -305,6 +305,7 @@ static int CmdPyramidClone(const char *Cmd) { uint8_t *bs = calloc(128, sizeof(uint8_t)); if (bs == NULL) { + PrintAndLogEx(WARNING, "failed to allocate memory"); return PM3_EMALLOC; } diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 4f5f6c3ca..33f3cdf7c 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -1550,6 +1550,7 @@ bool testKnownConfigBlock(uint32_t block0) { case T55X7_NEXWATCH_CONFIG_BLOCK: case T55X7_JABLOTRON_CONFIG_BLOCK: case T55X7_PYRONIX_CONFIG_BLOCK: + case T55X7_TEXECOM_CONFIG_BLOCK: return true; } return false; @@ -2298,6 +2299,9 @@ static void printT5x7KnownBlock0(uint32_t b0) { case T55X7_PYRONIX_CONFIG_BLOCK: snprintf(s + strlen(s), sizeof(s) - strlen(s), "Pyronix "); break; + case T55X7_TEXECOM_CONFIG_BLOCK: + snprintf(s + strlen(s), sizeof(s) - strlen(s), "Texecom "); + break; default: break; } diff --git a/client/src/cmdlft55xx.h b/client/src/cmdlft55xx.h index c3b8c31df..9160050a5 100644 --- a/client/src/cmdlft55xx.h +++ b/client/src/cmdlft55xx.h @@ -43,6 +43,7 @@ #define T55X7_SECURAKEY_CONFIG_BLOCK 0x000C8060 // ASK, Manchester, data rate 40, 3 data blocks #define T55X7_UNK_CONFIG_BLOCK 0x000880FA // ASK, Manchester, data rate 32, 7 data blocks STT, Inverse ... #define T55X7_PYRONIX_CONFIG_BLOCK 0x00088C40 // ASK, Manchester, data rate 32, 2 data blocks +#define T55X7_TEXECOM_CONFIG_BLOCK 0x001C8020 // ASK, Manchester, data rate 128, 1 data block // FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag // we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo, diff --git a/client/src/cmdpiv.c b/client/src/cmdpiv.c index 4c297124b..423dcfdab 100644 --- a/client/src/cmdpiv.c +++ b/client/src/cmdpiv.c @@ -596,8 +596,8 @@ static int PivGetData(Iso7816CommandChannel channel, const uint8_t tag[], size_t // Answer can be chained. Let's use a dynamically allocated buffer. size_t capacity = PM3_CMD_DATA_SIZE; struct tlvdb_root *root = calloc(1, sizeof(*root) + capacity); - if (root == NULL) { + PrintAndLogEx(WARNING, "failed to allocate memory"); return PM3_EMALLOC; } root->len = 0; diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 04b92a8f6..aa27fe6d4 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -1067,8 +1067,10 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { smart_loadjson("aidlist", &root); uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); - if (!buf) + if (buf == NULL) { + PrintAndLogEx(WARNING, "failed to allocate memory"); return PM3_EMALLOC; + } PrintAndLogEx(INFO, "Selecting card"); if (!smart_select(false, NULL)) { diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index c91f6a31b..07ffc3377 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -164,6 +164,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } // extract MFC + /* switch (frame[0]) { case MIFARE_AUTH_KEYA: { if (data_len > 3) { @@ -176,9 +177,11 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t break; } } + */ - // extract MFU-C + // extract MFU-C KEY when written. switch (frame[0]) { + case MIFARE_ULC_AUTH_1: { if (data_len != 4) { break; @@ -195,7 +198,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t break; } - PrintAndLogEx(INFO, "MFU-C AUTH"); + PrintAndLogEx(INFO, "Found a MFU-C authententication attempt"); PrintAndLogEx(INFO, "3DES %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, 8)); next_hdr = (tracelog_hdr_t *)(trace + tracepos); @@ -203,6 +206,8 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t if (next_hdr->frame[0] == MIFARE_ULC_AUTH_2 && next_hdr->data_len == 19) { PrintAndLogEx(NORMAL, "%s", sprint_hex_inrow(next_hdr->frame + 1, 16)); + } else { + PrintAndLogEx(NORMAL, "( " _RED_("partial") " )"); } return tracepos; @@ -323,7 +328,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t case MFDES_AUTHENTICATE: { // Assume wrapped or unwrapped - PrintAndLogEx(INFO, "AUTH NATIVE (keyNo %d)", frame[pos + long_jmp]); + PrintAndLogEx(INFO, "Found a MFDES Auth NATIVE (keyNo %d)", frame[pos + long_jmp]); if (next_record_is_response(tracepos, trace) == false) { break; } @@ -348,7 +353,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } case MFDES_AUTHENTICATE_ISO: { // Assume wrapped or unwrapped - PrintAndLogEx(INFO, "AUTH ISO (keyNo %d)", frame[pos + long_jmp]); + PrintAndLogEx(INFO, "Found a MFDES Auth ISO (keyNo %d)", frame[pos + long_jmp]); if (next_record_is_response(tracepos, trace) == false) { break; } @@ -379,7 +384,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } case MFDES_AUTHENTICATE_AES: { // Assume wrapped or unwrapped - PrintAndLogEx(INFO, "AUTH AES (keyNo %d)", frame[pos + long_jmp]); + PrintAndLogEx(INFO, "Found a MFDES Auth AES (keyNo %d)", frame[pos + long_jmp]); if (next_record_is_response(tracepos, trace)) { break; } @@ -403,7 +408,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t return tracepos; } case MFDES_AUTHENTICATE_EV2F: { - PrintAndLogEx(INFO, "AUTH EV2 First"); + PrintAndLogEx(INFO, "Found a MFDES Auth EV2 First"); uint16_t tmp = extractChall_ev2(tracepos, trace, pos, long_jmp); if (tmp == 0) break; @@ -412,7 +417,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } case MFDES_AUTHENTICATE_EV2NF: { - PrintAndLogEx(INFO, "AUTH EV2 Non First"); + PrintAndLogEx(INFO, "Found a MFDES Auth EV2 Non First"); uint16_t tmp = extractChall_ev2(tracepos, trace, pos, long_jmp); if (tmp == 0) break; @@ -556,9 +561,11 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case ISO_14443A: case MFDES: case LTO: - case SEOS: crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len); break; + case SEOS: + crcStatus = seos_CRC_check(hdr->isResponse, frame, data_len); + break; case ISO_7816_4: crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len) == 1 ? 3 : 0; crcStatus = iso14443B_CRC_check(frame, data_len) == 1 ? 4 : crcStatus; @@ -798,6 +805,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case ICLASS: annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; + case SEOS: + annotateSeos(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); + break; default: break; } @@ -834,9 +844,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case PROTO_CRYPTORF: annotateCryptoRF(explanation, sizeof(explanation), frame, data_len); break; - case SEOS: - annotateSeos(explanation, sizeof(explanation), frame, data_len); - break; case PROTO_FMCOS20: annotateFMCOS20(explanation, sizeof(explanation), frame, data_len); break; diff --git a/client/src/cmdwiegand.c b/client/src/cmdwiegand.c index 9fc06b6f9..e6ae43629 100644 --- a/client/src/cmdwiegand.c +++ b/client/src/cmdwiegand.c @@ -33,6 +33,46 @@ static int CmdHelp(const char *Cmd); +#define PACS_EXTRA_LONG_FORMAT 18 // 144 bits +#define PACS_LONG_FORMAT 12 // 96 bits +#define PACS_FORMAT 6 // 44 bits +static int wiegand_new_pacs(uint8_t *padded_pacs, uint8_t plen) { + + uint8_t d[PACS_EXTRA_LONG_FORMAT] = {0}; + memcpy(d, padded_pacs, plen); + + uint8_t pad = d[0]; + + char *binstr = (char *)calloc((PACS_EXTRA_LONG_FORMAT * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + PrintAndLogEx(INFO, "failed to allocate memory"); + return PM3_EMALLOC; + } + + uint8_t n = plen - 1; + + bytes_2_binstr(binstr, d + 1, n); + + binstr[strlen(binstr) - pad] = '\0'; + + size_t tlen = 0; + uint8_t tmp[16] = {0}; + binstr_2_bytes(tmp, &tlen, binstr); + PrintAndLogEx(SUCCESS, "Wiegand raw.... " _YELLOW_("%s"), sprint_hex_inrow(tmp, tlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - Wiegand") " ---------------------------"); + decode_wiegand(top, mid, bot, strlen(binstr)); + free(binstr); + return PM3_SUCCESS; +} int CmdWiegandList(const char *Cmd) { CLIParserContext *ctx; @@ -116,16 +156,18 @@ int CmdWiegandDecode(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "wiegand decode", "Decode raw hex or binary to wiegand format", - "wiegand decode --raw 2006f623ae" + "wiegand decode --raw 2006F623AE\n" + "wiegand decode --new 06BD88EB80 -> 4..8 bytes, new padded format " ); void *argtable[] = { arg_param_begin, arg_str0("r", "raw", "", "raw hex to be decoded"), arg_str0("b", "bin", "", "binary string to be decoded"), + arg_str0("n", "new", "", "new padded pacs as raw hex to be decoded"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIExecWithReturn(ctx, Cmd, argtable, false); int hlen = 0; char hex[40] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)hex, sizeof(hex), &hlen); @@ -133,6 +175,11 @@ int CmdWiegandDecode(const char *Cmd) { int blen = 0; uint8_t binarr[100] = {0x00}; int res = CLIParamBinToBuf(arg_get_str(ctx, 2), binarr, sizeof(binarr), &blen); + + int plen = 0; + uint8_t phex[8] = {0}; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), phex, sizeof(phex), &plen); + CLIParserFree(ctx); if (res) { @@ -155,13 +202,20 @@ int CmdWiegandDecode(const char *Cmd) { return PM3_EINVARG; } PrintAndLogEx(INFO, "Input bin len... %d", blen); + + } else if (plen) { + + return wiegand_new_pacs(phex, plen); + } else { PrintAndLogEx(ERR, "Empty input"); return PM3_EINVARG; } - wiegand_message_t packed = initialize_message_object(top, mid, bot, blen); - HIDTryUnpack(&packed); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------"); + + decode_wiegand(top, mid, bot, blen); return PM3_SUCCESS; } diff --git a/client/src/crypto/asn1utils.c b/client/src/crypto/asn1utils.c index 1fa839a7f..67392f534 100644 --- a/client/src/crypto/asn1utils.c +++ b/client/src/crypto/asn1utils.c @@ -107,6 +107,37 @@ int asn1_print(uint8_t *asn1buf, size_t asn1buflen, const char *indent) { return PM3_SUCCESS; } +int asn1_get_tag_length(const uint8_t *data, size_t *n, size_t *offset, size_t total_length) { + + if (*offset >= total_length) { + return -1; + } + + if (data[*offset] & 0x80) { + + // Long form: number of length bytes is indicated by the lower 7 bits + size_t len_bytes = data[*offset] & 0x7F; + + *offset += 1; + + if (*offset + len_bytes > total_length) { + return -1; + } + + *n = 0; + for (size_t i = 0; i < len_bytes; i++) { + *n = (*n << 8) | data[*offset]; + *offset += 1; + } + } else { + // Short form: length is directly represented + *n = data[*offset]; + *offset += 1; + } + + return 0; +} + typedef struct { const char *hex; diff --git a/client/src/crypto/asn1utils.h b/client/src/crypto/asn1utils.h index e55a2926f..57d443e6a 100644 --- a/client/src/crypto/asn1utils.h +++ b/client/src/crypto/asn1utils.h @@ -25,6 +25,8 @@ int asn1_print(uint8_t *asn1buf, size_t asn1buflen, const char *indent); int ecdsa_asn1_get_signature(uint8_t *signature, size_t signaturelen, uint8_t *rval, uint8_t *sval); + +int asn1_get_tag_length(const uint8_t *data, size_t *n, size_t *offset, size_t total_length); int asn1_selftest(void); #endif /* asn1utils.h */ diff --git a/client/src/crypto/originality.c b/client/src/crypto/originality.c index 5d667420f..6b6e687a0 100644 --- a/client/src/crypto/originality.c +++ b/client/src/crypto/originality.c @@ -136,25 +136,28 @@ const ecdsa_publickey_ng_t manufacturer_public_keys[] = { }; -// return pk if match index else -1 +// returns index of pk if match else -1 int originality_check_verify(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type) { return originality_check_verify_ex(data, data_len, signature, signature_len, type, false, false); } int originality_check_verify_ex(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type, bool reverse, bool hash) { - // test if signature is null + // test if signature is all zeros bool is_zero = true; for (uint8_t i = 0; i < signature_len; i++) { if (signature[i] != 0) { is_zero = false; + break; } } + if (is_zero) { return -1; } uint8_t tmp_data[data_len]; uint8_t tmp_signature[signature_len]; + if (reverse) { reverse_array_copy(data, data_len, tmp_data); reverse_array_copy(signature, signature_len, tmp_signature); @@ -180,17 +183,24 @@ int originality_check_verify_ex(uint8_t *data, uint8_t data_len, uint8_t *signat } int originality_check_print(uint8_t *signature, int signature_len, int index) { + + PrintAndLogEx(NORMAL, ""); + if ((index < 0) || (index >= ARRAYLEN(manufacturer_public_keys))) { + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16)); if (signature_len > 16) { PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16)); } + if (signature_len > 32) { PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16)); } + if (signature_len > 48) { PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48)); } + PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); return PM3_ESOFT; } @@ -200,23 +210,30 @@ int originality_check_print(uint8_t *signature, int signature_len, int index) { if (manufacturer_public_keys[index].keylen > 16) { PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 32); } + if (manufacturer_public_keys[index].keylen > 32) { PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 64); } + if (manufacturer_public_keys[index].keylen > 48) { PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 96); } + PrintAndLogEx(INFO, " Elliptic curve parameters: %s", mbedtls_ecp_curve_info_from_grp_id(manufacturer_public_keys[index].grp_id)->name); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16)); + if (signature_len > 16) { PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16)); } + if (signature_len > 32) { PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16)); } + if (signature_len > 48) { PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48)); } + PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); return PM3_SUCCESS; } diff --git a/client/src/emv/cmdemv.c b/client/src/emv/cmdemv.c index 4d7daec9e..4c38dae53 100644 --- a/client/src/emv/cmdemv.c +++ b/client/src/emv/cmdemv.c @@ -2122,9 +2122,9 @@ static int CmdEMVScan(const char *Cmd) { uint8_t psenum = (channel == CC_CONTACT) ? 1 : 2; - char filename[FILE_PATH_SIZE] = {0}; - int fnlen = 0; - CLIParamStrToBuf(arg_get_str(ctx, 12), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + uint8_t filename[FILE_PATH_SIZE] = {0}; + int filenamelen = sizeof(filename) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated + CLIGetStrWithReturn(ctx, 12, filename, &filenamelen); CLIParserFree(ctx); @@ -2507,7 +2507,7 @@ static int CmdEMVRoca(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("t", "selftest", "Self test"), + arg_lit0(NULL, "test", "Perform self tests"), arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end @@ -2981,7 +2981,7 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdEMVList, AlwaysAvailable, "List ISO7816 history"}, - {"test", CmdEMVTest, AlwaysAvailable, "Crypto logic selftest"}, + {"test", CmdEMVTest, AlwaysAvailable, "Perform crypto logic self tests"}, {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("Operations") " ---------------------"}, {"challenge", CmdEMVGenerateChallenge, IfPm3Iso14443, "Generate challenge"}, {"exec", CmdEMVExec, IfPm3Iso14443, "Executes EMV contactless transaction"}, diff --git a/client/src/fileutils.c b/client/src/fileutils.c index db151f3a1..2ba24d2e4 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -809,7 +809,7 @@ int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags if (res == 0) { if (verbose) { - PrintAndLogEx(SUCCESS, "Saved to json file `" _YELLOW_("%s") "`", filename); + PrintAndLogEx(SUCCESS, "Saved to json file " _YELLOW_("%s"), filename); } free(filename); return PM3_SUCCESS; @@ -1105,7 +1105,9 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, nfc_df_e ft) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL) { + return PM3_EINVARG; + } *datalen = 0; int retval = PM3_SUCCESS; @@ -1137,16 +1139,17 @@ int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, s memset(line, 0, sizeof(line)); if (fgets(line, sizeof(line), f) == NULL) { - if (feof(f)) + if (feof(f)) { break; - + } fclose(f); PrintAndLogEx(FAILED, "file reading error"); return PM3_EFILE; } - if (line[0] == '#') + if (line[0] == '#') { continue; + } str_cleanrn(line, sizeof(line)); str_lower(line); @@ -2409,7 +2412,6 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo // larger keys than expected is skipped if (strlen(line) > keylen) { - PrintAndLogEx(INFO, "larger %zu - %s", strlen(line), line); continue; } @@ -2626,6 +2628,7 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool fclose(f); if (verbose) { + switch (*dump_type) { case NFC_DF_MFU: PrintAndLogEx(INFO, "Detected MIFARE Ultralight / NTAG based dump format"); @@ -3081,6 +3084,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl PrintAndLogEx(WARNING, "fail, cannot allocate memory"); return PM3_EMALLOC; } + res = loadFileJSON(fn, *pdump, maxdumplen, dumplen, NULL); if (res == PM3_SUCCESS) { return res; @@ -3107,7 +3111,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl break; } case FLIPPER: { - nfc_df_e dumptype; + nfc_df_e dumptype = NFC_DF_UNKNOWN; res = detect_nfc_dump_format(fn, &dumptype, true); if (res != PM3_SUCCESS) { break; diff --git a/client/src/fileutils.h b/client/src/fileutils.h index ae04b8265..6fc450dd1 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -282,8 +282,31 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale */ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t keylen, uint32_t *keycnt); +/** + * @brief Utility function to load data safely from a DICTIONARY textfile. This method takes a preferred name. + * E.g. mfc_default_keys.dic + * + * @param preferredName + * @param suffix + * @param pdata A pointer to a pointer (for reverencing the loaded dictionary) + * @param keylen the number of bytes a key per row is + * @param verbose print messages if true + * @return 0 for ok, 1 for failz +*/ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, void **pdata, uint8_t keylen, uint32_t *keycnt, bool verbose); +/** + * @brief Utility function to load data from a XML textfile. This method takes a preferred name. + * E.g. dumpdata-15.xml + * + * @param preferredName + * @param data The data array to store the loaded bytes from file + * @param maxdatalen maximum size of data array in bytes + * @param datalen the number of bytes loaded from file + * @return 0 for ok, 1 for failz +*/ +int loadFileXML_safe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen); + int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen); /** diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index ac44508f2..f390c3ca2 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -1008,7 +1008,7 @@ void DesfirePrintAIDFunctions(uint32_t appid) { if ((aid[2] >> 4) == 0xF) { uint16_t short_aid = ((aid[2] & 0xF) << 12) | (aid[1] << 4) | (aid[0] >> 4); PrintAndLogEx(SUCCESS, " AID mapped to MIFARE Classic AID (MAD): " _YELLOW_("%02X"), short_aid); - PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X : " _YELLOW_("%s"), short_aid >> 8, nxp_cluster_to_text(short_aid >> 8)); + PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X..... " _YELLOW_("%s"), short_aid >> 8, nxp_cluster_to_text(short_aid >> 8)); MADDFDecodeAndPrint(short_aid, false); } else { AIDDFDecodeAndPrint(aid); @@ -1016,53 +1016,64 @@ void DesfirePrintAIDFunctions(uint32_t appid) { } int DesfireSelectAndAuthenticateEx(DesfireContext_t *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose) { - if (verbose) + if (verbose) { DesfirePrintContext(dctx); + } // needs card uid for diversification - if (dctx->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) + if (dctx->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) { DesfireGetCardUID(dctx); + } bool isosw = false; if (dctx->cmdSet == DCCISO) { dctx->cmdSet = DCCNativeISO; isosw = true; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Switch to " _CYAN_("native") " for select"); + } } int res; if (aid == 0x000000) { res = DesfireAnticollision(verbose); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire anticollision " _RED_("error") "."); + PrintAndLogEx(ERR, "Desfire anticollision " _RED_("fail")); return 200; } - if (verbose) + + if (verbose) { PrintAndLogEx(INFO, "Anticollision " _GREEN_("ok")); + } + } else { res = DesfireSelectAIDHex(dctx, aid, false, 0); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + PrintAndLogEx(ERR, "Desfire select " _RED_("fail")); return 200; } - if (verbose) + + if (verbose) { PrintAndLogEx(INFO, "App %06x " _GREEN_("selected"), aid); + } } - if (isosw) + if (isosw) { dctx->cmdSet = DCCISO; + } if (noauth == false) { + res = DesfireAuthenticate(dctx, secureChannel, verbose); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res)); + PrintAndLogEx(ERR, "Desfire authenticate " _RED_("fail") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res)); return res; } if (DesfireIsAuthenticated(dctx)) { - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated")); + } } else { return 201; } @@ -1087,7 +1098,7 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s res = DesfireSelectAIDHex(dctx, id, false, 0); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + PrintAndLogEx(ERR, "Desfire select " _RED_("error")); return 200; } if (verbose) @@ -1097,7 +1108,7 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s } else { res = DesfireSelectEx(dctx, true, way, id, NULL); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire %s select " _RED_("error") ".", DesfireSelectWayToStr(way)); + PrintAndLogEx(ERR, "Desfire %s select " _RED_("error"), DesfireSelectWayToStr(way)); return 202; } if (verbose) @@ -1107,12 +1118,13 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s if (selectfile) { res = DesfireSelectEx(dctx, false, ISWIsoID, isofileid, NULL); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error") "."); + PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error")); return 203; } - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Application %s file iso id %04x is " _GREEN_("selected"), DesfireWayIDStr(way, id), isofileid); + } } if (!noauth) { @@ -1123,8 +1135,9 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s } if (DesfireIsAuthenticated(dctx)) { - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated")); + } } else { return 201; } @@ -1864,17 +1877,21 @@ int DesfireFillAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS ap void DesfirePrintPICCInfo(DesfireContext_t *dctx, PICCInfo_t *PICCInfo) { PrintAndLogEx(SUCCESS, "------------------------------------ " _CYAN_("PICC level") " -------------------------------------"); - if (PICCInfo->freemem == 0xffffffff) - PrintAndLogEx(SUCCESS, "Applications count: " _GREEN_("%zu") " free memory " _YELLOW_("n/a"), PICCInfo->appCount); - else - PrintAndLogEx(SUCCESS, "Applications count: " _GREEN_("%zu") " free memory " _GREEN_("%d") " bytes", PICCInfo->appCount, PICCInfo->freemem); + if (PICCInfo->freemem == 0xffffffff) { + PrintAndLogEx(SUCCESS, "# applications....... " _YELLOW_("%zu"), PICCInfo->appCount); + } else { + PrintAndLogEx(SUCCESS, "# applications....... " _YELLOW_("%zu"), PICCInfo->appCount); + } + PrintAndLogEx(SUCCESS, ""); + if (PICCInfo->authCmdCheck.checked) { - PrintAndLogEx(SUCCESS, "PICC level auth commands: "); + PrintAndLogEx(SUCCESS, "PICC level auth commands"); DesfireCheckAuthCommandsPrint(&PICCInfo->authCmdCheck); } + if (PICCInfo->numberOfKeys > 0) { PrintKeySettings(PICCInfo->keySettings, PICCInfo->numKeysRaw, false, true); - PrintAndLogEx(SUCCESS, "PICC key 0 version: %d (0x%02x)", PICCInfo->keyVersion0, PICCInfo->keyVersion0); + PrintAndLogEx(SUCCESS, "PICC key "_YELLOW_("0") " version: %d (0x%02x)", PICCInfo->keyVersion0, PICCInfo->keyVersion0); } } @@ -1886,14 +1903,14 @@ void DesfirePrintAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS PrintAndLogEx(SUCCESS, "--------------------------------- " _CYAN_("Applications list") " ---------------------------------"); for (int i = 0; i < PICCInfo->appCount; i++) { - PrintAndLogEx(SUCCESS, _CYAN_("Application number: 0x%02X"), appList[i].appNum); - PrintAndLogEx(SUCCESS, " ISO id.... " _GREEN_("0x%04X"), appList[i].appISONum); - PrintAndLogEx(SUCCESS, " DF name... " _GREEN_("%s") " ( %s)", appList[i].appDFName, sprint_hex((uint8_t *)appList[i].appDFName, sizeof(appList[i].appDFName))); + PrintAndLogEx(SUCCESS, "Application ID....... " _CYAN_("0x%02X"), appList[i].appNum); + PrintAndLogEx(SUCCESS, " ISO id............ " _GREEN_("0x%04X"), appList[i].appISONum); + PrintAndLogEx(SUCCESS, " DF name........... " _GREEN_("%s") " ( %s )", appList[i].appDFName, sprint_hex_inrow((uint8_t *)appList[i].appDFName, sizeof(appList[i].appDFName))); DesfirePrintAIDFunctions(appList[i].appNum); if (PICCInfo->authCmdCheck.checked) { - PrintAndLogEx(SUCCESS, "Auth commands: "); + PrintAndLogEx(SUCCESS, "Auth commands"); DesfireCheckAuthCommandsPrint(&appList[i].authCmdCheck); PrintAndLogEx(SUCCESS, ""); } @@ -1902,7 +1919,7 @@ void DesfirePrintAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS PrintKeySettings(appList[i].keySettings, appList[i].numKeysRaw, true, true); if (appList[i].numberOfKeys > 0) { - PrintAndLogEx(SUCCESS, "Key versions [0..%d]: " NOLF, appList[i].numberOfKeys - 1); + PrintAndLogEx(SUCCESS, "Key versions [0..%d] " NOLF, appList[i].numberOfKeys - 1); for (uint8_t keyn = 0; keyn < appList[i].numberOfKeys; keyn++) { PrintAndLogEx(NORMAL, "%s %02x" NOLF, (keyn == 0) ? "" : ",", appList[i].keyVersions[keyn]); } @@ -2254,7 +2271,7 @@ int DesfireUpdateRecord(DesfireContext_t *dctx, uint8_t fnum, uint32_t recnum, u } static void PrintKeySettingsPICC(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) { - PrintAndLogEx(SUCCESS, "PICC level rights:"); + PrintAndLogEx(SUCCESS, "PICC level rights"); PrintAndLogEx(SUCCESS, "[%c...] CMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); PrintAndLogEx(SUCCESS, "[.%c..] CMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : "YES"); PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with CMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : "YES"); @@ -2263,27 +2280,27 @@ static void PrintKeySettingsPICC(uint8_t keysettings, uint8_t numkeys, bool prin if (print2ndbyte) { DesfirePrintCardKeyType(numkeys >> 6); - PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f); + PrintAndLogEx(SUCCESS, "Key cnt.... " _YELLOW_("%d"), numkeys & 0x0F); } } static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) { // Access rights. - PrintAndLogEx(SUCCESS, "Application level rights:"); + PrintAndLogEx(SUCCESS, "Application level rights"); uint8_t rights = ((keysettings >> 4) & 0x0F); switch (rights) { case 0x0: - PrintAndLogEx(SUCCESS, "-- AMK authentication is necessary to change any key (default)"); + PrintAndLogEx(SUCCESS, " - AMK authentication is necessary to change any key (default)"); break; case 0xE: - PrintAndLogEx(SUCCESS, "-- Authentication with the key to be changed (same KeyNo) is necessary to change a key"); + PrintAndLogEx(SUCCESS, " - Authentication with the key to be changed (same KeyNo) is necessary to change a key"); break; case 0xF: - PrintAndLogEx(SUCCESS, "-- All keys (except AMK,see Bit0) within this application are frozen"); + PrintAndLogEx(SUCCESS, " - All keys (except AMK,see Bit0) within this application are frozen"); break; default: PrintAndLogEx(SUCCESS, - "-- Authentication with the specified key " _YELLOW_("(0x%02x)") " is necessary to change any key.\n" + " - Authentication with the specified key " _YELLOW_("(0x%02x)") " is necessary to change any key.\n" "A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n" "For keys other then the master or change key, an authentication with the same key is needed.", rights & 0x0f @@ -2299,10 +2316,10 @@ static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print if (print2ndbyte) { DesfirePrintCardKeyType(numkeys >> 6); - PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f); - if (numkeys & 0x20) + PrintAndLogEx(SUCCESS, "Key cnt.... " _YELLOW_("%d"), numkeys & 0x0F); + if (numkeys & 0x20) { PrintAndLogEx(SUCCESS, "iso file id: enabled"); - PrintAndLogEx(SUCCESS, ""); + } } } diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index 6263c2321..98c3de0e9 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -504,16 +504,16 @@ uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorithm keyType) { void DesfirePrintCardKeyType(uint8_t keyType) { switch (keyType) { case 00: - PrintAndLogEx(SUCCESS, "Key: 2TDEA"); + PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("2TDEA")); break; case 01: - PrintAndLogEx(SUCCESS, "Key: 3TDEA"); + PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("3TDEA")); break; case 02: - PrintAndLogEx(SUCCESS, "Key: AES"); + PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("AES")); break; default: - PrintAndLogEx(SUCCESS, "Key: unknown: 0x%02x", keyType); + PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("unknown") " - 0x%02x", keyType); break; } } diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index 54f67a6a4..a3365bb04 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -69,8 +69,13 @@ static int open_mad_file(json_t **root, bool verbose) { goto out; } - if (verbose) - PrintAndLogEx(SUCCESS, "Loaded file " _YELLOW_("`%s`") " (%s) %zu records.", path, _GREEN_("ok"), json_array_size(*root)); + if (verbose) { + PrintAndLogEx(SUCCESS, "Loaded file `" _YELLOW_("%s") "` " _GREEN_("%zu") " records ( " _GREEN_("ok") " )" + , path + , json_array_size(*root) + ); + } + out: free(path); return retval; @@ -183,7 +188,7 @@ static uint16_t madGetAID(const uint8_t *sector, bool swapmad, int MADver, int s } } -int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) { +int MADCheck(uint8_t *sector0, uint8_t *sector16, bool verbose, bool *haveMAD2) { if (sector0 == NULL) return PM3_EINVARG; @@ -217,13 +222,13 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector0[16], _GREEN_("ok")); } - if (mad_ver == 2 && sector10) { - int res2 = madCRCCheck(sector10, true, 2); + if (mad_ver == 2 && sector16) { + int res2 = madCRCCheck(sector16, true, 2); if (res == PM3_SUCCESS) res = res2; if (verbose && !res2) - PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector10[0], _GREEN_("ok")); + PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector16[0], _GREEN_("ok")); } // MA (multi-application card) @@ -236,27 +241,30 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) return res; } -int MADDecode(uint8_t *sector0, uint8_t *sector10, uint16_t *mad, size_t *madlen, bool swapmad) { +int MADDecode(uint8_t *sector0, uint8_t *sector16, uint16_t *mad, size_t *madlen, bool swapmad) { *madlen = 0; bool haveMAD2 = false; - int res = MADCheck(sector0, sector10, false, &haveMAD2); + int res = MADCheck(sector0, sector16, false, &haveMAD2); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Not a valid MAD"); return res; } - for (int i = 1; i < 16; i++) { + // 7 + 8 == 15 + for (int i = 1; i <= 16; i++) { mad[*madlen] = madGetAID(sector0, swapmad, 1, i); (*madlen)++; } if (haveMAD2) { - // mad2 sector (0x10 == 16dec) here + // mad2 sector (0x10 == 16) here mad[*madlen] = 0x0005; (*madlen)++; + // 7 + 8 + 8 == 23 for (int i = 1; i < 24; i++) { - mad[*madlen] = madGetAID(sector10, swapmad, 2, i); + mad[*madlen] = madGetAID(sector16, swapmad, 2, i); + (*madlen)++; } } @@ -347,8 +355,16 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMA aid ); } else { - char fmt[60]; - snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i, aid, "%s"); + char fmt[80]; + snprintf(fmt + , sizeof(fmt) + , (ibs == i) ? + _MAGENTA_(" %02d [%04X] %s") : + " %02d [" _GREEN_("%04X") "] %s" + , i + , aid + , "%s" + ); print_aid_description(mad_known_aids, aid, fmt, verbose); prev_aid = aid; } @@ -400,8 +416,16 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { aid ); } else { - char fmt[60]; - snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i + 16, aid, "%s"); + char fmt[80]; + snprintf(fmt + , sizeof(fmt) + , (ibs == i) ? + _MAGENTA_(" %02d [%04X] %s") : + " %02d [" _GREEN_("%04X") "] %s" + , i + 16 + , aid + , "%s" + ); print_aid_description(mad_known_aids, aid, fmt, verbose); prev_aid = aid; } @@ -415,7 +439,7 @@ int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose) { open_mad_file(&mad_known_aids, false); char fmt[128]; - snprintf(fmt, sizeof(fmt), " MAD AID Function 0x%04X :" _YELLOW_("%s"), short_aid, "%s"); + snprintf(fmt, sizeof(fmt), " MAD AID Function 0x%04X... " _YELLOW_("%s"), short_aid, "%s"); print_aid_description(mad_known_aids, short_aid, fmt, verbose); close_mad_file(mad_known_aids); return PM3_SUCCESS; @@ -429,8 +453,9 @@ bool HasMADKey(uint8_t *d) { } int DetectHID(uint8_t *d, uint16_t manufacture) { - if (d == NULL) + if (d == NULL) { return -1; + } // find HID for (int i = 1; i < 16; i++) { @@ -456,16 +481,16 @@ int convert_mad_to_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) } uint8_t sector0[MFBLOCK_SIZE * 4] = {0}; - uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; + uint8_t sector16[MFBLOCK_SIZE * 4] = {0}; memcpy(sector0, in, sizeof(sector0)); if (ilen == MIFARE_4K_MAX_BYTES) { - memcpy(sector10, in + (MF_MAD2_SECTOR * 4 * MFBLOCK_SIZE), sizeof(sector10)); + memcpy(sector16, in + (MF_MAD2_SECTOR * 4 * MFBLOCK_SIZE), sizeof(sector16)); } uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; - if (MADDecode(sector0, sector10, mad, &madlen, false)) { + if (MADDecode(sector0, sector16, mad, &madlen, false)) { PrintAndLogEx(ERR, "can't decode MAD"); return PM3_ESOFT; } diff --git a/client/src/mifare/mad.h b/client/src/mifare/mad.h index d1f5240f4..1da097527 100644 --- a/client/src/mifare/mad.h +++ b/client/src/mifare/mad.h @@ -21,8 +21,8 @@ #include "common.h" -int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2); -int MADDecode(uint8_t *sector0, uint8_t *sector10, uint16_t *mad, size_t *madlen, bool swapmad); +int MADCheck(uint8_t *sector0, uint8_t *sector16, bool verbose, bool *haveMAD2); +int MADDecode(uint8_t *sector0, uint8_t *sector16, uint16_t *mad, size_t *madlen, bool swapmad); int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMAD2); int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose); int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose); diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 18f22af7c..a2a8de47a 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -291,9 +291,16 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh if ((singleSectorParams >> 15) & 1) { if (curr_keys) { - uint64_t foo = bytes_to_num(resp.data.asBytes, 6); + // uint64_t foo = bytes_to_num(resp.data.asBytes, 6); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("Key %s for block %2i found: %012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo); +// PrintAndLogEx(SUCCESS, "found Key %s for block %2i found: " _GREEN_("%012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo); + + PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + singleSectorParams & 0xFF, + ((singleSectorParams >> 8) & 1) ? 'B' : 'A', + sprint_hex_inrow(resp.data.asBytes, MIFARE_KEY_SIZE) + ); + return PM3_SUCCESS; } } @@ -1029,19 +1036,25 @@ int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8 // EMULATOR int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount) { + return mf_eml_get_mem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE); +} - size_t size = blocksCount * MFBLOCK_SIZE; +int mf_eml_get_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { + + size_t size = ((size_t) blocksCount) * blockBtWidth; if (size > PM3_CMD_DATA_SIZE) { return PM3_ESOFT; } struct { - uint8_t blockno; + uint16_t blockno; uint8_t blockcnt; + uint8_t blockwidth; } PACKED payload; payload.blockno = blockNum; payload.blockcnt = blocksCount; + payload.blockwidth = blockBtWidth; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMGET, (uint8_t *)&payload, sizeof(payload)); @@ -1052,8 +1065,9 @@ int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount) { return PM3_ETIMEOUT; } - if (resp.status == PM3_SUCCESS) + if (resp.status == PM3_SUCCESS) { memcpy(data, resp.data.asBytes, size); + } return resp.status; } @@ -1065,7 +1079,7 @@ int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount) { int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { struct p { - uint8_t blockno; + uint16_t blockno; uint8_t blockcnt; uint8_t blockwidth; uint8_t data[]; @@ -1219,7 +1233,7 @@ int mf_chinese_set_block(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t p if (!isOK) { uint8_t reason = (resp.oldarg[1] & 0xFF); - if ( reason == 4) { + if (reason == 4) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(WARNING, "GDM magic write signature block failed"); } else if (reason == 5) { diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index 6dc12da52..e043db324 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -92,6 +92,7 @@ int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, uint8_t int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *sector); int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount); +int mf_eml_get_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount); int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 048a92ec8..2447b924f 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -202,6 +202,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf 15 slixeasenable" }, { 0, "hf 15 slixprivacydisable" }, { 0, "hf 15 slixprivacyenable" }, + { 0, "hf 15 slixprotectpage" }, { 0, "hf 15 passprotectafi" }, { 0, "hf 15 passprotecteas" }, { 0, "hf 15 findafi" }, diff --git a/client/src/scripting.c b/client/src/scripting.c index 4f7705eb3..4bc86fe3d 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -344,12 +344,14 @@ static int l_WaitForResponseTimeout(lua_State *L) { return returnToLuaWithError(L, "You need to supply at least command to wait for"); // extract first param. cmd byte to look for - if (n >= 1) + if (n >= 1) { cmd = (uint32_t)luaL_checkinteger(L, 1); + } // extract second param. timeout value - if (n >= 2) - ms_timeout = luaL_checkinteger(L, 2); + if (n >= 2) { + ms_timeout = (size_t)luaL_checkinteger(L, 2); + } PacketResponseNG resp; if (WaitForResponseTimeout(cmd, &resp, ms_timeout) == false) { @@ -740,8 +742,9 @@ static int l_reveng_models(lua_State *L) { int count = 0; uint8_t in_width = (uint8_t)luaL_checkinteger(L, 1); - if (in_width > 89) + if (in_width > 89) { return returnToLuaWithError(L, "Width cannot exceed 89, got %d", in_width); + } uint8_t width[NMODELS]; memset(width, 0, sizeof(width)); @@ -749,8 +752,9 @@ static int l_reveng_models(lua_State *L) { width[0] = in_width; - if (!GetModels(models, &count, width)) + if (!GetModels(models, &count, width)) { return returnToLuaWithError(L, "didn't find any models"); + } lua_newtable(L); for (int i = 0; i < count; i++) { diff --git a/client/src/util.c b/client/src/util.c index 29ed40eb2..f01067534 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -152,10 +152,10 @@ void FillFileNameByUID(char *filenamePrefix, const uint8_t *uid, const char *ext int len = strlen(filenamePrefix); - for (int j = 0; j < uidlen; j++) { + for (int i = 0; i < uidlen; i++) { // This is technically not the safest option, but there is no way to make this work without changing the function signature // Possibly todo for future PR, but given UID lenghts are defined by program and not variable, should not be an issue - snprintf(filenamePrefix + len + j * 2, 3, "%02X", uid[j]); + snprintf(filenamePrefix + len + i * 2, 3, "%02X", uid[i]); } strcat(filenamePrefix, ext); @@ -196,7 +196,7 @@ bool CheckStringIsHEXValue(const char *value) { } for (size_t i = 0; i < strlen(value); i++) { - if (!isxdigit(value[i])) { + if (isxdigit(value[i]) == 0) { return false; } } @@ -220,11 +220,13 @@ void ascii_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len } size_t m = (min_str_len > i) ? min_str_len : 0; - if (m > hex_max_len) + if (m > hex_max_len) { m = hex_max_len; + } - for (; i < m; i++, tmp++) + for (; i < m; i++, tmp++) { *tmp = ' '; + } // remove last space *tmp = '\0'; @@ -234,8 +236,9 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t min_str_len, const size_t spaces_between, bool uppercase) { // sanity check - if (buf == NULL || hex_len < 1) + if (buf == NULL || hex_len < 1) { return; + } // 1. hex string length. // 2. byte array to be converted to string @@ -252,18 +255,22 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, *(tmp++) = b2s((hex_data[i] >> 4), uppercase); *(tmp++) = b2s(hex_data[i], uppercase); - for (size_t j = 0; j < spaces_between; j++) + for (size_t j = 0; j < spaces_between; j++) { *(tmp++) = ' '; + } } i *= (2 + spaces_between); size_t m = (min_str_len > i) ? min_str_len : 0; - if (m > hex_max_len) - m = hex_max_len; - while (m--) + if (m > hex_max_len) { + m = hex_max_len; + } + + while (m--) { *(tmp++) = ' '; + } // remove last space *tmp = '\0'; @@ -274,9 +281,9 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, void print_hex(const uint8_t *data, const size_t len) { if (data == NULL || len == 0) return; - for (size_t i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) { PrintAndLogEx(NORMAL, "%02x " NOLF, data[i]); - + } PrintAndLogEx(NORMAL, ""); } @@ -621,10 +628,13 @@ char *sprint_breakdown_bin(color_t color, const char *bs, int width, int padn, i } int hex_to_bytes(const char *hexValue, uint8_t *bytesValue, size_t maxBytesValueLen) { + char buf[4] = {0}; int indx = 0; int bytesValueLen = 0; + while (hexValue[indx]) { + if (hexValue[indx] == '\t' || hexValue[indx] == ' ') { indx++; continue; @@ -684,6 +694,7 @@ void bytes_to_bytebits(const void *src, const size_t srclen, void *dest) { uint32_t i = srclen * 8; size_t j = srclen; + while (j--) { uint8_t b = s[j]; d[--i] = (b >> 0) & 1; @@ -740,20 +751,33 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) { *en = 0; // skip spaces - while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + while (line[*bg] == ' ' || line[*bg] == '\t') { + (*bg)++; + } + if (*bg >= len) { return 1; } for (i = 0; i < paramnum; i++) { - while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++; - while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; - if (line[*bg] == '\0') return 1; + while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0') { + (*bg)++; + } + + while (line[*bg] == ' ' || line[*bg] == '\t') { + (*bg)++; + } + + if (line[*bg] == '\0') { + return 1; + } } *en = *bg; - while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++; + while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0') { + (*en)++; + } (*en)--; @@ -763,7 +787,9 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) { int param_getlength(const char *line, int paramnum) { int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) return 0; + if (param_getptr(line, &bg, &en, paramnum)) { + return 0; + } return en - bg + 1; } @@ -775,10 +801,13 @@ char param_getchar(const char *line, int paramnum) { char param_getchar_indx(const char *line, int indx, int paramnum) { int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) return 0x00; + if (param_getptr(line, &bg, &en, paramnum)) { + return 0; + } - if (bg + indx > en) + if (bg + indx > en) { return '\0'; + } return line[bg + indx]; } @@ -795,7 +824,9 @@ uint8_t param_get8(const char *line, int paramnum) { */ uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) { uint8_t val = param_get8ex(line, paramnum, 255, 10); - if ((int8_t) val == -1) return 1; + if ((int8_t) val == -1) { + return 1; + } (*destination) = val; return 0; } @@ -808,49 +839,56 @@ uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) { uint8_t param_isdec(const char *line, int paramnum) { int bg, en; //TODO, check more thorougly - if (!param_getptr(line, &bg, &en, paramnum)) return 1; + if (!param_getptr(line, &bg, &en, paramnum)) { + return 1; + } // return strtoul(&line[bg], NULL, 10) & 0xff; - return 0; } uint8_t param_get8ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoul(&line[bg], NULL, base) & 0xff; - else + } else { return deflt; + } } uint32_t param_get32ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoul(&line[bg], NULL, base); - else + } else { return deflt; + } } uint64_t param_get64ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoull(&line[bg], NULL, base); - else + } else { return deflt; + } } float param_getfloat(const char *line, int paramnum, float deflt) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtof(&line[bg], NULL); - else + } else { return deflt; + } } int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) { int bg, en, i; uint32_t temp; - if (param_getptr(line, &bg, &en, paramnum)) return 1; + if (param_getptr(line, &bg, &en, paramnum)) { + return 1; + } *hexcnt = en - bg + 1; @@ -860,7 +898,9 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) } for (i = 0; i < *hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) return 1; + if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) { + return 1; + } sscanf((char[]) {line[bg + i], line[bg + i + 1], 0}, "%X", &temp); data[i / 2] = temp & 0xff; @@ -873,14 +913,16 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum)) { return 1; + } *datalen = 0; char buf[5] = {0}; int indx = bg; while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { indx++; continue; @@ -910,9 +952,10 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda indx++; } - if (strlen(buf) > 0) + if (strlen(buf) > 0) { //error when not completed hex bytes return 3; + } return 0; } @@ -927,6 +970,7 @@ int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxda char buf[5] = {0}; int indx = bg; while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { indx++; continue; @@ -992,11 +1036,14 @@ int hextobinarray_n(char *target, char *source, int sourcelen) { char *start = source; // process 4 bits (1 hex digit) at a time while (sourcelen--) { + char x = *(source++); + // capitalize if (x >= 'a' && x <= 'f') { x -= 32; } + // convert to numeric value if (x >= '0' && x <= '9') { x -= '0'; @@ -1006,6 +1053,7 @@ int hextobinarray_n(char *target, char *source, int sourcelen) { PrintAndLogEx(INFO, "(hextobinarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start); return 0; } + // output for (i = 0 ; i < 4 ; ++i, ++count) { *(target++) = (x >> (3 - i)) & 1; @@ -1053,15 +1101,20 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz uint32_t t = 0; // written target chars uint32_t r = 0; // consumed bits uint8_t w = 0; // wrong bits separator printed + for (size_t s = 0 ; s < srclen; s++) { + if ((source[s] == 0) || (source[s] == 1)) { w = 0; x += (source[s] << (3 - i)); i++; + if (i == 4) { + if (t >= targetlen - 2) { return r; } + snprintf(target + t, targetlen - t, "%X", x); t++; r += 4; @@ -1069,10 +1122,13 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz i = 0; } } else { + if (i > 0) { + if (t >= targetlen - 5) { return r; } + snprintf(target + t, targetlen - t, "%X[%i]", x, i); t += 4; r += i; @@ -1080,13 +1136,17 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz i = 0; w = 1; } + if (w == 0) { + if (t >= targetlen - 2) { return r; } + snprintf(target + t, targetlen - t, " "); t++; } + r++; } } @@ -1107,9 +1167,9 @@ int binstr_2_binarray(uint8_t *target, char *source, int length) { while (length--) { char x = *(source++); // convert from binary value - if (x >= '0' && x <= '1') + if (x >= '0' && x <= '1') { x -= '0'; - else { + } else { PrintAndLogEx(WARNING, "(binstring2binarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start); return 0; } @@ -1165,8 +1225,9 @@ void hex_xor_token(uint8_t *d, const uint8_t *x, int dn, int xn) { // return parity bit required to match type uint8_t GetParity(const uint8_t *bits, uint8_t type, int length) { int x; - for (x = 0 ; length > 0 ; --length) + for (x = 0 ; length > 0 ; --length) { x += bits[length - 1]; + } x %= 2; return x ^ type; } @@ -1190,24 +1251,30 @@ void wiegand_add_parity_swapped(uint8_t *target, const uint8_t *source, uint8_t // Pack a bitarray into a uint32_t. uint32_t PackBits(uint8_t start, uint8_t len, const uint8_t *bits) { - if (len > 32) return 0; + if (len > 32) { + return 0; + } int i = start; int j = len - 1; uint32_t tmp = 0; - for (; j >= 0; --j, ++i) + for (; j >= 0; --j, ++i) { tmp |= bits[i] << j; + } return tmp; } uint64_t HornerScheme(uint64_t num, uint64_t divider, uint64_t factor) { + uint64_t remaind = 0, quotient = 0, result = 0; remaind = num % divider; quotient = num / divider; - if (!(quotient == 0 && remaind == 0)) + + if (!(quotient == 0 && remaind == 0)) { result += HornerScheme(quotient, divider, factor) * factor + remaind; + } return result; } @@ -1228,15 +1295,17 @@ int detect_num_CPUs(void) { return sysinfo.dwNumberOfProcessors; #else int count = sysconf(_SC_NPROCESSORS_ONLN); - if (count <= 0) + if (count <= 0) { count = 1; + } return count; #endif } void str_lower(char *s) { - for (size_t i = 0; i < strlen(s); i++) + for (size_t i = 0; i < strlen(s); i++) { s[i] = tolower(s[i]); + } } void str_upper(char *s) { @@ -1244,8 +1313,9 @@ void str_upper(char *s) { } void strn_upper(char *s, size_t n) { - for (size_t i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) { s[i] = toupper(s[i]); + } } // check for prefix in string bool str_startswith(const char *s, const char *pre) { @@ -1265,8 +1335,9 @@ bool str_endswith(const char *s, const char *suffix) { // Replace unprintable characters with a dot in char buffer void clean_ascii(unsigned char *buf, size_t len) { for (size_t i = 0; i < len; i++) { - if (!isprint(buf[i])) + if (isprint(buf[i]) == 0) { buf[i] = '.'; + } } } @@ -1279,12 +1350,12 @@ void str_cleanrn(char *buf, size_t len) { // replace char in buffer void str_creplace(char *buf, size_t len, char from, char to) { for (size_t i = 0; i < len; i++) { - if (buf[i] == from) + if (buf[i] == from) { buf[i] = to; + } } } - char *str_dup(const char *src) { return str_ndup(src, strlen(src)); } @@ -1365,8 +1436,9 @@ int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) for (;;) { int res = sscanf(&str[i], "%1u", &n); - if ((res != 1) || (n > 1)) + if ((res != 1) || (n > 1)) { break; + } *hi2 = (*hi2 << 1) | (*hi >> 31); *hi = (*hi << 1) | (*lo >> 31); @@ -1388,8 +1460,9 @@ int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const uint8_t *ar int i = 0; for (; i < arrlen; i++) { uint8_t n = arr[i]; - if (n > 1) + if (n > 1) { break; + } *hi2 = (*hi2 << 1) | (*hi >> 31); *hi = (*hi << 1) | (*lo >> 31); @@ -1445,17 +1518,20 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_ for (size_t i = 0; i < max; i++) { // compare only first byte - if (src[i] != pattern[0]) + if (src[i] != pattern[0]) { continue; + } // try to match rest of the pattern for (int j = plen - 1; j >= 1; j--) { - if (src[i + j] != pattern[j]) + if (src[i + j] != pattern[j]) { break; + } - if (j == 1) + if (j == 1) { return i; + } } } return -1; @@ -1467,17 +1543,20 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_ int byte_strrstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen) { for (int i = srclen - plen; i >= 0; i--) { // compare only first byte - if (src[i] != pattern[0]) + if (src[i] != pattern[0]) { continue; + } // try to match rest of the pattern for (int j = plen - 1; j >= 1; j--) { - if (src[i + j] != pattern[j]) + if (src[i + j] != pattern[j]) { break; + } - if (j == 1) + if (j == 1) { return i; + } } } return -1; diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index e3e146153..d41ceaabc 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -19,13 +19,23 @@ #include #include "commonutil.h" +static bool step_parity_check(wiegand_message_t *packed, int start, int length, bool even_parity) { + bool parity = even_parity; + for (int i = start; i < start + length; i += 2) { + // Extract 2 bits + bool bit1 = get_bit_by_position(packed, i); + bool bit2 = get_bit_by_position(packed, i + 1); -static bool Pack_Defcon32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + // Calculate parity for these 2 bits + parity ^= (bit1 ^ bit2); + } + return parity; +} + +static bool Pack_Defcon32(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x00FFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x0fffff) return false; // Can't encode CN. - if (card->IssueLevel > 0x00000F) return false; // Can't encode Issue - if (card->OEM > 0) return false; // Not used in this format + + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 42; /* @@ -66,10 +76,10 @@ static bool Pack_Defcon32(wiegand_card_t *card, wiegand_message_t *packed, bool } static bool Unpack_Defcon32(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 42) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 16); card->IssueLevel = get_linear_field(packed, 17, 4); card->CardNumber = get_linear_field(packed, 21, 20); @@ -82,14 +92,10 @@ static bool Unpack_Defcon32(wiegand_message_t *packed, wiegand_card_t *card) { return true; } - -static bool Pack_H10301(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10301(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 26; // Set number of bits packed->Bot |= (card->CardNumber & 0xFFFF) << 1; @@ -102,9 +108,10 @@ static bool Pack_H10301(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10301(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 26) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = (packed->Bot >> 1) & 0xFFFF; card->FacilityCode = (packed->Bot >> 17) & 0xFF; card->ParityValid = @@ -113,14 +120,11 @@ static bool Unpack_H10301(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_ind26(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ind26(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // 12 bits - if (card->CardNumber > 0xFFF) return false; // 12 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 26; // Set number of bits @@ -140,10 +144,10 @@ static bool Pack_ind26(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_ind26(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 26) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 12); card->CardNumber = get_linear_field(packed, 13, 12); @@ -153,13 +157,10 @@ static bool Unpack_ind26(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_Tecom27(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Tecom27(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x7FF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 27; set_nonlinear_field(packed, card->FacilityCode, 11, (uint8_t[]) {15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); @@ -170,23 +171,21 @@ static bool Pack_Tecom27(wiegand_card_t *card, wiegand_message_t *packed, bool p } static bool Unpack_Tecom27(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 27) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_nonlinear_field(packed, 16, (uint8_t[]) {0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); card->FacilityCode = get_nonlinear_field(packed, 11, (uint8_t[]) {15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); return true; } -static bool Pack_ind27(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ind27(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x1FFF) return false; // 13 bits - if (card->CardNumber > 0x3FFF) return false; // 14 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // 4 bit + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 27; // Set number of bits @@ -199,22 +198,19 @@ static bool Pack_ind27(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_ind27(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 27) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 0, 13); card->CardNumber = get_linear_field(packed, 13, 14); return true; } -static bool Pack_indasc27(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_indasc27(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x1FFF) return false; // 13 bits - if (card->CardNumber > 0x3FFF) return false; // 14 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 27; set_nonlinear_field(packed, card->FacilityCode, 13, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22}); @@ -225,22 +221,19 @@ static bool Pack_indasc27(wiegand_card_t *card, wiegand_message_t *packed, bool } static bool Unpack_indasc27(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 27) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_nonlinear_field(packed, 13, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22}); card->CardNumber = get_nonlinear_field(packed, 14, (uint8_t[]) {26, 1, 3, 15, 14, 17, 20, 13, 25, 2, 18, 21, 11, 23}); return true; } -static bool Pack_2804W(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_2804W(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0FF) return false; // Can't encode FC. - if (card->CardNumber > 0x7FFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 28; set_linear_field(packed, card->FacilityCode, 4, 8); @@ -260,10 +253,11 @@ static bool Pack_2804W(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_2804W(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 28) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 4, 8); card->CardNumber = get_linear_field(packed, 12, 15); card->ParityValid = @@ -273,14 +267,11 @@ static bool Unpack_2804W(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_ind29(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ind29(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x1FFF) return false; // 13 bits - if (card->CardNumber > 0xFFFF) return false; // 16 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // 4 bit + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 29; // Set number of bits @@ -293,22 +284,19 @@ static bool Pack_ind29(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_ind29(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 29) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 0, 13); card->CardNumber = get_linear_field(packed, 13, 16); return true; } -static bool Pack_ATSW30(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ATSW30(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 30; set_linear_field(packed, card->FacilityCode, 1, 12); @@ -325,10 +313,10 @@ static bool Pack_ATSW30(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_ATSW30(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 30) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 12); card->CardNumber = get_linear_field(packed, 13, 16); card->ParityValid = @@ -337,13 +325,10 @@ static bool Unpack_ATSW30(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_ADT31(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_ADT31(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0F) return false; // Can't encode FC. - if (card->CardNumber > 0x7FFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 31; set_linear_field(packed, card->FacilityCode, 1, 4); @@ -355,22 +340,21 @@ static bool Pack_ADT31(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_ADT31(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 31) return false; // Wrong length? Stop here. + + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 4); card->CardNumber = get_linear_field(packed, 5, 23); return true; } -static bool Pack_hcp32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_hcp32(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Not used - if (card->CardNumber > 0x3FFF) return false; // 24 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; // Set number of bits @@ -382,22 +366,20 @@ static bool Pack_hcp32(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_hcp32(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 32) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 1, 24); return true; } -static bool Pack_hpp32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_hpp32(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // 12 bits - if (card->CardNumber > 0x1FFFFFFF) return false; // 29 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; // Set number of bits @@ -410,23 +392,21 @@ static bool Pack_hpp32(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_hpp32(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 32) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 12); card->CardNumber = get_linear_field(packed, 13, 29); return true; } -static bool Pack_wie32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_wie32(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // 12 bits - if (card->CardNumber > 0xFFFF) return false; // 16 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; // Set number of bits @@ -439,22 +419,20 @@ static bool Pack_wie32(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_wie32(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 32) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 4, 12); card->CardNumber = get_linear_field(packed, 16, 16); return true; } -static bool Pack_Kastle(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Kastle(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x00FF) return false; // Can't encode FC. - if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0x001F) return false; // IL is only 5 bits. - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; // Set number of bits set_bit_by_position(packed, 1, 1); // Always 1 @@ -469,11 +447,12 @@ static bool Pack_Kastle(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_Kastle(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 32) return false; // Wrong length? Stop here. if (get_bit_by_position(packed, 1) != 1) return false; // Always 1 in this format + memset(card, 0, sizeof(wiegand_card_t)); + card->IssueLevel = get_linear_field(packed, 2, 5); card->FacilityCode = get_linear_field(packed, 7, 8); card->CardNumber = get_linear_field(packed, 15, 16); @@ -483,13 +462,10 @@ static bool Unpack_Kastle(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_Kantech(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Kantech(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 32; set_linear_field(packed, card->FacilityCode, 7, 8); @@ -500,21 +476,19 @@ static bool Pack_Kantech(wiegand_card_t *card, wiegand_message_t *packed, bool p } static bool Unpack_Kantech(wiegand_message_t *packed, wiegand_card_t *card) { + if (packed->Length != 32) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 32) return false; // Wrong length? Stop here. card->FacilityCode = get_linear_field(packed, 7, 8); card->CardNumber = get_linear_field(packed, 15, 16); return true; } -static bool Pack_D10202(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_D10202(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x007F) return false; // Can't encode FC. - if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 33; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 7); @@ -527,10 +501,11 @@ static bool Pack_D10202(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_D10202(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 33) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 8, 24); card->FacilityCode = get_linear_field(packed, 1, 7); card->ParityValid = @@ -539,13 +514,10 @@ static bool Unpack_D10202(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_H10306(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10306(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 34; // Set number of bits packed->Bot |= (card->CardNumber & 0xFFFF) << 1; @@ -559,10 +531,11 @@ static bool Pack_H10306(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10306(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 16); card->CardNumber = get_linear_field(packed, 17, 16); @@ -573,13 +546,10 @@ static bool Unpack_H10306(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_N10002(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_N10002(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 34; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 16); @@ -598,10 +568,11 @@ static bool Pack_N10002(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_N10002(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 16); card->CardNumber = get_linear_field(packed, 17, 16); @@ -612,13 +583,10 @@ static bool Unpack_N10002(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_C1k35s(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_C1k35s(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 35; // Set number of bits packed->Bot |= (card->CardNumber & 0x000FFFFF) << 1; @@ -633,10 +601,11 @@ static bool Pack_C1k35s(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_C1k35s(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 35) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = (packed->Bot >> 1) & 0x000FFFFF; card->FacilityCode = ((packed->Mid & 1) << 11) | ((packed->Bot >> 21)); card->ParityValid = @@ -646,13 +615,10 @@ static bool Unpack_C1k35s(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_H10320(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10320(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) - if (card->CardNumber > 99999999) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -681,13 +647,14 @@ static bool Pack_H10320(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10320(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. if (get_bit_by_position(packed, 0) != 1) { return false; } + memset(card, 0, sizeof(wiegand_card_t)); + // This card is BCD-encoded rather than binary. Get the 4-bit groups independently. for (uint32_t idx = 0; idx < 8; idx++) { uint64_t val = get_linear_field(packed, idx * 4, 4); @@ -707,13 +674,10 @@ static bool Unpack_H10320(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_S12906(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_S12906(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFF) return false; // Can't encode FC. - if (card->IssueLevel > 0x03) return false; // Can't encode IL. - if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 36; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 8); @@ -727,10 +691,10 @@ static bool Pack_S12906(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_S12906(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 36) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 8); card->IssueLevel = get_linear_field(packed, 9, 2); card->CardNumber = get_linear_field(packed, 11, 24); @@ -740,13 +704,10 @@ static bool Unpack_S12906(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_Sie36(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Sie36(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0003FFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 36; // Set number of bits set_linear_field(packed, card->FacilityCode, 1, 18); @@ -763,10 +724,11 @@ static bool Pack_Sie36(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_Sie36(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 36) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 18); card->CardNumber = get_linear_field(packed, 19, 16); card->ParityValid = @@ -775,13 +737,10 @@ static bool Unpack_Sie36(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_C15001(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_C15001(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x000000FF) return false; // Can't encode FC. - if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0x000003FF) return false; // Can't encode OEM. + if (!validate_card_limit(format_idx, card)) return false; if (card->OEM == 0) card->OEM = 900; @@ -798,12 +757,11 @@ static bool Pack_C15001(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_C15001(wiegand_message_t *packed, wiegand_card_t *card) { + + if (packed->Length != 36) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); - - if (packed->Length != 36) - return false; // Wrong length? Stop here. - card->OEM = get_linear_field(packed, 1, 10); card->FacilityCode = get_linear_field(packed, 11, 8); card->CardNumber = get_linear_field(packed, 19, 16); @@ -813,13 +771,10 @@ static bool Unpack_C15001(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_H10302(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10302(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) - if (card->CardNumber > 0x00000007FFFFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits set_linear_field(packed, card->CardNumber, 1, 35); @@ -831,10 +786,10 @@ static bool Pack_H10302(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10302(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 37) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 1, 35); card->ParityValid = (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 18))) && @@ -842,13 +797,10 @@ static bool Unpack_H10302(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_P10004(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_P10004(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x00001FFF) return false; // Can't encode FC. - if (card->CardNumber > 0x0003FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -861,23 +813,21 @@ static bool Pack_P10004(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_P10004(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 13); card->CardNumber = get_linear_field(packed, 14, 18); // unknown parity scheme return true; } -static bool Pack_H10304(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H10304(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0000FFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x0007FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -892,10 +842,11 @@ static bool Pack_H10304(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_H10304(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 16); card->CardNumber = get_linear_field(packed, 17, 19); card->ParityValid = @@ -904,13 +855,10 @@ static bool Unpack_H10304(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_HGeneric37(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_HGeneric37(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Not used in this format - if (card->CardNumber > 0x0007FFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -942,11 +890,12 @@ static bool Pack_HGeneric37(wiegand_card_t *card, wiegand_message_t *packed, boo } static bool Unpack_HGeneric37(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 37) return false; // Wrong length? Stop here. if (get_bit_by_position(packed, 36) != 1) return false; // Always 1 in this format + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 4, 32); card->ParityValid = (get_bit_by_position(packed, 0) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]) {4, 8, 12, 16, 20, 24, 28, 32}))) && @@ -956,13 +905,51 @@ static bool Unpack_HGeneric37(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_MDI37(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_H800002(int format_idx, wiegand_card_t *card, + wiegand_message_t *packed, bool preamble) { + int even_parity = 0; memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x0000F) return false; // Can't encode FC. - if (card->CardNumber > 0x1FFFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) { + return false; + } + + packed->Length = 46; + set_linear_field(packed, card->FacilityCode, 1, 14); + set_linear_field(packed, card->CardNumber, 15, 30); + + // Parity over 44 bits + even_parity = evenparity32((packed->Bot >> 1) ^ (packed->Mid & 0x1fff)); + set_bit_by_position(packed, even_parity, 0); + // Invert parity for setting odd parity + set_bit_by_position(packed, even_parity ^ 1, 45); + if (preamble) { + return add_HID_header(packed); + } + return true; +} + +static bool Unpack_H800002(wiegand_message_t *packed, wiegand_card_t *card) { + if (packed->Length != 46) { + return false; // Wrong length? Stop here. + } + + int even_parity = 0; + memset(card, 0, sizeof(wiegand_card_t)); + + card->FacilityCode = get_linear_field(packed, 1, 14); + card->CardNumber = get_linear_field(packed, 15, 30); + even_parity = evenparity32((packed->Bot >> 1) ^ (packed->Mid & 0x1fff)); + card->ParityValid = get_bit_by_position(packed, 0) == even_parity; + // Invert logic to compare against oddparity + card->ParityValid &= get_bit_by_position(packed, 45) != even_parity; + return true; +} + +static bool Pack_MDI37(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + memset(packed, 0, sizeof(wiegand_message_t)); + + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 37; // Set number of bits @@ -977,10 +964,10 @@ static bool Pack_MDI37(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_MDI37(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 37) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 3, 4);; card->CardNumber = get_linear_field(packed, 7, 29); @@ -991,14 +978,11 @@ static bool Unpack_MDI37(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_P10001(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_P10001(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 40; // Set number of bits set_linear_field(packed, 0xF, 0, 4); @@ -1017,10 +1001,10 @@ static bool Pack_P10001(wiegand_card_t *card, wiegand_message_t *packed, bool pr static bool Unpack_P10001(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 40) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 16, 16); card->FacilityCode = get_linear_field(packed, 4, 12); card->ParityValid = ( @@ -1032,14 +1016,11 @@ static bool Unpack_P10001(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_C1k48s(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_C1k48s(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x003FFFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x007FFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 48; // Set number of bits packed->Bot |= (card->CardNumber & 0x007FFFFF) << 1; @@ -1056,10 +1037,10 @@ static bool Pack_C1k48s(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_C1k48s(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 48) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = (packed->Bot >> 1) & 0x007FFFFF; card->FacilityCode = ((packed->Mid & 0x00003FFF) << 8) | ((packed->Bot >> 24)); card->ParityValid = @@ -1069,14 +1050,11 @@ static bool Unpack_C1k48s(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_CasiRusco40(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_CasiRusco40(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFFFFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 40; // Set number of bits set_linear_field(packed, card->CardNumber, 1, 38); @@ -1087,23 +1065,21 @@ static bool Pack_CasiRusco40(wiegand_card_t *card, wiegand_message_t *packed, bo } static bool Unpack_CasiRusco40(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 40) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 1, 38); return true; } -static bool Pack_Optus(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Optus(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + + if (!validate_card_limit(format_idx, card)) return false; memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x3FF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format - packed->Length = 34; // Set number of bits set_linear_field(packed, card->CardNumber, 1, 16); set_linear_field(packed, card->FacilityCode, 22, 11); @@ -1114,23 +1090,21 @@ static bool Pack_Optus(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_Optus(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->CardNumber = get_linear_field(packed, 1, 16); card->FacilityCode = get_linear_field(packed, 22, 11); return true; } -static bool Pack_Smartpass(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Smartpass(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x3FF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0x7) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 34; // Set number of bits @@ -1143,24 +1117,22 @@ static bool Pack_Smartpass(wiegand_card_t *card, wiegand_message_t *packed, bool } static bool Unpack_Smartpass(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 13); card->IssueLevel = get_linear_field(packed, 14, 3); card->CardNumber = get_linear_field(packed, 17, 16); return true; } -static bool Pack_bqt34(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_bqt34(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 34; // Set number of bits @@ -1180,10 +1152,11 @@ static bool Pack_bqt34(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_bqt34(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 34) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 8); card->CardNumber = get_linear_field(packed, 9, 24); @@ -1193,14 +1166,11 @@ static bool Unpack_bqt34(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_bqt38(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_bqt38(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // 12 bits - if (card->CardNumber > 0x3FFFF) return false; // 19 bits - if (card->IssueLevel > 0x7) return false; // 4 bit - if (card->OEM > 0) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 38; // Set number of bits @@ -1221,10 +1191,11 @@ static bool Pack_bqt38(wiegand_card_t *card, wiegand_message_t *packed, bool pre } static bool Unpack_bqt38(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 38) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 24, 13); card->CardNumber = get_linear_field(packed, 1, 19); card->IssueLevel = get_linear_field(packed, 20, 4); @@ -1235,14 +1206,11 @@ static bool Unpack_bqt38(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_iscs38(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_iscs38(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0x3FF) return false; // 12 bits - if (card->CardNumber > 0xFFFFFF) return false; // 19 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0x7) return false; // 4 bit + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 38; // Set number of bits @@ -1263,10 +1231,11 @@ static bool Pack_iscs38(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_iscs38(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 38) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 5, 10); card->CardNumber = get_linear_field(packed, 15, 22); card->OEM = get_linear_field(packed, 1, 4); @@ -1277,14 +1246,11 @@ static bool Unpack_iscs38(wiegand_message_t *packed, wiegand_card_t *card) { return true; } -static bool Pack_pw39(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_pw39(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFFF) return false; // 12 bits - if (card->CardNumber > 0xFFFFF) return false; // 19 bits - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0) return false; // 4 bit + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 39; // Set number of bits @@ -1304,10 +1270,11 @@ static bool Pack_pw39(wiegand_card_t *card, wiegand_message_t *packed, bool prea } static bool Unpack_pw39(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 39) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 17); card->CardNumber = get_linear_field(packed, 18, 20); @@ -1317,15 +1284,11 @@ static bool Unpack_pw39(wiegand_message_t *packed, wiegand_card_t *card) { return true; } - -static bool Pack_bc40(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_bc40(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); - if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. - if (card->CardNumber > 0xFFFFF) return false; // Can't encode CN. - if (card->IssueLevel > 0) return false; // Not used in this format - if (card->OEM > 0x7F) return false; // Not used in this format + if (!validate_card_limit(format_idx, card)) return false; packed->Length = 40; // Set number of bits @@ -1345,10 +1308,12 @@ static bool Pack_bc40(wiegand_card_t *card, wiegand_message_t *packed, bool prea } static bool Unpack_bc40(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 40) return false; // Wrong length? Stop here. + memset(card, 0, sizeof(wiegand_card_t)); + + card->OEM = get_linear_field(packed, 0, 7); card->FacilityCode = get_linear_field(packed, 7, 12); card->CardNumber = get_linear_field(packed, 19, 19); @@ -1358,26 +1323,11 @@ static bool Unpack_bc40(wiegand_message_t *packed, wiegand_card_t *card) { return true; } - -static bool step_parity_check(wiegand_message_t *packed, int start, int length, bool even_parity) { - bool parity = even_parity; - for (int i = start; i < start + length; i += 2) { - // Extract 2 bits - bool bit1 = get_bit_by_position(packed, i); - bool bit2 = get_bit_by_position(packed, i + 1); - - // Calculate parity for these 2 bits - parity ^= (bit1 ^ bit2); - } - return parity; -} - -static bool Pack_Avig56(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { +static bool Pack_Avig56(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); packed->Length = 56; - if (card->FacilityCode > 0xFFFFF) return false; // Can't encode FC. - if (card->CardNumber > 0x3FFFFFFFF) return false; // Can't encode CN. + if (!validate_card_limit(format_idx, card)) return false; set_linear_field(packed, card->FacilityCode, 1, 20); set_linear_field(packed, card->CardNumber, 21, 34); @@ -1395,10 +1345,11 @@ static bool Pack_Avig56(wiegand_card_t *card, wiegand_message_t *packed, bool pr } static bool Unpack_Avig56(wiegand_message_t *packed, wiegand_card_t *card) { - memset(card, 0, sizeof(wiegand_card_t)); if (packed->Length != 56) return false; + memset(card, 0, sizeof(wiegand_card_t)); + card->FacilityCode = get_linear_field(packed, 1, 20); card->CardNumber = get_linear_field(packed, 21, 34); @@ -1410,6 +1361,30 @@ static bool Unpack_Avig56(wiegand_message_t *packed, wiegand_card_t *card) { return true; } +static bool Pack_IR56(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + memset(packed, 0, sizeof(wiegand_message_t)); + packed->Length = 56; + + if (!validate_card_limit(format_idx, card)) return false; + + packed->Bot = card->CardNumber; + packed->Mid = card->FacilityCode; + + if (preamble) + return add_HID_header(packed); + + return true; +} + +static bool Unpack_IR56(wiegand_message_t *packed, wiegand_card_t *card) { + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 56) return false; + + card->FacilityCode = packed->Mid; + card->CardNumber = packed->Bot; + return true; +} // --------------------------------------------------------------------------------------------------- void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed) { @@ -1421,7 +1396,7 @@ void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed) { size_t s_len = 128; char *s = calloc(s_len, sizeof(uint8_t)); - snprintf(s, s_len * sizeof(uint8_t), _YELLOW_("%-10s")" %-32s", fmt->Name, fmt->Descrp); + snprintf(s, s_len * sizeof(uint8_t), _YELLOW_("%-10s")" %-32s", fmt->Name, fmt->Description); if (packed->Top != 0) { PrintAndLogEx(SUCCESS, "%s -> " _GREEN_("%X%08X%08X"), @@ -1460,25 +1435,6 @@ void print_wiegand_code(wiegand_message_t *packed) { static void hid_print_card(wiegand_card_t *card, const cardformat_t format) { - /* - PrintAndLogEx(SUCCESS, " Format: %s (%s)", format.Name, format.Descrp); - - if (format.Fields.hasFacilityCode) - PrintAndLogEx(SUCCESS, "Facility Code: %d",card->FacilityCode); - - if (format.Fields.hasCardNumber) - PrintAndLogEx(SUCCESS, " Card Number: %d",card->CardNumber); - - if (format.Fields.hasIssueLevel) - PrintAndLogEx(SUCCESS, " Issue Level: %d",card->IssueLevel); - - if (format.Fields.hasOEMCode) - PrintAndLogEx(SUCCESS, " OEM Code: %d",card->OEM); - - if (format.Fields.hasParity) - PrintAndLogEx(SUCCESS, " Parity: %s",card->ParityValid ? "Valid" : "Invalid"); - */ - char s[110] = {0}; if (format.Fields.hasFacilityCode) snprintf(s, sizeof(s), "FC: " _GREEN_("%u"), card->FacilityCode); @@ -1495,50 +1451,52 @@ static void hid_print_card(wiegand_card_t *card, const cardformat_t format) { if (format.Fields.hasParity) snprintf(s + strlen(s), sizeof(s) - strlen(s), " parity ( %s )", card->ParityValid ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(SUCCESS, "[%-8s] %-32s %s", format.Name, format.Descrp, s); + PrintAndLogEx(SUCCESS, "[%-8s] %-32s %s", format.Name, format.Description, s); } static const cardformat_t FormatTable[] = { - {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack - {"ind26", Pack_ind26, Unpack_ind26, "Indala 26-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"ind27", Pack_ind27, Unpack_ind27, "Indala 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"indasc27", Pack_indasc27, Unpack_indasc27, "Indala ASC 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand 28-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"ind29", Pack_ind29, Unpack_ind29, "Indala 29-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"HCP32", Pack_hcp32, Unpack_hcp32, "HID Check Point 32-bit", {1, 0, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"HPP32", Pack_hpp32, Unpack_hpp32, "HID Hewlett-Packard 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"Kastle", Pack_Kastle, Unpack_Kastle, "Kastle 32-bit", {1, 1, 1, 0, 1}}, // from @xilni; PR #23 on RfidResearchGroup/proxmark3 - {"Kantech", Pack_Kantech, Unpack_Kantech, "Indala/Kantech KFS 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"WIE32", Pack_wie32, Unpack_wie32, "Wiegand 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack - {"N10002", Pack_N10002, Unpack_N10002, "Honeywell/Northern N10002 34-bit", {1, 1, 0, 0, 1}}, // from proxclone.com - {"Optus34", Pack_Optus, Unpack_Optus, "Indala Optus 34-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"SMP34", Pack_Smartpass, Unpack_Smartpass, "Cardkey Smartpass 34-bit", {1, 1, 1, 0, 0}}, // from cardinfo.barkweb.com.au - {"BQT34", Pack_bqt34, Unpack_bqt34, "BQT 34-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack - {"C15001", Pack_C15001, Unpack_C15001, "HID KeyScan 36-bit", {1, 1, 0, 1, 1}}, // from Proxmark forums - {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au - {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 37-bit BCD", {1, 0, 0, 0, 1}}, // from Proxmark forums - {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", {1, 0, 0, 0, 1}}, // from Proxmark forums - {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"P10004", Pack_P10004, Unpack_P10004, "HID P10004 37-bit PCSC", {1, 1, 0, 0, 0}}, // from @bthedorff; PR #1559 - {"HGen37", Pack_HGeneric37, Unpack_HGeneric37, "HID Generic 37-bit", {1, 0, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"MDI37", Pack_MDI37, Unpack_MDI37, "PointGuard MDI 37-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"BQT38", Pack_bqt38, Unpack_bqt38, "BQT 38-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au - {"ISCS", Pack_iscs38, Unpack_iscs38, "ISCS 38-bit", {1, 1, 0, 1, 1}}, // from cardinfo.barkweb.com.au - {"PW39", Pack_pw39, Unpack_pw39, "Pyramid 39-bit wiegand format", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au - {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"Casi40", Pack_CasiRusco40, Unpack_CasiRusco40, "Casi-Rusco 40-bit", {1, 0, 0, 0, 0}}, // from cardinfo.barkweb.com.au - {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack - {"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", {1, 1, 0, 1, 1}}, // from - {"Avig56", Pack_Avig56, Unpack_Avig56, "Avigilon 56-bit", {1, 1, 0, 0, 1}}, - {"Defcon32", Pack_Defcon32, Unpack_Defcon32, "Custom Defcon RFCTF 42 BIT format", {1, 1, 1, 0, 1}}, // Created by (@micsen) for the CTF - {NULL, NULL, NULL, NULL, {0, 0, 0, 0, 0}} // Must null terminate array + {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit", 26, {1, 1, 0, 0, 1, 0xFF, 0xFFFF, 0, 0}}, // imported from old pack/unpack + {"ind26", Pack_ind26, Unpack_ind26, "Indala 26-bit", 26, {1, 1, 0, 0, 1, 0xFFF, 0xFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"ind27", Pack_ind27, Unpack_ind27, "Indala 27-bit", 27, {1, 1, 0, 0, 0, 0x1FFF, 0x3FFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"indasc27", Pack_indasc27, Unpack_indasc27, "Indala ASC 27-bit", 27, {1, 1, 0, 0, 0, 0x1FFF, 0x3FFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", 27, {1, 1, 0, 0, 0, 0x7FF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand 28-bit", 28, {1, 1, 0, 0, 1, 0xFF, 0x7FFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"ind29", Pack_ind29, Unpack_ind29, "Indala 29-bit", 29, {1, 1, 0, 0, 0, 0x1FFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", 30, {1, 1, 0, 0, 1, 0xFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", 31, {1, 1, 0, 0, 0, 0xF, 0x7FFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"HCP32", Pack_hcp32, Unpack_hcp32, "HID Check Point 32-bit", 32, {1, 0, 0, 0, 0, 0, 0x3FFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"HPP32", Pack_hpp32, Unpack_hpp32, "HID Hewlett-Packard 32-bit", 32, {1, 1, 0, 0, 0, 0xFFF, 0x1FFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"Kastle", Pack_Kastle, Unpack_Kastle, "Kastle 32-bit", 32, {1, 1, 1, 0, 1, 0xFF, 0xFFFF, 0x1F, 0}}, // from @xilni; PR #23 on RfidResearchGroup/proxmark3 + {"Kantech", Pack_Kantech, Unpack_Kantech, "Indala/Kantech KFS 32-bit", 32, {1, 1, 0, 0, 0, 0xFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"WIE32", Pack_wie32, Unpack_wie32, "Wiegand 32-bit", 32, {1, 1, 0, 0, 0, 0xFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit", 33, {1, 1, 0, 0, 1, 0x7F, 0xFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit", 34, {1, 1, 0, 0, 1, 0xFFFF, 0xFFFF, 0, 0}}, // imported from old pack/unpack + {"N10002", Pack_N10002, Unpack_N10002, "Honeywell/Northern N10002 34-bit", 34, {1, 1, 0, 0, 1, 0xFFFF, 0xFFFF, 0, 0}}, // from proxclone.com + {"Optus34", Pack_Optus, Unpack_Optus, "Indala Optus 34-bit", 34, {1, 1, 0, 0, 0, 0x3FF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"SMP34", Pack_Smartpass, Unpack_Smartpass, "Cardkey Smartpass 34-bit", 34, {1, 1, 1, 0, 0, 0x3FF, 0xFFFF, 0x7, 0}}, // from cardinfo.barkweb.com.au + {"BQT34", Pack_bqt34, Unpack_bqt34, "BQT 34-bit", 34, {1, 1, 0, 0, 1, 0xFF, 0xFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit std", 35, {1, 1, 0, 0, 1, 0xFFF, 0xFFFFF, 0, 0}}, // imported from old pack/unpack + {"C15001", Pack_C15001, Unpack_C15001, "HID KeyScan 36-bit", 36, {1, 1, 0, 1, 1, 0xFF, 0xFFFF, 0, 0x3FF}}, // from Proxmark forums + {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", 36, {1, 1, 1, 0, 1, 0xFF, 0x3, 0xFFFFFF, 0}}, // from cardinfo.barkweb.com.au + {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", 36, {1, 1, 0, 0, 1, 0x3FFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 37-bit BCD", 37, {1, 0, 0, 0, 1, 0, 99999999, 0, 0}}, // from Proxmark forums + {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", 37, {1, 0, 0, 0, 1, 0, 0x7FFFFFFFF, 0, 0}}, // from Proxmark forums + {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit", 37, {1, 1, 0, 0, 1, 0xFFFF, 0x7FFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"P10004", Pack_P10004, Unpack_P10004, "HID P10004 37-bit PCSC", 37, {1, 1, 0, 0, 0, 0x1FFF, 0x3FFFF, 0, 0}}, // from @bthedorff; PR #1559 + {"HGen37", Pack_HGeneric37, Unpack_HGeneric37, "HID Generic 37-bit", 37, {1, 0, 0, 0, 1, 0, 0x7FFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"MDI37", Pack_MDI37, Unpack_MDI37, "PointGuard MDI 37-bit", 37, {1, 1, 0, 0, 1, 0xF, 0x1FFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"BQT38", Pack_bqt38, Unpack_bqt38, "BQT 38-bit", 38, {1, 1, 1, 0, 1, 0xFFF, 0x3FFFF, 0x7, 0}}, // from cardinfo.barkweb.com.au + {"ISCS", Pack_iscs38, Unpack_iscs38, "ISCS 38-bit", 38, {1, 1, 0, 1, 1, 0x3FF, 0xFFFFFF, 0, 0x7}}, // from cardinfo.barkweb.com.au + {"PW39", Pack_pw39, Unpack_pw39, "Pyramid 39-bit wiegand format", 39, {1, 1, 0, 0, 1, 0xFFFF, 0xFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit", 40, {1, 1, 0, 0, 0, 0xFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"Casi40", Pack_CasiRusco40, Unpack_CasiRusco40, "Casi-Rusco 40-bit", 40, {1, 0, 0, 0, 0, 0, 0xFFFFFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au + {"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", 40, {1, 1, 0, 1, 1, 0xFFF, 0xFFFFF, 0, 0x7F}}, // from + {"Defcon32", Pack_Defcon32, Unpack_Defcon32, "Custom Defcon RFCTF 42-bit", 42, {1, 1, 1, 0, 1, 0xFFFF, 0xFFFFF, 0xF, 0}}, // Created by (@micsen) for the CTF + {"H800002", Pack_H800002, Unpack_H800002, "HID H800002 46-bit", 46, {1, 1, 0, 0, 1, 0x3FFF, 0x3FFFFFFF, 0, 0}}, + {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", 48, {1, 1, 0, 0, 1, 0x003FFFFF, 0x007FFFFF, 0, 0}}, // imported from old pack/unpack + {"Avig56", Pack_Avig56, Unpack_Avig56, "Avigilon 56-bit", 56, {1, 1, 0, 0, 1, 0xFFFFF, 0x3FFFFFFFF, 0, 0}}, + {"IR56", Pack_IR56, Unpack_IR56, "Inner Range 56-bit", 56, {1, 1, 0, 0, 0, 0xFFFFFF, 0xFFFFFFFF, 0, 0}}, + {NULL, NULL, NULL, NULL, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}} // Must null terminate array }; void HIDListFormats(void) { @@ -1551,7 +1509,7 @@ void HIDListFormats(void) { int i = 0; while (FormatTable[i].Name) { - PrintAndLogEx(INFO, _YELLOW_("%-10s")" %-30s", FormatTable[i].Name, FormatTable[i].Descrp); + PrintAndLogEx(INFO, _YELLOW_("%-10s")" %-30s", FormatTable[i].Name, FormatTable[i].Description); ++i; } PrintAndLogEx(INFO, "------------------------------------------------------------"); @@ -1594,13 +1552,23 @@ int HIDFindCardFormat(const char *format) { return -1; } +// validate if the card's FC, CN, IL, OEM are within the limit of its format +// return true if the card is valid +bool validate_card_limit(int format_idx, wiegand_card_t *card) { + cardformatdescriptor_t card_descriptor = FormatTable[format_idx].Fields; + return !((card->FacilityCode > card_descriptor.MaxFC) || + (card->CardNumber > card_descriptor.MaxCN) || + (card->IssueLevel > card_descriptor.MaxIL) || + (card->OEM > card_descriptor.MaxOEM)); +} + bool HIDPack(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); if ((format_idx < 0) || (format_idx > ARRAYLEN(FormatTable) - 2)) return false; - return FormatTable[format_idx].Pack(card, packed, preamble); + return FormatTable[format_idx].Pack(format_idx, card, packed, preamble); } void HIDPackTryAll(wiegand_card_t *card, bool preamble) { @@ -1613,7 +1581,7 @@ void HIDPackTryAll(wiegand_card_t *card, bool preamble) { int i = 0; while (FormatTable[i].Name) { memset(&packed, 0, sizeof(wiegand_message_t)); - bool res = FormatTable[i].Pack(card, &packed, preamble); + bool res = FormatTable[i].Pack(i, card, &packed, preamble); if (res) { cardformat_t fmt = HIDGetCardFormat(i); print_desc_wiegand(&fmt, &packed); @@ -1624,34 +1592,40 @@ void HIDPackTryAll(wiegand_card_t *card, bool preamble) { } bool HIDTryUnpack(wiegand_message_t *packed) { - if (FormatTable[0].Name == NULL) + if (FormatTable[0].Name == NULL) { return false; + } - int i = 0; wiegand_card_t card; memset(&card, 0, sizeof(wiegand_card_t)); uint8_t found_cnt = 0, found_invalid_par = 0; + int i = 0; while (FormatTable[i].Name) { if (FormatTable[i].Unpack(packed, &card)) { found_cnt++; hid_print_card(&card, FormatTable[i]); - - if (FormatTable[i].Fields.hasParity || card.ParityValid == false) + // if fields has parity AND card parity is false + if (FormatTable[i].Fields.hasParity && (card.ParityValid == false)) { found_invalid_par++; + } } ++i; } if (found_cnt) { - PrintAndLogEx(INFO, "found %u matching format%c", found_cnt, (found_cnt > 1) ? 's' : ' '); + PrintAndLogEx(INFO, "found " _YELLOW_("%u") " matching " _YELLOW_("%d-bit") " format%s" + , found_cnt + , packed->Length + , (found_cnt > 1) ? "s" : "" + ); } - if (packed->Length && found_invalid_par == 0) { - PrintAndLogEx(WARNING, "Wiegand unknown bit len %d", packed->Length); - PrintAndLogEx(HINT, "Try 0xFFFF's http://cardinfo.barkweb.com.au/"); + if (packed->Length && ((found_cnt - found_invalid_par) == 0)) { // if length > 0 and no valid parity matches + PrintAndLogEx(FAILED, "Parity tests failed"); } + PrintAndLogEx(NORMAL, ""); return ((found_cnt - found_invalid_par) > 0); } @@ -1664,6 +1638,34 @@ void HIDUnpack(int idx, wiegand_message_t *packed) { } } +// decode wiegand format using HIDTryUnpack +// return true if at least one valid matching formats found +bool decode_wiegand(uint32_t top, uint32_t mid, uint32_t bot, int n) { + + bool res = false; + if (top == 0 && mid == 0 && bot == 0) { + return res; + } + + if (n > 0) { + wiegand_message_t packed = initialize_message_object(top, mid, bot, n); + res = HIDTryUnpack(&packed); + } else { + wiegand_message_t packed = initialize_message_object(top, mid, bot, n); // 26-37 bits + res = HIDTryUnpack(&packed); + + PrintAndLogEx(INFO, "Trying with a preamble bit..."); + packed.Length += 1; + res |= HIDTryUnpack(&packed); + } + + if (res == false) { + PrintAndLogEx(DEBUG, "DEBUG: Error - " _RED_("HID no values found")); + } + + return res; +} + int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose) { uint8_t n = length - 1; uint8_t pad = data[0]; @@ -1674,17 +1676,18 @@ int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbos bytes_2_binstr(binstr, data + 1, n); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(data, length)); - PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); +// PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------"); + PrintAndLogEx(SUCCESS, "PACS............. " _GREEN_("%s"), sprint_hex_inrow(data, length)); + PrintAndLogEx(DEBUG, "padded bin....... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); binstr[strlen(binstr) - pad] = '\0'; - PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + PrintAndLogEx(DEBUG, "bin.............. " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); size_t hexlen = 0; uint8_t hex[16] = {0}; binstr_2_bytes(hex, &hexlen, binstr); - PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(SUCCESS, "hex.............. " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); uint32_t top = 0, mid = 0, bot = 0; if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { @@ -1694,14 +1697,16 @@ int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbos } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Wiegand decode"); wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr)); HIDTryUnpack(&packed); - PrintAndLogEx(NORMAL, ""); - if (strlen(binstr) >= 26 && verbose) { + + // SEOS + // iCLASS Legacy SE + // iCLASS Legacy SR + // iCLASS Legacy PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy")); PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr); @@ -1712,6 +1717,8 @@ int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbos PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr); PrintAndLogEx(NORMAL, ""); + // MIFARE DESFire + // MIFARE Classic char mfcbin[28] = {0}; mfcbin[0] = '1'; diff --git a/client/src/wiegand_formats.h b/client/src/wiegand_formats.h index 630d9cbb4..97b097e4a 100644 --- a/client/src/wiegand_formats.h +++ b/client/src/wiegand_formats.h @@ -36,17 +36,23 @@ typedef struct { bool hasIssueLevel; bool hasOEMCode; bool hasParity; + uint32_t MaxFC; // max Facility Code + uint64_t MaxCN; // max CardNumber + uint32_t MaxIL; // max IssueLevel + uint32_t MaxOEM;// max OEM } cardformatdescriptor_t; // Structure for defined Wiegand card formats available for packing/unpacking typedef struct { const char *Name; - bool (*Pack)(wiegand_card_t *card, wiegand_message_t *packed, bool preamble); + bool (*Pack)(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble); bool (*Unpack)(wiegand_message_t *packed, wiegand_card_t *card); - const char *Descrp; + const char *Description; + uint32_t Bits; // Number of bits in this format cardformatdescriptor_t Fields; } cardformat_t; +bool validate_card_limit(int format_idx, wiegand_card_t *card); void HIDListFormats(void); int HIDFindCardFormat(const char *format); cardformat_t HIDGetCardFormat(int idx); @@ -54,6 +60,7 @@ bool HIDPack(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bo bool HIDTryUnpack(wiegand_message_t *packed); void HIDPackTryAll(wiegand_card_t *card, bool preamble); void HIDUnpack(int idx, wiegand_message_t *packed); +bool decode_wiegand(uint32_t top, uint32_t mid, uint32_t bot, int n); int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose); void print_wiegand_code(wiegand_message_t *packed); void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed); diff --git a/client/src/wiegand_formatutils.c b/client/src/wiegand_formatutils.c index f77ab5bfe..d6d5758e6 100644 --- a/client/src/wiegand_formatutils.c +++ b/client/src/wiegand_formatutils.c @@ -128,11 +128,25 @@ bool set_nonlinear_field(wiegand_message_t *data, uint64_t value, uint8_t numBit return result; } -static uint8_t get_length_from_header(wiegand_message_t *data) { +uint8_t get_length_from_header(wiegand_message_t *data) { /** * detect if message has "preamble" / "sentinel bit" * Right now we just calculate the highest bit set - * 37 bit formats is hard to detect since it doesnt have a sentinel bit + * + * (from http://www.proxmark.org/forum/viewtopic.php?pid=5368#p5368) + * 0000 0010 0000 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx 26-bit + * 0000 0010 0000 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx 27-bit + * 0000 0010 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx 28-bit + * 0000 0010 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx 29-bit + * 0000 0010 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 30-bit + * 0000 0010 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 31-bit + * 0000 0010 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 32-bit + * 0000 0010 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 33-bit + * 0000 0010 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 34-bit + * 0000 0010 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 35-bit + * 0000 0011 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 36-bit + * 0000 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 37-bit + * 0000 00xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 38-bit */ uint8_t len = 0; uint32_t hfmt = 0; // for calculating card length @@ -140,29 +154,15 @@ static uint8_t get_length_from_header(wiegand_message_t *data) { if ((data->Top & 0x000FFFFF) > 0) { // > 64 bits hfmt = data->Top & 0x000FFFFF; len = 64; - } else if (data->Mid > 0) { // < 63-32 bits - - // detect HID format b38 set - if (data->Mid & 0xFFFFFFC0) { - hfmt = data->Mid; - len = 32; - } else { - - PrintAndLogEx(DEBUG, "hid preamble detected"); - len = 32; - - if ((data->Mid ^ 0x20) == 0) { hfmt = data->Bot; len = 0; } - else if ((data->Mid & 0x10) == 0) { hfmt = data->Mid & 0x1F; } - else if ((data->Mid & 0x08) == 0) { hfmt = data->Mid & 0x0F; } - else if ((data->Mid & 0x04) == 0) { hfmt = data->Mid & 0x07; } - else if ((data->Mid & 0x02) == 0) { hfmt = data->Mid & 0x03; } - else if ((data->Mid & 0x01) == 0) { hfmt = data->Mid & 0x01; } - else { hfmt = data->Mid & 0x3F;} - } - - } else { - hfmt = data->Bot; - len = 0; + } else if (data->Mid & 0xFFFFFFC0) { // handle 38bit and above format + hfmt = data->Mid; + len = 31; // remove leading 1 (preamble) in 38-64 bits format + } else if (((data->Mid >> 5) & 1) == 1) { // bit 38 is set => 26-36bit format + hfmt = (((data->Mid & 31) << 6) | (data->Bot >> 26)); // get bits 27-37 to check for format len bit + len = 25; + } else { // if bit 38 is not set => 37bit format + hfmt = 0; + len = 37; } while (hfmt > 0) { @@ -170,10 +170,6 @@ static uint8_t get_length_from_header(wiegand_message_t *data) { len++; } - // everything less than 26 bits found, assume 26 bits - if (len < 26) - len = 26; - return len; } @@ -193,13 +189,15 @@ wiegand_message_t initialize_message_object(uint32_t top, uint32_t mid, uint32_t bool add_HID_header(wiegand_message_t *data) { // Invalid value - if (data->Length > 84 || data->Length == 0) + if (data->Length > 84 || data->Length == 0) { return false; + } if (data->Length == 48) { data->Mid |= 1U << (data->Length - 32); // Example leading 1: start bit return true; } + if (data->Length >= 64) { data->Top |= 0x09e00000; // Extended-length header data->Top |= 1U << (data->Length - 64); // leading 1: start bit diff --git a/client/src/wiegand_formatutils.h b/client/src/wiegand_formatutils.h index 01a413032..05a36329b 100644 --- a/client/src/wiegand_formatutils.h +++ b/client/src/wiegand_formatutils.h @@ -52,6 +52,7 @@ bool set_nonlinear_field(wiegand_message_t *data, uint64_t value, uint8_t numBit wiegand_message_t initialize_message_object(uint32_t top, uint32_t mid, uint32_t bot, int n); +uint8_t get_length_from_header(wiegand_message_t *data); bool add_HID_header(wiegand_message_t *data); #endif diff --git a/client/t55_chk.lua b/client/t55_chk.lua deleted file mode 100644 index 8f88cdf3c..000000000 --- a/client/t55_chk.lua +++ /dev/null @@ -1,133 +0,0 @@ -local os = require("os") -local ac = require('ansicolors') -local getopt = require('getopt') -local dir = os.getenv('HOME') .. '/proxmark3/client/dictionaries/' -local dictionary_path = dir .. 'T5577date.dic' -local cyan = ac.cyan -local res = ac.reset - -author = ' Author: jareckib - created 02.02.2025' -version = ' version v1.01' -desc = [[ - A simple script for searching the password for T5577. The script creates a - dictionary starting from the entered starting year to the entered ending year. - There are two search methods - DDMMYYYY or YYYYMMDD. Checking the entire year - takes about 1 minute and 50 seconds. Date from 1900 to 2100. The script may be - useful if the password is, for example, a date of birth. -]] - -usage = [[ - script run t55_chk [-s start_year] [-e end_year] [-d | -y] -]] -options = [[ - -h Show this help message - -s Starting year (required) - -e Ending year (default: current year) - -d Search method: DDMMYYYY - -y Search method: YYYYMMDD -]] -examples = [[ - script run t55_chk -s 1999 -d - start from 1999, end year is current year, method 01011999 - script run t55_chk -s 1999 -y - start from 1999, end year is current year, method 19990101 - script run t55_chk -s 1999 -e 2001 -y - start from 1999, end year 2001, method 19990101 - script run t55_chk -s 1999 -e 2001 -d - start from 1999, end year 2001, method 01011999 -]] - -local function help() - print(ac.green..author..res) - print(version) - print(desc) - print(cyan..' Usage:'..res) - print(usage) - print(cyan..' Options:'..res) - print(options) - print(cyan..' Examples:'..res) - print(examples) -end - -local days_in_month = { - [1] = 31, [2] = 28, [3] = 31, [4] = 30, [5] = 31, [6] = 30, - [7] = 31, [8] = 31, [9] = 30, [10] = 31, [11] = 30, [12] = 31 -} - -local function generate_dictionary(start_year, end_year, mode) - local file = io.open(dictionary_path, "w") - if not file then - print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Cannot create T5577date.dic') - return false - end - - for year = start_year, end_year do - for month = 1, 12 do - local days_in_current_month = days_in_month[month] - if month == 2 and ((year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)) then - days_in_current_month = 29 - end - - for day = 1, days_in_current_month do - local month_str = string.format("%02d", month) - local day_str = string.format("%02d", day) - local year_str = tostring(year) - local entry = (mode == "1") and (year_str .. month_str .. day_str) or (day_str .. month_str .. year_str) - file:write(entry .. "\n") - end - end - end - - file:close() - return true -end - -local function oops(err) - core.console('clear') - print( string.rep('--',39) ) - print( string.rep('--',39) ) - print(ac.red..' ERROR:'..res.. err) - print( string.rep('--',39) ) - print( string.rep('--',39) ) - return nil, err -end - -local function main(args) - if #args == 0 then return help() end - - local start_year, end_year, mode = nil, nil, nil - local current_year = tonumber(os.date("%Y")) - - for o, a in getopt.getopt(args, 'hs:e:dy') do - if o == 'h' then return help() end - if o == 's' then - start_year = tonumber(a) - if not start_year then return oops('Invalid start year') end - end - if o == 'e' then - end_year = tonumber(a) - if not end_year then return oops('Invalid end year (-e)') end - end - if o == 'd' then mode = "d" end - if o == 'y' then mode = "y" end - end - - if not start_year then return oops('Starting year is required') end - if start_year < 1900 or start_year > 2100 then - return oops('Start year must be between 1900 and 2100') - end - if args[#args] == "-e" then return oops('Ending year cannot be empty') end - if not end_year then end_year = current_year end - if end_year < 1900 or end_year > 2100 then - return oops('End year must be between 1900 and 2100') - end - - if end_year < start_year then return oops('End year cannot be earlier than start year') end - if not mode then return oops('You must select searching method'..cyan..' -d'..res.. ' or '..cyan.. '-y'..res) end - - if generate_dictionary(start_year, end_year, mode) then - print(ac.green .. " File created: " .. dictionary_path .. res) - print(cyan .. " Starting password testing on T5577..." .. res) - core.console('lf t55 chk -f ' .. dictionary_path) - else - return oops('Problem saving the file') - end -end - -main(args) \ No newline at end of file diff --git a/common/commonutil.c b/common/commonutil.c index 7ed34067f..d4eed608e 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -17,6 +17,7 @@ //----------------------------------------------------------------------------- #include "commonutil.h" #include +#include "stdbool.h" /* Similar to FpgaGatherVersion this formats stored version information * into a string representation. It takes a pointer to the struct version_information_t, @@ -403,25 +404,34 @@ void Uint8byteToMemBe(uint8_t *data, uint64_t value) { } // Rotate Left - Ultralight, Desfire -void rol(uint8_t *data, const size_t len) { +void rol(uint8_t *data, const size_t n) { uint8_t first = data[0]; - for (size_t i = 0; i < len - 1; i++) { + for (size_t i = 0; i < n - 1; i++) { data[i] = data[i + 1]; } - data[len - 1] = first; + data[n - 1] = first; } // Rotate Right - Ultralight, Desfire -void ror(uint8_t *data, const size_t len) { - uint8_t last = data[len - 1]; +void ror(uint8_t *data, const size_t n) { + uint8_t last = data[n - 1]; - for (int i = len - 1; i > 0; i--) { + for (int i = n - 1; i > 0; i--) { data[i] = data[i - 1]; } data[0] = last; } +void xor(uint8_t *dest, const uint8_t *src, size_t n) { + + const uint8_t *s = src; + uint8_t *d = dest; + + for (; n > 0; n--) { + *d++ ^= *s++; + } +} void lsl(uint8_t *data, size_t len) { for (size_t n = 0; n < len - 1; n++) { diff --git a/common/commonutil.h b/common/commonutil.h index 0b187ef10..2ab4fbd79 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -128,8 +128,9 @@ void Uint7byteToMemBe(uint8_t *data, uint64_t value); void Uint8byteToMemBe(uint8_t *data, uint64_t value); // rotate left byte array -void rol(uint8_t *data, const size_t len); -void ror(uint8_t *data, const size_t len); +void rol(uint8_t *data, const size_t n); +void ror(uint8_t *data, const size_t n); +void xor(uint8_t *dest, const uint8_t *src, size_t n); void lsl(uint8_t *data, size_t len); uint32_t le24toh(const uint8_t data[3]); diff --git a/common/hitag2/hitag2_crypto.c b/common/hitag2/hitag2_crypto.c index 9e9499426..381451645 100644 --- a/common/hitag2/hitag2_crypto.c +++ b/common/hitag2/hitag2_crypto.c @@ -404,15 +404,10 @@ void ht2_hitag2_cipher_reset(hitag2_t *tag, const uint8_t *iv) { ((uint64_t)tag->sectors[1][1] << 24) | ((uint64_t)tag->sectors[1][2] << 32) | ((uint64_t)tag->sectors[1][3] << 40); - uint32_t uid = ((uint32_t)tag->sectors[0][0]) | - ((uint32_t)tag->sectors[0][1] << 8) | - ((uint32_t)tag->sectors[0][2] << 16) | - ((uint32_t)tag->sectors[0][3] << 24); - uint32_t iv_ = (((uint32_t)(iv[0]))) | - (((uint32_t)(iv[1])) << 8) | - (((uint32_t)(iv[2])) << 16) | - (((uint32_t)(iv[3])) << 24); - tag->cs = ht2_hitag2_init(REV64(key), REV32(uid), REV32(iv_)); + uint32_t uid = MemLeToUint4byte(tag->sectors[0]); + uint32_t riv = MemLeToUint4byte(iv); + + tag->cs = ht2_hitag2_init(REV64(key), REV32(uid), REV32(riv)); } int ht2_hitag2_cipher_authenticate(uint64_t *state, const uint8_t *authenticator_is) { diff --git a/common/mbedtls/asn1parse.c b/common/mbedtls/asn1parse.c index 98460e055..84a558bdc 100644 --- a/common/mbedtls/asn1parse.c +++ b/common/mbedtls/asn1parse.c @@ -321,11 +321,11 @@ static int asn1_get_sequence_of_cb(void *ctx, cb_ctx->cur; if (cur->buf.p != NULL) { - cur->next = - mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); + cur->next = mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); - if (cur->next == NULL) + if (cur->next == NULL) { return (MBEDTLS_ERR_ASN1_ALLOC_FAILED); + } cur = cur->next; } diff --git a/common/mbedtls/cmac.c b/common/mbedtls/cmac.c index 32a5937b0..b179ad483 100644 --- a/common/mbedtls/cmac.c +++ b/common/mbedtls/cmac.c @@ -180,8 +180,7 @@ int mbedtls_cipher_cmac_starts(mbedtls_cipher_context_t *ctx, if (ctx == NULL || ctx->cipher_info == NULL || key == NULL) return (MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA); - if ((retval = mbedtls_cipher_setkey(ctx, key, (int)keybits, - MBEDTLS_ENCRYPT)) != 0) + if ((retval = mbedtls_cipher_setkey(ctx, key, (int)keybits, MBEDTLS_ENCRYPT)) != 0) return (retval); type = ctx->cipher_info->type; @@ -405,14 +404,12 @@ int mbedtls_aes_cmac_prf_128(const unsigned char *key, size_t key_length, } else { memset(zero_key, 0, MBEDTLS_AES_BLOCK_SIZE); - ret = mbedtls_cipher_cmac(cipher_info, zero_key, 128, key, - key_length, int_key); + ret = mbedtls_cipher_cmac(cipher_info, zero_key, 128, key, key_length, int_key); if (ret != 0) goto exit; } - ret = mbedtls_cipher_cmac(cipher_info, int_key, 128, input, in_len, - output); + ret = mbedtls_cipher_cmac(cipher_info, int_key, 128, input, in_len, output); exit: mbedtls_platform_zeroize(int_key, sizeof(int_key)); diff --git a/doc/commands.json b/doc/commands.json index d3b4e711e..04d547610 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -920,7 +920,7 @@ }, "emv help": { "command": "emv help", - "description": "----------- ----------------------- General ----------------------- help This help list List ISO7816 history test Crypto logic selftest --------------------------------------------------------------------------------------- emv list available offline: yes Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "----------- ----------------------- General ----------------------- help This help list List ISO7816 history test Perform crypto logic self tests --------------------------------------------------------------------------------------- emv list available offline: yes Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "emv list --frame -> show frame delay times", "emv list -1 -> use trace buffer" @@ -1025,11 +1025,11 @@ "offline": false, "options": [ "-h, --help This help", - "-t, --selftest Self test", + "--test Perform self tests", "-a, --apdu Show APDU requests and responses", "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], - "usage": "emv roca [-htaw]" + "usage": "emv roca [-haw] [--test]" }, "emv scan": { "command": "emv scan", @@ -1410,19 +1410,19 @@ "hf 14a simaid -t 3 -> MIFARE Desfire", "hf 14a simaid -t 4 -> ISO/IEC 14443-4", "hf 14a simaid -t 11 -> Javacard (JCOP)", - "hf 14a simaid -t 3 --aid a000000000000000000000 --response 9000 --apdu 9000 -> AID, Response and APDU", - "hf 14a simaid -t 3 --rats 05788172220101 --response 01009000 --apdu 86009000 -> Custom RATS Added", - "hf 14a simaid -t 3 --rats 05788172220101 -x -> Enumerate AID Values" + "hf 14a simaid -t 3 --aid a000000000000000000000 --selectaid_response 9000 --getdata_response 9000 -> Custom AID and responses", + "hf 14a simaid -t 3 --ats 0578817222 --selectaid_response 01009000 --getdata_response 86009000 -> Custom ATS and responses", + "hf 14a simaid -t 3 --ats 0578817222 -x -> Enumerate AID Values" ], "offline": false, "options": [ "-h, --help This help", "-t, --type <1-12> Simulation type to use", "-u, --uid <4|7|10> hex bytes UID", - "-r, --rats <0-20> hex bytes RATS", - "-a, --aid <0-100> hex bytes for AID to respond to (Default: A000000000000000000000)", - "-e, --response <0-100> hex bytes for APDU Response to AID Select (Default: 9000)", - "-p, --apdu <0-100> hex bytes for APDU Response to Get Data request after AID (Default: 9000)", + "-r, --ats <0-20> hex bytes ATS", + "-a, --aid <0-30> hex bytes for AID to respond to (Default: A000000000000000000000)", + "-e, --selectaid_response <0-100> hex bytes for APDU Response to AID Select (Default: 9000)", + "-p, --getdata_response <0-100> hex bytes for APDU Response to Get Data request after AID (Default: 9000)", "-x, --enumerate Enumerate all AID values via returning Not Found and print them to console" ], "usage": "hf 14a simaid [-hx] -t <1-12> [-u ] [-r ] [-a ] [-e ] [-p ]" @@ -2064,6 +2064,23 @@ ], "usage": "hf 15 slixprivacyenable [-h] -p " }, + "hf 15 slixprotectpage": { + "command": "hf 15 slixprotectpage", + "description": "Defines protection pointer address of user mem and access cond. for pages", + "notes": [ + "hf 15 slixprotectpage -w deadbeef -p 3 -h 3" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-r, --readpw read password, 4 hex bytes", + "-w, --writepw write password, 4 hex bytes", + "-p, --ptr protection pointer page (0-78), if 0 entire user mem", + "-l, --lo page protection flags of lo page (0-None, 1-ReadPR, 2-WritePR)", + "-i, --hi page protection flags of hi page (0-None, 1-ReadPR, 2-WritePR)" + ], + "usage": "hf 15 slixprotectpage [-h] [-r ] [-w ] [-p ] -l -i " + }, "hf 15 slixwritepwd": { "command": "hf 15 slixwritepwd", "description": "Write a password on a SLIX family ISO-15693 tag.nSome tags do not support all different password types.", @@ -3657,14 +3674,23 @@ "command": "hf iclass sam", "description": "Extract PACS via a HID SAM", "notes": [ - "hf iclass sam" + "hf iclass sam", + "hf iclass sam -p -d a005a103800104 -> get PACS data, but ensure that epurse will stay unchanged", + "hf iclass sam --break-on-nr-mac -> get Nr-MAC for extracting encrypted SIO" ], "offline": false, "options": [ "-h, --help This help", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "-k, --keep keep the field active after command executed", + "-n, --nodetect skip selecting the card and sending card details to SAM", + "-t, --tlv decode TLV", + "--break-on-nr-mac stop tag interaction on nr-mac", + "-p, --prevent-epurse-update fake epurse update", + "--shallow shallow mod", + "-d, --data DER encoded command to send to SAM" ], - "usage": "hf iclass sam [-hv]" + "usage": "hf iclass sam [-hvkntp] [--break-on-nr-mac] [--shallow] [-d ]..." }, "hf iclass sim": { "command": "hf iclass sim", @@ -4486,12 +4512,13 @@ "-f, --file Specify a filename for dump file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", + "--1k+ MIFARE Classic Ev1 1k / S50", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", "--emu from emulator memory", "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cload [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu] [--gdm]" + "usage": "hf mf cload [-h] [-f ] [--mini] [--1k] [--1k+] [--2k] [--4k] [--emu] [--gdm]" }, "hf mf csave": { "command": "hf mf csave", @@ -5013,6 +5040,7 @@ "-h, --help This help", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", + "--1k+ MIFARE Classic Ev1 1k / S50", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", "-p, --pwd password 4bytes", @@ -5022,7 +5050,7 @@ "--start index of block to start writing (default 0)", "--end index of block to end writing (default last block)" ], - "usage": "hf mf gload [-hv] [--mini] [--1k] [--2k] [--4k] [-p ] [-f ] [--emu] [--start ] [--end ]" + "usage": "hf mf gload [-hv] [--mini] [--1k] [--1k+] [--2k] [--4k] [-p ] [-f ] [--emu] [--start ] [--end ]" }, "hf mf gsave": { "command": "hf mf gsave", @@ -7693,7 +7721,7 @@ "description": "Extract PACS via a HID SAM", "notes": [ "hf seos sam", - "hd seos sam -d a005a103800104 -> get PACS data" + "hf seos sam -d a005a103800104 -> get PACS data" ], "offline": false, "options": [ @@ -9851,8 +9879,8 @@ "command": "lf hitag chk", "description": "Run dictionary key or password recovery against Hitag card.", "notes": [ - "lf hitag chk", - "-> checks for both pwd / crypto keyslf hitag chk --crypto -> use def dictionary", + "lf hitag chk -> checks for both pwd / crypto keys", + "lf hitag chk --crypto -> use def dictionary", "lf hitag chk --pwd -f my.dic -> pwd mode, custom dictionary" ], "offline": false, @@ -13157,15 +13185,17 @@ "command": "wiegand decode", "description": "Decode raw hex or binary to wiegand format", "notes": [ - "wiegand decode --raw 2006f623ae" + "wiegand decode --raw 2006F623AE", + "wiegand decode --new 06BD88EB80 -> 4..8 bytes, new padded format" ], "offline": true, "options": [ "-h, --help This help", "-r, --raw raw hex to be decoded", - "-b, --bin binary string to be decoded" + "-b, --bin binary string to be decoded", + "-n, --new new padded pacs as raw hex to be decoded" ], - "usage": "wiegand decode [-h] [-r ] [-b ]" + "usage": "wiegand decode [-h] [-r ] [-b ] [-n ]" }, "wiegand encode": { "command": "wiegand encode", @@ -13200,8 +13230,8 @@ } }, "metadata": { - "commands_extracted": 759, + "commands_extracted": 760, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-01-14T15:42:02" + "extracted_on": "2025-03-12T15:46:33" } } diff --git a/doc/commands.md b/doc/commands.md index 8650508ff..960e40a2b 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -150,7 +150,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`emv help `|Y |`This help` |`emv list `|Y |`List ISO7816 history` -|`emv test `|Y |`Crypto logic selftest` +|`emv test `|Y |`Perform crypto logic self tests` |`emv challenge `|N |`Generate challenge` |`emv exec `|N |`Executes EMV contactless transaction` |`emv genac `|N |`Generate ApplicationCryptogram` @@ -261,6 +261,7 @@ Check column "offline" for their availability. |`hf 15 slixeasenable `|N |`Enable EAS mode on SLIX ISO-15693 tag` |`hf 15 slixprivacydisable`|N |`Disable privacy mode on SLIX ISO-15693 tag` |`hf 15 slixprivacyenable`|N |`Enable privacy mode on SLIX ISO-15693 tag` +|`hf 15 slixprotectpage `|N |`Protect pages on SLIX ISO-15693 tag` |`hf 15 passprotectafi `|N |`Password protect AFI - Cannot be undone` |`hf 15 passprotecteas `|N |`Password protect EAS - Cannot be undone` |`hf 15 findafi `|N |`Brute force AFI of an ISO-15693 tag` diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index fb838d4db..e103b1188 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -29,6 +29,7 @@ Useful docs: * [MIFARE Classic Gen1B](#mifare-classic-gen1b) * [Mifare Classic Direct Write OTP](#mifare-classic-direct-write-otp) * [MIFARE Classic OTP 2.0](#mifare-classic-otp-20) + * [MIFARE Classic MF4](#mifare-classic-mf4) * [MIFARE Classic DirectWrite aka Gen2 aka CUID](#mifare-classic-directwrite-aka-gen2-aka-cuid) * [MIFARE Classic Gen3 aka APDU](#mifare-classic-gen3-aka-apdu) * [MIFARE Classic USCUID](#mifare-classic-uscuid) @@ -642,6 +643,92 @@ hf mf info * Write: `40(7)`, `43`, `A0xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc +## MIFARE Classic MF4 + +^[Top](#top) + +Similar to OTP 2.0, but now additional configuration is possible. +Were manufactured by iKey LLC as a replacement for MF3. + +### Characteristics + +* Initial UID is 00000000 +* BCC: unknown +* SAK/ATQA: configurable +* ATS: configurable +* PPS: configurable (fake response) +* All bytes are 00 from factory wherever possible. + +### Identify + +^[Top](#top) + +Only possible before personalization. + +``` +hf mf info +... +[=] --- Magic Tag Information +[+] Magic capabilities... Gen 1a + +[=] --- PRNG Information +[+] Prng................. hard + +hf mf cgetblk --blk 3 +hf mf rdbl --blk 3 +[ If the ACLs do not match, this is an MF4 ] +``` + +### Magic commands + +^[Top](#top) + +Warning: changing the UID from 00000000 will disable all of these commands permanently. + +* Read backdoor: `40(7)`, `43`, `30xx`+crc +* Write: `40(7)`, `43`, `A0xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc + +### Magic configuration + +^[Top](#top) + +By accessing trailers of sectors 11-15 using gen1 mode, it is possible to re-configure the tag. + +The layout for a sector is below: +* block 0: data +* block 1: data +* block 2: data +* block 3[0-5] - key A +* block 3[6] - configuration byte +* block 3[7] - ACL byte [bits 7-4], configuration[3-0]/RFU +* block 3[8] - ACL byte +* block 3[9] - ACL user byte +* block 3[10-15] - key B + +Any data set in one mode will be mirrored to the other, as such be careful when configuring from gen1 mode to avoid unintentionally changing access conditions, keys or configurations. + +Here is how the IC can be configured: +* ATS + * Maximum length is 16 bytes inclduing TL + * Stored in trailers of sectors 0-10 (bytes 0-10: byte 6 of the matching sector; bytes 11-15: byte 7 lower half of sectors `(byte num.-11) (is lower half? +1 if yes)`) + * To avoid issues, please set unused bytes to 00 + * **Example** - to make the 15th byte `AF` you should set block 31 to `FFFFFFFFFFFF 00 0 A 8000 FFFFFFFFFFFF` and block 35 to `FFFFFFFFFFFF 00 0 F 8000 FFFFFFFFFFFF` +* ATQA/SAK + * If the values are changed from defaults, the custom values will be used during anticollision. + * SAK (CL2/final select, default 0x08): sector 11 trailer, byte 6 + * SAK (7b intermediate, default 0x04): sector 12 trailer, byte 6 + * ATQA (higher half (transmission), default 0x44): sector 13 trailer, byte 6 + * ATQA (lower half (transmission), default 0x00): sector 14 trailer, byte 6 + * **Example** - to make the SAK `28`, you should set block 47 to `FFFFFFFFFFFF 28 0 0 8000 FFFFFFFFFFFF` +* Anticollision behavior + * PPS support: sector 14 trailer, byte 7, bit 2 (from least significant); 0: off, 1: on + * RATS support: sector 14 trailer, byte 7, bit 0 (from least significant); 0: off; 1: on + * CL2 (7 byte UID) support: sector 15 trailer, byte 7, bit 3 (from least significant); 0: 4 bytes, 1: 7 bytes + * **Example** - to enable 7 byte UIDs, you should set block 63 to `FFFFFFFFFFFF 00 0 8 8000 FFFFFFFFFFFF` +* Locking the IC, i.e. removing magic wakeup + * In block 63, set byte 7 bits 2 and 0 to `0b1`, resulting in byte 7 containing at least `05`. + * Write your UID. + ## MIFARE Classic DirectWrite aka Gen2 aka CUID ^[Top](#top) @@ -650,8 +737,8 @@ hf mf info * Other names: * MF-8 (RU) - * MF-3 (RU) - * What's so special about this chip in particular..? + * MF-3 (RU) - not susceptible to "field reset bug", a way to detect [OTP](#mifare-classic-direct-write-otp) chips. + * MF-3.2 (RU) - static nonce `01200145`, helps avoid magic detection. ### Identify diff --git a/doc/md/Installation_Instructions/Troubleshooting.md b/doc/md/Installation_Instructions/Troubleshooting.md index 14402b68f..079e4bf4a 100644 --- a/doc/md/Installation_Instructions/Troubleshooting.md +++ b/doc/md/Installation_Instructions/Troubleshooting.md @@ -29,6 +29,7 @@ Always use the latest repository commits from *master* branch. There are always - [Qt Session management error](#qt-session-management-error) - [found architecture 'x86\_64' required architecture 'arm64' error](#found-architecture-x86_64-required-architecture-arm64-error) - [wrong permissions on runtime directory /run/user/1000](#wrong-permissions-on-runtime-directory-runuser1000) + - [proxspace `file not found or locked` on Windows 11](#proxspace-file-not-found-or-locked-on-windows-11) ## `pm3` or `pm3-flash*` doesn't see my Proxmark @@ -360,4 +361,33 @@ export XDG_RUNTIME_DIR=/run/user/1000 or export XDG_RUNTIME_DIR=/var/run/user/1000 -``` \ No newline at end of file +``` + +## proxspace 'file not found or locked' on Windows 11 +^[Top](#top) + +if you receive an error "file not found or locked" for any operation that needs to write a file. + +The cause is that Windows locks down many folders as 'read only', and you can't easily change this setting. + +How to fix (use this at your own risk): + +``` + Open your Windows Settings Control Panel + Then select "Privacy and security" + Then select "Windows Security" + Then select "Virus & threat protection" + Then scroll down and select "Manage ransomware protection" + Then select "Allow an app through Controlled folder access" + Answer "Yes" to allow this app to make changes to your system + Then select "Add an allowed app" to select the proper "proxmark3.exe" in the client folder. + +Potentially also do: + Select "Recently blocked apps" + Then select the most recent "proxmark3.exe" by pressing the "+" next to it. + Then select "Close". + +Side note: +You may also be able to choose "Browse all apps" and find your specific proxmark3.exe in the client folder but +be sure to choose the proper location and specific file in case you have more than one stored on your PC somewhere. +``` diff --git a/doc/md/Installation_Instructions/Windows-Installation-Instructions.md b/doc/md/Installation_Instructions/Windows-Installation-Instructions.md index e56979bf5..89cf966ea 100644 --- a/doc/md/Installation_Instructions/Windows-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Windows-Installation-Instructions.md @@ -6,6 +6,7 @@ ## Table of Contents - [Windows Installation instructions](#windows-installation-instructions) - [Table of Contents](#table-of-contents) + - [Installing on WSL 2](#installing-on-wsl-2) - [Installing dev-environment with ProxSpace](#installing-dev-environment-with-proxspace) - [Video Installation guide](#video-installation-guide) - [Driver Installation ( Windows 7 )](#driver-installation--windows-7-) @@ -25,6 +26,8 @@ - [Done!](#done-1) + + There are three ways to install, build and use Proxmark3 on Windows: * Using Gator96100 **ProxSpace**, a package to assist in your Windows installation of MinGW @@ -34,6 +37,9 @@ There are three ways to install, build and use Proxmark3 on Windows: We have listed three ways to use these two setups (dev environment vs pre-compiled binaries) --- +## Installing on WSL 2 +^[Top](#top) +Installing on WSL 2 use this [installation readme](Windows-WSL2-Installation-Instructions.md). ## Installing dev-environment with ProxSpace ^[Top](#top) diff --git a/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md b/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md index 68fe1e899..9102de117 100644 --- a/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md @@ -58,6 +58,20 @@ The fastest option is to run the brew command with the `arch -arm64` prefix i.e. Visual Studio Code still runs under Rosetta 2 and if you're developing for proxmark3 on an Apple Silicon Mac you might want to consider running the Insiders build which has support for running natively on Apple Silicon. +If you see an error when linking: +``` +ld: warning: ignoring file /usr/local/Cellar/.../libpython3.9.dylib, building for macOS-arm64 but attempting to link with file built for macOS-x86_64 +Undefined symbols for architecture arm64: + "_PyArg_UnpackTuple", referenced from: + _SwigPyObject_own in pm3_pywrap.o + ... +``` +your build environment has tried to link python across architectures. You can find or install python via homebrew (arm64) and inform the linker to use this +``` +brew install python@ +export LDFLAGS="-L/opt/homebrew/Cellar/python@/./Frameworks/Python.framework/Versions//lib/" && make +``` + ## Install Proxmark3 tools ^[Top](#top) diff --git a/docker/opensuse-leap/Dockerfile b/docker/opensuse-leap/Dockerfile index f381bfd9e..701ac8505 100644 --- a/docker/opensuse-leap/Dockerfile +++ b/docker/opensuse-leap/Dockerfile @@ -6,7 +6,7 @@ RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-de RUN zypper addrepo https://download.opensuse.org/repositories/home:wkazubski/15.6/home:wkazubski.repo && \ zypper --gpg-auto-import-keys refresh && \ - zypper --non-interactive install cross-arm-none-eabi-gcc13 cross-arm-none-eabi-newlib + zypper --non-interactive install cross-arm-none-eabi-gcc14 cross-arm-none-eabi-newlib RUN zypper --non-interactive install cmake python3 python3-pip && \ python3 -m pip install ansicolors sslcrypto diff --git a/fpga/Makefile b/fpga/Makefile index 821ac0ec7..769185260 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -44,6 +44,9 @@ XST_OPTS_AREA += -opt_level 2 XST_OPTS_AREA += -fsm_style bram XST_OPTS_AREA += -fsm_encoding compact +# par specific option (set determistic seed) +PAR_OPTIONS = -t 1 + # Types of selective module compilation: # WITH_LF Enables selection of LF modules (and disables all HF) @@ -179,12 +182,13 @@ work: %.ncd: %_map.ncd $(Q)$(RM) $@ $(info [-] PAR $@) - $(Q)$(XILINX_TOOLS_PREFIX)par $(VERBOSITY) -w $< $@ + $(Q)$(XILINX_TOOLS_PREFIX)par $(PAR_OPTIONS) $(VERBOSITY) -w $< $@ %.bit: %.ncd $(Q)$(RM) $@ $*.drc $*.rbt $(info [=] BITGEN $@) $(Q)$(XILINX_TOOLS_PREFIX)bitgen $(VERBOSITY) -w $* $@ + python3 ../strip_date_time_from_binary.py $@ || true $(Q)$(CP) $@ .. # Build all targets diff --git a/fpga/fpga_icopyx_hf.bit b/fpga/fpga_icopyx_hf.bit index db2878bbf..a7824dd74 100644 Binary files a/fpga/fpga_icopyx_hf.bit and b/fpga/fpga_icopyx_hf.bit differ diff --git a/fpga/fpga_pm3_felica.bit b/fpga/fpga_pm3_felica.bit index f79babab6..386638478 100644 Binary files a/fpga/fpga_pm3_felica.bit and b/fpga/fpga_pm3_felica.bit differ diff --git a/fpga/fpga_pm3_hf.bit b/fpga/fpga_pm3_hf.bit index 2e7a94059..1c159a612 100644 Binary files a/fpga/fpga_pm3_hf.bit and b/fpga/fpga_pm3_hf.bit differ diff --git a/fpga/fpga_pm3_hf_15.bit b/fpga/fpga_pm3_hf_15.bit index f2b1e4ff3..055615e71 100644 Binary files a/fpga/fpga_pm3_hf_15.bit and b/fpga/fpga_pm3_hf_15.bit differ diff --git a/fpga/fpga_pm3_lf.bit b/fpga/fpga_pm3_lf.bit index b87ea9489..79485cac1 100644 Binary files a/fpga/fpga_pm3_lf.bit and b/fpga/fpga_pm3_lf.bit differ diff --git a/fpga/strip_date_time_from_binary.py b/fpga/strip_date_time_from_binary.py new file mode 100644 index 000000000..aba9c0dc5 --- /dev/null +++ b/fpga/strip_date_time_from_binary.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +import sys + +# File to take a .bit file generated by xilinx webpack ISE +# and replace the date and time embedded within with 'F'. +# The header of the bitfile is seperated by ascii markers +# 'a', 'b', 'c', 'd' etc. +# see fpga_compress.c for comparison + +def parse_and_split_file(filename): + split_chars = ['a', 'b', 'c', 'd'] # Characters to split on + extracted_data = [] # for debug + + print("Overwriting date and time in bitfile {}".format(filename)) + + with open(filename, 'rb') as f: # Read as binary to handle non-text files + data = f.read(100) # Read first 100 bytes which should contain all information + + decoded_data = bytearray(data) + + for i in range(len(decoded_data) - 3): + # subsequent two bytes after marker are null and the length + next_byte = decoded_data[i+1] + data_length = decoded_data[i+2] - 1 # Don't overwrite terminating char + + if decoded_data[i] == ord(split_chars[0]) and next_byte == 0x0: + start = i+3 + extracted_data.append(decoded_data[start:start+data_length]) + + # time, date + if split_chars[0] == 'c' or split_chars[0] == 'd': + decoded_data[start:start+data_length] = bytes('F', encoding='ascii') * data_length + + split_chars.pop(0) + + if not split_chars: + break + + print("Extracted data from bitfile: {}".format(extracted_data)) + + with open(filename, 'r+b') as f: # Write back modified bytes + f.seek(0) + f.write(decoded_data.ljust(100, b' ')) + print("writing complete") + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python script.py ") + sys.exit(1) + + filename = sys.argv[1] + parse_and_split_file(filename) diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 996b04079..a71e09a1e 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -575,6 +575,7 @@ typedef struct { #define CMD_HF_ISO15693_SLIX_PASS_PROTECT_AFI 0x0863 #define CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS 0x0864 #define CMD_HF_ISO15693_SLIX_WRITE_PWD 0x0865 +#define CMD_HF_ISO15693_SLIX_PROTECT_PAGE 0x0868 #define CMD_HF_ISO15693_WRITE_AFI 0x0866 #define CMD_HF_TEXKOM_SIMULATE 0x0320 #define CMD_HF_ISO15693_EML_CLEAR 0x0330 @@ -772,7 +773,8 @@ typedef struct { #define FLAG_INTERACTIVE 0x0001 #define FLAG_ATQA_IN_DATA 0x0002 #define FLAG_SAK_IN_DATA 0x0004 -#define FLAG_RATS_IN_DATA 0x0008 +#define FLAG_ATS_IN_DATA 0x0008 +#define FLAG_ENUMERATE_AID 0x0010 // internal constants, use the function macros instead #define FLAG_MASK_UID 0x0030 @@ -923,6 +925,9 @@ typedef struct { // No key available client/pm3: no cryptographic key available. #define PM3_ENOKEY -28 +// Cryptographic error client/pm3: cryptographic operation failed +#define PM3_ECRYPTO -29 + // No data client/pm3: no data available, no host frame available (not really an error) #define PM3_ENODATA -98 // Quit program client: reserved, order to quit the program diff --git a/include/protocols.h b/include/protocols.h index 0e6dbb3f3..7048263d5 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -205,6 +205,12 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MIFARE_EV1_UIDF1 0x40 #define MIFARE_EV1_UIDF2 0x20 #define MIFARE_EV1_UIDF3 0x60 +#define MIFARE_EV1_SELECT_APP 0x5A +#define MIFARE_EV1_AUTH_AES 0xAA +#define MIFARE_EV1_AUTH_AES_2 0xAF +#define MIFARE_EV1_GET_FILE_INFO 0xF5 +#define MIFARE_EV1_READ_DATA 0xBD + #define MIFARE_ULC_WRITE 0xA2 #define MIFARE_ULC_COMP_WRITE 0xA0 @@ -473,30 +479,30 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define PICOPASS_SECURE_PAGEMODE_KEYS_MODIFIABLE 0x03 // ISO 7816-4 Basic interindustry commands. For command APDU's. -#define ISO7816_READ_BINARY 0xB0 -#define ISO7816_WRITE_BINARY 0xD0 -#define ISO7816_UPDATE_BINARY 0xD6 -#define ISO7816_ERASE_BINARY 0x0E -#define ISO7816_READ_RECORDS 0xB2 -#define ISO7816_WRITE_RECORDS 0xD2 -#define ISO7816_APPEND_RECORD 0xE2 -#define ISO7816_UPDATE_RECORD 0xDC -#define ISO7816_GET_DATA 0xCA -#define ISO7816_PUT_DATA 0xDA -#define ISO7816_SELECT_FILE 0xA4 -#define ISO7816_VERIFY 0x20 -#define ISO7816_INTERNAL_AUTHENTICATION 0x88 -#define ISO7816_EXTERNAL_AUTHENTICATION 0x82 -#define ISO7816_GET_CHALLENGE 0x84 -#define ISO7816_MANAGE_CHANNEL 0x70 -#define ISO7816_APPLICATION_BLOCK 0x1E -#define ISO7816_APPLICATION_UNBLOCK 0x18 -#define ISO7816_CARD_BLOCK 0x16 +#define ISO7816_READ_BINARY 0xB0 +#define ISO7816_WRITE_BINARY 0xD0 +#define ISO7816_UPDATE_BINARY 0xD6 +#define ISO7816_ERASE_BINARY 0x0E +#define ISO7816_READ_RECORDS 0xB2 +#define ISO7816_WRITE_RECORDS 0xD2 +#define ISO7816_APPEND_RECORD 0xE2 +#define ISO7816_UPDATE_RECORD 0xDC +#define ISO7816_GET_DATA 0xCA +#define ISO7816_PUT_DATA 0xDA +#define ISO7816_SELECT_FILE 0xA4 +#define ISO7816_VERIFY 0x20 +#define ISO7816_INTERNAL_AUTHENTICATION 0x88 +#define ISO7816_EXTERNAL_AUTHENTICATION 0x82 +#define ISO7816_GET_CHALLENGE 0x84 +#define ISO7816_MANAGE_CHANNEL 0x70 +#define ISO7816_APPLICATION_BLOCK 0x1E +#define ISO7816_APPLICATION_UNBLOCK 0x18 +#define ISO7816_CARD_BLOCK 0x16 #define ISO7816_GENERATE_APPLICATION_CRYPTOGRAM 0xAE -#define ISO7816_GET_PROCESSING_OPTIONS 0xA8 -#define ISO7816_PIN_CHANGE 0x24 +#define ISO7816_GET_PROCESSING_OPTIONS 0xA8 +#define ISO7816_PIN_CHANGE 0x24 -#define ISO7816_GET_RESPONSE 0xC0 +#define ISO7816_GET_RESPONSE 0xC0 // ISO7816-4 For response APDU's #define ISO7816_OK 0x9000 diff --git a/tools/fpga_compress/fpga_compress.c b/tools/fpga_compress/fpga_compress.c index 97ba90024..a24b035df 100644 --- a/tools/fpga_compress/fpga_compress.c +++ b/tools/fpga_compress/fpga_compress.c @@ -18,9 +18,13 @@ #include #include #include +#include +#include "time.h" #include "fpga.h" #include "lz4hc.h" +int fileno(FILE *); + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -354,6 +358,8 @@ static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len for (uint16_t i = 0; i < FPGA_BITSTREAM_FIXED_HEADER_SIZE; i++) { if (fgetc(infile) != bitparse_fixed_header[i]) { fprintf(stderr, "Invalid FPGA file. Aborting...\n\n"); + fprintf(stderr, "File: %s\n", infile_name); + return (EXIT_FAILURE); } } @@ -380,30 +386,22 @@ static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len strncat(dst, tempstr, len - strlen(dst) - 1); } - strncat(dst, " ", len - strlen(dst) - 1); - if (bitparse_find_section(infile, 'c', &fpga_info_len)) { - for (uint32_t i = 0; i < fpga_info_len; i++) { - char c = (char)fgetc(infile); - if (i < sizeof(tempstr)) { - if (c == '/') c = '-'; - if (c == ' ') c = '0'; - tempstr[i] = c; - } - } - strncat(dst, tempstr, len - strlen(dst) - 1); + // Get file statistics to extract date and time via file timestamp + int fd = fileno(infile); + struct stat fileStat; + + if (fstat(fd, &fileStat) == 0) { + struct tm *modTime = localtime(&fileStat.st_mtime); + + + char timeBuf[64]; + snprintf(timeBuf, sizeof(timeBuf), " %02d-%02d-%04d %02d:%02d:%02d", + modTime->tm_mday, modTime->tm_mon + 1, modTime->tm_year + 1900, + modTime->tm_hour, modTime->tm_min, modTime->tm_sec); + + strncat(dst, timeBuf, len - strlen(dst) - 1); } - if (bitparse_find_section(infile, 'd', &fpga_info_len)) { - strncat(dst, " ", len - strlen(dst) - 1); - for (uint32_t i = 0; i < fpga_info_len; i++) { - char c = (char)fgetc(infile); - if (i < sizeof(tempstr)) { - if (c == ' ') c = '0'; - tempstr[i] = c; - } - } - strncat(dst, tempstr, len - strlen(dst) - 1); - } return 0; } diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index d5a73fb62..d0952ec36 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -445,6 +445,8 @@ while true; do if ! CheckExecute "nfc decode test - vcard" "$CLIENTBIN -c 'nfc decode -d d20ca3746578742f782d7643617264424547494e3a56434152440a56455253494f4e3a332e300a4e3a43687269733b4963656d616e3b3b3b0a464e3a476f7468656e627572670a5245563a323032312d30362d32345432303a31353a30385a0a6974656d322e582d4142444154453b747970653d707265663a323032302d30362d32340a4954454d322e582d41424c4142454c3a5f24213c416e6e69766572736172793e21245f0a454e443a56434152440a'" "END:VCARD"; then break; fi if ! CheckExecute "nfc decode test - apple wallet" "$CLIENTBIN -c 'nfc decode -d 031AD10116550077616C6C65743A2F2F61637469766174652F6E6663FE'" "activate/nfc"; then break; fi if ! CheckExecute "nfc decode test - signature" "$CLIENTBIN -c 'nfc decode -d 03FF010194113870696C65742E65653A656B616172743A3266195F26063132303832325904202020205F28033233335F2701316E1B5A13333038363439303039303030323636343030355304EBF2CE704103000000AC536967010200803A2448FCA7D354A654A81BD021150D1A152D1DF4D7A55D2B771F12F094EAB6E5E10F2617A2F8DAD4FD38AFF8EA39B71C19BD42618CDA86EE7E144636C8E0E7CFC4096E19C3680E09C78A0CDBC05DA2D698E551D5D709717655E56FE3676880B897D2C70DF5F06ECE07C71435255144F8EE41AF110E7B180DA0E6C22FB8FDEF61800025687474703A2F2F70696C65742E65652F6372742F33303836343930302D303030312E637274FE'" "30864900-0001.crt"; then break; fi + if ! CheckExecute "wiegand decode test - raw" "$CLIENTBIN -c 'wiegand decode --raw 2006F623AE'" "FC: 123 CN: 4567 parity \( ok \)"; then break; fi + if ! CheckExecute "wiegand decode test - new" "$CLIENTBIN -c 'wiegand decode --new 06BD88EB80'" "FC: 123 CN: 4567 parity \( ok \)"; then break; fi echo -e "\n${C_BLUE}Testing LF:${C_NC}" if ! CheckExecute "lf hitag2 test" "$CLIENTBIN -c 'lf hitag test'" "Tests \( ok"; then break; fi