diff --git a/CHANGELOG.md b/CHANGELOG.md index ee2468215..595064b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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] + - Added `c ` option to `lf pac clone` to allow cloning PAC/Stanley tag from card ID (@danshuk) + - Added decoded PAC/Stanley card ID to `lf pac read` (@danshuk) - Chg mifare classic keytable output refactored and uses colors (@iceman1001) - Fix `hf mf nested` - now writes the correct blockno (@iceman1001) - Chg `lf t55xx dump` - now supports saving to JSON (@iceman1001) diff --git a/client/cmdlfpac.c b/client/cmdlfpac.c index eb1b20cd9..04ff13633 100644 --- a/client/cmdlfpac.c +++ b/client/cmdlfpac.c @@ -4,8 +4,8 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Low frequency Stanley/PAC tag commands -// NRZ, RF/32, 128 bits long (unknown cs) +// Low frequency PAC/Stanley tag commands +// NRZ, RF/32, 128 bits long //----------------------------------------------------------------------------- #include "cmdlfpac.h" @@ -21,22 +21,111 @@ #include "lfdemod.h" // preamble test #include "protocols.h" // t55xx defines #include "cmdlft55xx.h" // clone.. +#include "parity.h" static int CmdHelp(const char *Cmd); static int usage_lf_pac_clone(void) { - PrintAndLogEx(NORMAL, "clone a Stanley/PAC tag to a T55x7 tag."); + PrintAndLogEx(NORMAL, "clone a PAC/Stanley tag to a T55x7 tag."); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf pac clone [h] [b ]"); + PrintAndLogEx(NORMAL, "Usage: lf pac clone [h] [c ] [b ]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " b : raw hex data. 12 bytes max"); + PrintAndLogEx(NORMAL, " c : 8 byte card ID"); + PrintAndLogEx(NORMAL, " b : raw hex data. 16 bytes max"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf pac clone c CD4F5552 "); PrintAndLogEx(NORMAL, " lf pac clone b FF2049906D8511C593155B56D5B2649F "); return PM3_SUCCESS; } +// by danshuk +// PAC_8byte format: preamble (8 mark/idle bits), ascii STX (02), ascii '2' (32), ascii '0' (30), ascii bytes 0..7 (cardid), then xor checksum of cardid bytes +// all bytes following 8 bit preamble are one start bit (0), 7 data bits (lsb first), odd parity bit, and one stop bit (1) +static int demodbuf_to_pacid(uint8_t *src, const size_t src_size, uint8_t *dst, const size_t dst_size) { + const size_t byteLength = 10; // start bit, 7 data bits, parity bit, stop bit + const size_t startIndex = 8 + (3 * byteLength) + 1; // skip 8 bits preamble, STX, '2', '0', and first start bit + const size_t dataLength = 9; + + if (startIndex + byteLength * (dataLength - 1) > src_size) { + PrintAndLogEx(DEBUG, "DEBUG: Error - PAC: Source buffer too small"); + return PM3_EOVFLOW; + } + if (dataLength > dst_size) { + PrintAndLogEx(DEBUG, "DEBUG: Error - PAC: Destination buffer too small"); + return PM3_EOVFLOW; + } + + uint8_t checksum = 0; + for (size_t idx = 0; idx < dataLength; idx++) { + uint8_t byte = (uint8_t)bytebits_to_byteLSBF(src + startIndex + (byteLength * idx), 8); + dst[idx] = byte & 0x7F; // discard the parity bit + if (oddparity8(dst[idx]) != (byte & 0x80) >> 7) { + PrintAndLogEx(DEBUG, "DEBUG: Error - PAC: Parity check failed"); + return PM3_ESOFT; + } + if (idx < dataLength - 1) checksum ^= byte; + } + if (dst[dataLength - 1] != checksum) { + PrintAndLogEx(DEBUG, "DEBUG: Error - PAC: Bad checksum - expected: %02X, actual: %02X", dst[dataLength - 1], checksum); + return PM3_ESOFT; + } + dst[dataLength - 1] = 0; // overwrite checksum byte with null terminator + + return PM3_SUCCESS; +} + +/* +// convert a 16 byte array of raw demod data (FF204990XX...) to 8 bytes of PAC_8byte ID +// performs no parity or checksum validation +static void pacRawToCardId(uint8_t* outCardId, const uint8_t* rawBytes) { + for (int i = 4; i < 12; i++) { + uint8_t shift = 7 - (i + 3) % 4 * 2; + size_t index = i + (i - 1) / 4; + + outCardId[i - 4] = reflect8((((rawBytes[index] << 8) | (rawBytes[index + 1])) >> shift) & 0xFE); + } +} +*/ + +// convert 8 bytes of PAC_8byte ID to 16 byte array of raw data (FF204990XX...) +static void pacCardIdToRaw(uint8_t* outRawBytes, const char* cardId) { + uint8_t idbytes[10]; + + // prepend PAC_8byte card type "20" + idbytes[0] = '2'; + idbytes[1] = '0'; + for (size_t i = 0; i < 8; i++) + idbytes[i + 2] = toupper(cardId[i]); + + // initialise array with start and stop bits + for (size_t i = 0; i < 16; i++) + outRawBytes[i] = 0x40 >> (i + 3) % 5 * 2; + + outRawBytes[0] = 0xFF; // mark + stop + outRawBytes[1] = 0x20; // start + reflect8(STX) + + uint8_t checksum = 0; + for (size_t i = 2; i < 13; i++) { + uint8_t shift = 7 - (i + 3) % 4 * 2; + uint8_t index = i + (i - 1) / 4; + + uint16_t pattern; + if (i < 12) { + pattern = reflect8(idbytes[i - 2]); + pattern |= oddparity8(pattern); + if (i > 3) checksum ^= idbytes[i - 2]; + } + else + pattern = (reflect8(checksum) & 0xFE) | oddparity8(checksum); + pattern <<= shift; + + outRawBytes[index] |= pattern >> 8 & 0xFF; + outRawBytes[index + 1] |= pattern & 0xFF; + } +} + //see NRZDemod for what args are accepted static int CmdPacDemod(const char *Cmd) { @@ -68,13 +157,14 @@ static int CmdPacDemod(const char *Cmd) { uint32_t raw3 = bytebits_to_byte(DemodBuffer + 64, 32); uint32_t raw4 = bytebits_to_byte(DemodBuffer + 96, 32); - // preamble then appears to have marker bits of "10" CS? - // 11111111001000000 10 01001100 10 00001101 10 00001101 10 00001101 10 00001101 10 00001101 10 00001101 10 00001101 10 00001101 10 10001100 10 100000001 - // unknown checksum 9 bits at the end + const size_t idLen = 9; // 8 bytes + null terminator + uint8_t cardid[idLen]; + int retval = demodbuf_to_pacid(DemodBuffer, DemodBufferLen, cardid, sizeof(cardid)); + + if (retval == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "PAC/Stanley Tag Found -- Card ID: %s, Raw: %08X%08X%08X%08X", cardid, raw1, raw2, raw3, raw4); - PrintAndLogEx(SUCCESS, "PAC/Stanley Tag Found -- Raw: %08X%08X%08X%08X", raw1, raw2, raw3, raw4); - PrintAndLogEx(INFO, "How the Raw ID is translated by the reader is unknown. Share your trace file on forum"); - return PM3_SUCCESS; + return retval; } static int CmdPacRead(const char *Cmd) { @@ -93,6 +183,21 @@ static int CmdPacClone(const char *Cmd) { switch (tolower(param_getchar(Cmd, cmdp))) { case 'h': return usage_lf_pac_clone(); + case 'c': { + // skip first block, 4*4 = 16 bytes left + uint8_t rawhex[16] = {0}; + char cardid[9]; + int res = param_getstr(Cmd, cmdp + 1, cardid, sizeof(cardid)); + if (res < 8) + errors = true; + + pacCardIdToRaw(rawhex, cardid); + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + blocks[i] = bytes_to_num(rawhex + ((i - 1) * 4), sizeof(uint32_t)); + } + cmdp += 2; + break; + } case 'b': { // skip first block, 4*4 = 16 bytes left uint8_t rawhex[16] = {0}; @@ -115,10 +220,10 @@ static int CmdPacClone(const char *Cmd) { if (errors || cmdp == 0) return usage_lf_pac_clone(); - //Pac - compat mode, NRZ, data rate 40, 3 data blocks - blocks[0] = T55x7_MODULATION_DIRECT | T55x7_BITRATE_RF_40 | 4 << T55x7_MAXBLOCK_SHIFT; + //Pac - compat mode, NRZ, data rate 32, 3 data blocks + blocks[0] = T55x7_MODULATION_DIRECT | T55x7_BITRATE_RF_32 | 4 << T55x7_MAXBLOCK_SHIFT; - PrintAndLogEx(INFO, "Preparing to clone Securakey to T55x7 with raw hex"); + PrintAndLogEx(INFO, "Preparing to clone PAC/Stanley tag to T55x7 with raw hex"); print_blocks(blocks, ARRAYLEN(blocks)); return clone_t55xx_tag(blocks, ARRAYLEN(blocks)); @@ -133,7 +238,7 @@ static int CmdPacSim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"demod", CmdPacDemod, AlwaysAvailable, "Demodulate an PAC tag from the GraphBuffer"}, + {"demod", CmdPacDemod, AlwaysAvailable, "Demodulate a PAC tag from the GraphBuffer"}, {"read", CmdPacRead, IfPm3Lf, "Attempt to read and extract tag data from the antenna"}, {"clone", CmdPacClone, IfPm3Lf, "clone PAC tag to T55x7"}, {"sim", CmdPacSim, IfPm3Lf, "simulate PAC tag"},