'hf iclass wrbl' - both commands now tries tens times. Both honor verbose param, both has gotten some love with regards to timing on device side, and they also turn off the field more consistent.
2543 lines
83 KiB
C
2543 lines
83 KiB
C
//-----------------------------------------------------------------------------
|
|
// Gerhard de Koning Gans - May 2008
|
|
// Hagen Fritsch - June 2010
|
|
// Gerhard de Koning Gans - May 2011
|
|
// Gerhard de Koning Gans - June 2012 - Added iClass card and reader emulation
|
|
//
|
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
|
// the license.
|
|
//-----------------------------------------------------------------------------
|
|
// Routines to support iClass.
|
|
//-----------------------------------------------------------------------------
|
|
// Based on ISO14443a implementation. Still in experimental phase.
|
|
// Contribution made during a security research at Radboud University Nijmegen
|
|
//
|
|
// Please feel free to contribute and extend iClass support!!
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// FIX:
|
|
// ====
|
|
// We still have sometimes a demodulation error when sniffing iClass communication.
|
|
// The resulting trace of a read-block-03 command may look something like this:
|
|
//
|
|
// + 22279: : 0c 03 e8 01
|
|
//
|
|
// ...with an incorrect answer...
|
|
//
|
|
// + 85: 0: TAG ff! ff! ff! ff! ff! ff! ff! ff! bb 33 bb 00 01! 0e! 04! bb !crc
|
|
//
|
|
// We still left the error signalling bytes in the traces like 0xbb
|
|
//
|
|
// A correct trace should look like this:
|
|
//
|
|
// + 21112: : 0c 03 e8 01
|
|
// + 85: 0: TAG ff ff ff ff ff ff ff ff ea f5
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "iclass.h"
|
|
|
|
#include "proxmark3_arm.h"
|
|
#include "cmd.h"
|
|
// Needed for CRC in emulation mode;
|
|
// same construction as in ISO 14443;
|
|
// different initial value (CRC_ICLASS)
|
|
#include "crc16.h"
|
|
#include "optimized_cipher.h"
|
|
|
|
#include "appmain.h"
|
|
#include "BigBuf.h"
|
|
#include "fpgaloader.h"
|
|
#include "string.h"
|
|
#include "util.h"
|
|
#include "dbprint.h"
|
|
#include "protocols.h"
|
|
#include "ticks.h"
|
|
|
|
static int g_wait = 300;
|
|
static int timeout = 5000;
|
|
static uint32_t time_rdr = 0;
|
|
static uint32_t time_response = 0;
|
|
|
|
static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay);
|
|
int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf);
|
|
|
|
#define MODE_SIM_CSN 0
|
|
#define MODE_EXIT_AFTER_MAC 1
|
|
#define MODE_FULLSIM 2
|
|
|
|
#ifndef ICLASS_DMA_BUFFER_SIZE
|
|
# define ICLASS_DMA_BUFFER_SIZE 256
|
|
#endif
|
|
|
|
// The length of a received command will in most cases be no more than 18 bytes.
|
|
// 32 should be enough!
|
|
#ifndef ICLASS_BUFFER_SIZE
|
|
#define ICLASS_BUFFER_SIZE 32
|
|
#endif
|
|
|
|
#define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The software UART that receives commands from the reader, and its state
|
|
// variables.
|
|
//-----------------------------------------------------------------------------
|
|
/*
|
|
typedef struct {
|
|
enum {
|
|
STATE_UNSYNCD,
|
|
STATE_START_OF_COMMUNICATION,
|
|
STATE_RECEIVING
|
|
} state;
|
|
uint16_t shiftReg;
|
|
int bitCnt;
|
|
int byteCnt;
|
|
// int byteCntMax;
|
|
int posCnt;
|
|
int nOutOfCnt;
|
|
int OutOfCnt;
|
|
int syncBit;
|
|
int samples;
|
|
int highCnt;
|
|
int swapper;
|
|
int counter;
|
|
int bitBuffer;
|
|
int dropPosition;
|
|
uint8_t *output;
|
|
} tUartIc;
|
|
*/
|
|
typedef struct {
|
|
enum {
|
|
DEMOD_IC_UNSYNCD,
|
|
DEMOD_IC_START_OF_COMMUNICATION,
|
|
DEMOD_IC_START_OF_COMMUNICATION2,
|
|
DEMOD_IC_START_OF_COMMUNICATION3,
|
|
DEMOD_IC_SOF_COMPLETE,
|
|
DEMOD_IC_MANCHESTER_D,
|
|
DEMOD_IC_MANCHESTER_E,
|
|
DEMOD_IC_END_OF_COMMUNICATION,
|
|
DEMOD_IC_END_OF_COMMUNICATION2,
|
|
DEMOD_IC_MANCHESTER_F,
|
|
DEMOD_IC_ERROR_WAIT
|
|
} state;
|
|
int bitCount;
|
|
int posCount;
|
|
int syncBit;
|
|
uint16_t shiftReg;
|
|
uint32_t buffer;
|
|
uint32_t buffer2;
|
|
uint32_t buffer3;
|
|
int buff;
|
|
int samples;
|
|
int len;
|
|
enum {
|
|
SUB_NONE,
|
|
SUB_FIRST_HALF,
|
|
SUB_SECOND_HALF,
|
|
SUB_BOTH
|
|
} sub;
|
|
uint8_t *output;
|
|
} tDemodIc;
|
|
|
|
/*
|
|
* Abrasive's uart implementation
|
|
* https://github.com/abrasive/proxmark3/commit/2b8bff7daea8ae1193bf7ee29b1fa46e95218902
|
|
*/
|
|
// Static vars for UART
|
|
typedef struct {
|
|
bool synced;
|
|
bool frame;
|
|
bool frame_done;
|
|
uint8_t *buf;
|
|
int len;
|
|
} tUartIc;
|
|
static tUartIc Uart;
|
|
|
|
static void OnError(uint8_t reason) {
|
|
reply_mix(CMD_ACK, 0, reason, 0, 0, 0);
|
|
switch_off();
|
|
}
|
|
|
|
static void uart_reset(void) {
|
|
Uart.frame_done = false;
|
|
Uart.synced = false;
|
|
Uart.frame = false;
|
|
}
|
|
|
|
static void uart_init(uint8_t *data) {
|
|
Uart.buf = data;
|
|
uart_reset();
|
|
}
|
|
|
|
static void uart_bit(uint8_t bit) {
|
|
static uint8_t buf = 0xff;
|
|
static uint8_t n_buf;
|
|
static int nmsg_byte;
|
|
buf <<= 1;
|
|
buf |= bit ? 1 : 0;
|
|
|
|
if (!Uart.frame) {
|
|
if (buf == 0x7b) { // 0b0111 1011
|
|
Uart.frame = true;
|
|
n_buf = 0;
|
|
Uart.len = 0;
|
|
nmsg_byte = 0;
|
|
}
|
|
} else {
|
|
static uint8_t msg_byte;
|
|
n_buf++;
|
|
if (n_buf == 8) {
|
|
msg_byte >>= 2;
|
|
switch (buf) {
|
|
case 0xbf: // 0 - 1011 1111
|
|
break;
|
|
case 0xef: // 1 - 1110 1111
|
|
msg_byte |= (1 << 6);
|
|
break;
|
|
case 0xfb: // 2 - 1111 1011
|
|
msg_byte |= (2 << 6);
|
|
break;
|
|
case 0xfe: // 3 - 1111 1110
|
|
msg_byte |= (3 << 6);
|
|
break;
|
|
case 0xdf: // eof - 1101 1111
|
|
Uart.frame = false;
|
|
Uart.synced = false;
|
|
Uart.frame_done = true;
|
|
break;
|
|
default:
|
|
Uart.frame = false;
|
|
Uart.synced = false;
|
|
Dbprintf("[-] bad %02X at %d:%d", buf, Uart.len, nmsg_byte);
|
|
}
|
|
|
|
if (Uart.frame) { // data bits
|
|
nmsg_byte += 2;
|
|
if (nmsg_byte >= 8) {
|
|
Uart.buf[Uart.len++] = msg_byte;
|
|
nmsg_byte = 0;
|
|
}
|
|
}
|
|
n_buf = 0;
|
|
buf = 0xff;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void uart_samples(uint8_t byte) {
|
|
static uint32_t buf;
|
|
static int window;
|
|
static int drop_next = 0;
|
|
|
|
uint32_t falling;
|
|
int lz;
|
|
|
|
if (!Uart.synced) {
|
|
if (byte == 0xFF)
|
|
return;
|
|
buf = 0xFFFFFFFF;
|
|
window = 0;
|
|
drop_next = 0;
|
|
Uart.synced = true;
|
|
}
|
|
|
|
buf <<= 8;
|
|
buf |= byte;
|
|
|
|
if (drop_next) {
|
|
drop_next = 0;
|
|
return;
|
|
}
|
|
|
|
again:
|
|
falling = ~buf & ((buf >> 1) ^ buf) & (0xFF << window);
|
|
|
|
uart_bit(!falling);
|
|
|
|
if (!falling)
|
|
return;
|
|
|
|
lz = __builtin_clz(falling) - 24 + window;
|
|
|
|
// aim to get falling edge on fourth-leftmost bit of window
|
|
window += 3 - lz;
|
|
|
|
if (window < 0) {
|
|
window += 8;
|
|
drop_next = 1;
|
|
} else if (window >= 8) {
|
|
window -= 8;
|
|
goto again;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
static void UartReset(){
|
|
Uart.state = STATE_UNSYNCD;
|
|
Uart.shiftReg = 0;
|
|
Uart.bitCnt = 0;
|
|
Uart.byteCnt = 0;
|
|
Uart.posCnt = 0;
|
|
Uart.nOutOfCnt = 0;
|
|
Uart.OutOfCnt = 0;
|
|
Uart.syncBit = 0;
|
|
Uart.samples = 0;
|
|
Uart.highCnt = 0;
|
|
Uart.swapper = 0;
|
|
Uart.counter = 0;
|
|
Uart.bitBuffer = 0;
|
|
Uart.dropPosition = 0;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* READER TO CARD
|
|
* 1 out of 4 Decoding
|
|
* 1 out of 256 Decoding
|
|
*/
|
|
/*
|
|
static RAMFUNC int OutOfNDecoding(int bit) {
|
|
//int error = 0;
|
|
int bitright;
|
|
|
|
if (!Uart.bitBuffer) {
|
|
Uart.bitBuffer = bit ^ 0xFF0;
|
|
return false;
|
|
} else {
|
|
Uart.bitBuffer <<= 4;
|
|
Uart.bitBuffer ^= bit;
|
|
}
|
|
|
|
// if (Uart.swapper) {
|
|
// Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF;
|
|
// Uart.byteCnt++;
|
|
// Uart.swapper = 0;
|
|
// if (Uart.byteCnt > 15) return true;
|
|
// }
|
|
// else {
|
|
// Uart.swapper = 1;
|
|
// }
|
|
|
|
if (Uart.state != STATE_UNSYNCD) {
|
|
Uart.posCnt++;
|
|
|
|
if ((Uart.bitBuffer & Uart.syncBit) ^ Uart.syncBit)
|
|
bit = 0;
|
|
else
|
|
bit = 1;
|
|
|
|
if (((Uart.bitBuffer << 1) & Uart.syncBit) ^ Uart.syncBit)
|
|
bitright = 0;
|
|
else
|
|
bitright = 1;
|
|
|
|
if(bit != bitright)
|
|
bit = bitright;
|
|
|
|
|
|
// So, now we only have to deal with *bit*, lets see...
|
|
if (Uart.posCnt == 1) {
|
|
// measurement first half bitperiod
|
|
if (!bit) {
|
|
// Drop in first half means that we are either seeing
|
|
// an SOF or an EOF.
|
|
|
|
if (Uart.nOutOfCnt == 1) {
|
|
// End of Communication
|
|
Uart.state = STATE_UNSYNCD;
|
|
Uart.highCnt = 0;
|
|
if (Uart.byteCnt == 0) {
|
|
// Its not straightforward to show single EOFs
|
|
// So just leave it and do not return TRUE
|
|
Uart.output[0] = 0xf0;
|
|
Uart.byteCnt++;
|
|
} else {
|
|
return true;
|
|
}
|
|
} else if (Uart.state != STATE_START_OF_COMMUNICATION) {
|
|
// When not part of SOF or EOF, it is an error
|
|
Uart.state = STATE_UNSYNCD;
|
|
Uart.highCnt = 0;
|
|
//error = 4;
|
|
}
|
|
}
|
|
} else {
|
|
// measurement second half bitperiod
|
|
// Count the bitslot we are in... (ISO 15693)
|
|
Uart.nOutOfCnt++;
|
|
|
|
if (!bit) {
|
|
if (Uart.dropPosition) {
|
|
if (Uart.state == STATE_START_OF_COMMUNICATION) {
|
|
//error = 1;
|
|
} else {
|
|
//error = 7;
|
|
}
|
|
// It is an error if we already have seen a drop in current frame
|
|
Uart.state = STATE_UNSYNCD;
|
|
Uart.highCnt = 0;
|
|
} else {
|
|
Uart.dropPosition = Uart.nOutOfCnt;
|
|
}
|
|
}
|
|
Uart.posCnt = 0;
|
|
|
|
if (Uart.nOutOfCnt == Uart.OutOfCnt && Uart.OutOfCnt == 4) {
|
|
Uart.nOutOfCnt = 0;
|
|
|
|
if (Uart.state == STATE_START_OF_COMMUNICATION) {
|
|
if (Uart.dropPosition == 4) {
|
|
Uart.state = STATE_RECEIVING;
|
|
Uart.OutOfCnt = 256;
|
|
} else if (Uart.dropPosition == 3) {
|
|
Uart.state = STATE_RECEIVING;
|
|
Uart.OutOfCnt = 4;
|
|
//Uart.output[Uart.byteCnt] = 0xdd;
|
|
//Uart.byteCnt++;
|
|
} else {
|
|
Uart.state = STATE_UNSYNCD;
|
|
Uart.highCnt = 0;
|
|
}
|
|
Uart.dropPosition = 0;
|
|
} else {
|
|
// RECEIVING DATA
|
|
// 1 out of 4
|
|
if (!Uart.dropPosition) {
|
|
Uart.state = STATE_UNSYNCD;
|
|
Uart.highCnt = 0;
|
|
//error = 9;
|
|
} else {
|
|
Uart.shiftReg >>= 2;
|
|
|
|
// Swap bit order
|
|
Uart.dropPosition--;
|
|
//if(Uart.dropPosition == 1) { Uart.dropPosition = 2; }
|
|
//else if(Uart.dropPosition == 2) { Uart.dropPosition = 1; }
|
|
|
|
Uart.shiftReg ^= ((Uart.dropPosition & 0x03) << 6);
|
|
Uart.bitCnt += 2;
|
|
Uart.dropPosition = 0;
|
|
|
|
if (Uart.bitCnt == 8) {
|
|
Uart.output[Uart.byteCnt] = (Uart.shiftReg & 0xff);
|
|
Uart.byteCnt++;
|
|
Uart.bitCnt = 0;
|
|
Uart.shiftReg = 0;
|
|
}
|
|
}
|
|
}
|
|
} else if (Uart.nOutOfCnt == Uart.OutOfCnt) {
|
|
// RECEIVING DATA
|
|
// 1 out of 256
|
|
if (!Uart.dropPosition) {
|
|
Uart.state = STATE_UNSYNCD;
|
|
Uart.highCnt = 0;
|
|
//error = 3;
|
|
} else {
|
|
Uart.dropPosition--;
|
|
Uart.output[Uart.byteCnt] = (Uart.dropPosition & 0xff);
|
|
Uart.byteCnt++;
|
|
Uart.bitCnt = 0;
|
|
Uart.shiftReg = 0;
|
|
Uart.nOutOfCnt = 0;
|
|
Uart.dropPosition = 0;
|
|
}
|
|
}
|
|
*/
|
|
/*if (error) {
|
|
Uart.output[Uart.byteCnt] = 0xAA;
|
|
Uart.byteCnt++;
|
|
Uart.output[Uart.byteCnt] = error & 0xFF;
|
|
Uart.byteCnt++;
|
|
Uart.output[Uart.byteCnt] = 0xAA;
|
|
Uart.byteCnt++;
|
|
Uart.output[Uart.byteCnt] = (Uart.bitBuffer >> 8) & 0xFF;
|
|
Uart.byteCnt++;
|
|
Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF;
|
|
Uart.byteCnt++;
|
|
Uart.output[Uart.byteCnt] = (Uart.syncBit >> 3) & 0xFF;
|
|
Uart.byteCnt++;
|
|
Uart.output[Uart.byteCnt] = 0xAA;
|
|
Uart.byteCnt++;
|
|
return true;
|
|
}*/
|
|
/*
|
|
}
|
|
} else {
|
|
bit = Uart.bitBuffer & 0xf0;
|
|
bit >>= 4;
|
|
bit ^= 0x0F; // drops become 1s ;-)
|
|
if (bit) {
|
|
// should have been high or at least (4 * 128) / fc
|
|
// according to ISO this should be at least (9 * 128 + 20) / fc
|
|
if (Uart.highCnt == 8) {
|
|
// we went low, so this could be start of communication
|
|
// it turns out to be safer to choose a less significant
|
|
// syncbit... so we check whether the neighbour also represents the drop
|
|
Uart.posCnt = 1; // apparently we are busy with our first half bit period
|
|
Uart.syncBit = bit & 8;
|
|
Uart.samples = 3;
|
|
|
|
if (!Uart.syncBit) { Uart.syncBit = bit & 4; Uart.samples = 2; }
|
|
else if (bit & 4) { Uart.syncBit = bit & 4; Uart.samples = 2; bit <<= 2; }
|
|
|
|
if (!Uart.syncBit) { Uart.syncBit = bit & 2; Uart.samples = 1; }
|
|
else if (bit & 2) { Uart.syncBit = bit & 2; Uart.samples = 1; bit <<= 1; }
|
|
|
|
if (!Uart.syncBit) { Uart.syncBit = bit & 1; Uart.samples = 0;
|
|
if (Uart.syncBit && (Uart.bitBuffer & 8)) {
|
|
Uart.syncBit = 8;
|
|
|
|
// the first half bit period is expected in next sample
|
|
Uart.posCnt = 0;
|
|
Uart.samples = 3;
|
|
}
|
|
} else if (bit & 1) { Uart.syncBit = bit & 1; Uart.samples = 0; }
|
|
|
|
Uart.syncBit <<= 4;
|
|
Uart.state = STATE_START_OF_COMMUNICATION;
|
|
Uart.bitCnt = 0;
|
|
Uart.byteCnt = 0;
|
|
Uart.nOutOfCnt = 0;
|
|
Uart.OutOfCnt = 4; // Start at 1/4, could switch to 1/256
|
|
Uart.dropPosition = 0;
|
|
Uart.shiftReg = 0;
|
|
//error = 0;
|
|
} else {
|
|
Uart.highCnt = 0;
|
|
}
|
|
} else {
|
|
if (Uart.highCnt < 8)
|
|
Uart.highCnt++;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
*/
|
|
//=============================================================================
|
|
// Manchester
|
|
//=============================================================================
|
|
static tDemodIc Demod;
|
|
static void DemodIcReset() {
|
|
Demod.bitCount = 0;
|
|
Demod.posCount = 0;
|
|
Demod.syncBit = 0;
|
|
Demod.shiftReg = 0;
|
|
Demod.buffer = 0;
|
|
Demod.buffer2 = 0;
|
|
Demod.buffer3 = 0;
|
|
Demod.buff = 0;
|
|
Demod.samples = 0;
|
|
Demod.len = 0;
|
|
Demod.sub = SUB_NONE;
|
|
Demod.state = DEMOD_IC_UNSYNCD;
|
|
}
|
|
static void DemodIcInit(uint8_t *data) {
|
|
Demod.output = data;
|
|
DemodIcReset();
|
|
}
|
|
|
|
// UART debug
|
|
// it adds the debug values which will be put in the tracelog,
|
|
// visible on client when running 'hf list iclass'
|
|
/*
|
|
pm3 --> hf li iclass
|
|
Recorded Activity (TraceLen = 162 bytes)
|
|
Start | End | Src | Data (! denotes parity error) | CRC | Annotation |
|
|
------------|------------|-----|-----------------------------------------------------------------|-----|--------------------|
|
|
0 | 0 | Rdr |0a | | ACTALL
|
|
1280 | 1280 | Tag |bb! 33! bb! 01 02 04 08 bb! | ok |
|
|
1280 | 1280 | Rdr |0c | | IDENTIFY
|
|
1616 | 1616 | Tag |bb! 33! bb! 00! 02 00! 02 bb! | ok |
|
|
1616 | 1616 | Rdr |0a | | ACTALL
|
|
2336 | 2336 | Tag |bb! d4! bb! 02 08 00! 08 bb! | ok |
|
|
2336 | 2336 | Rdr |0c | | IDENTIFY
|
|
2448 | 2448 | Tag |bb! 33! bb! 00! 00! 00! 02 bb! | ok |
|
|
2448 | 2448 | Rdr |0a | | ACTALL
|
|
2720 | 2720 | Tag |bb! d4! bb! 08 0b 01 04 bb! | ok |
|
|
2720 | 2720 | Rdr |0c | | IDENTIFY
|
|
3232 | 3232 | Tag |bb! d4! bb! 02 02 08 04 bb! | ok |
|
|
*/
|
|
static void uart_debug(int error, int bit) {
|
|
Demod.output[Demod.len] = 0xBB;
|
|
Demod.len++;
|
|
Demod.output[Demod.len] = error & 0xFF;
|
|
Demod.len++;
|
|
Demod.output[Demod.len] = 0xBB;
|
|
Demod.len++;
|
|
Demod.output[Demod.len] = bit & 0xFF;
|
|
Demod.len++;
|
|
Demod.output[Demod.len] = Demod.buffer & 0xFF;
|
|
Demod.len++;
|
|
// Look harder ;-)
|
|
Demod.output[Demod.len] = Demod.buffer2 & 0xFF;
|
|
Demod.len++;
|
|
Demod.output[Demod.len] = Demod.syncBit & 0xFF;
|
|
Demod.len++;
|
|
Demod.output[Demod.len] = 0xBB;
|
|
Demod.len++;
|
|
}
|
|
|
|
/*
|
|
* CARD TO READER
|
|
* in ISO15693-2 mode - Manchester
|
|
* in ISO 14443b - BPSK coding
|
|
*
|
|
* Timings:
|
|
* ISO 15693-2
|
|
* Tout = 330 µs, Tprog 1 = 4 to 15 ms, Tslot = 330 µs + (number of slots x 160 µs)
|
|
* ISO 14443a
|
|
* Tout = 100 µs, Tprog = 4 to 15 ms, Tslot = 100 µs+ (number of slots x 80 µs)
|
|
* ISO 14443b
|
|
Tout = 76 µs, Tprog = 4 to 15 ms, Tslot = 119 µs+ (number of slots x 150 µs)
|
|
*
|
|
*
|
|
* So for current implementation in ISO15693, its 330 µs from end of reader, to start of card.
|
|
*/
|
|
static RAMFUNC int ManchesterDecoding_iclass(uint32_t v) {
|
|
int bit;
|
|
int modulation;
|
|
int error = 0;
|
|
|
|
bit = Demod.buffer;
|
|
Demod.buffer = Demod.buffer2;
|
|
Demod.buffer2 = Demod.buffer3;
|
|
Demod.buffer3 = v;
|
|
|
|
// too few bits?
|
|
if (Demod.buff < 3) {
|
|
Demod.buff++;
|
|
return false;
|
|
}
|
|
|
|
if (Demod.state == DEMOD_IC_UNSYNCD) {
|
|
Demod.output[Demod.len] = 0xfa;
|
|
Demod.syncBit = 0;
|
|
//Demod.samples = 0;
|
|
Demod.posCount = 1; // This is the first half bit period, so after syncing handle the second part
|
|
|
|
if (bit & 0x08)
|
|
Demod.syncBit = 0x08;
|
|
|
|
if (bit & 0x04) {
|
|
if (Demod.syncBit)
|
|
bit <<= 4;
|
|
|
|
Demod.syncBit = 0x04;
|
|
}
|
|
|
|
if (bit & 0x02) {
|
|
if (Demod.syncBit)
|
|
bit <<= 2;
|
|
|
|
Demod.syncBit = 0x02;
|
|
}
|
|
|
|
if (bit & 0x01 && Demod.syncBit)
|
|
Demod.syncBit = 0x01;
|
|
|
|
if (Demod.syncBit) {
|
|
Demod.len = 0;
|
|
Demod.state = DEMOD_IC_START_OF_COMMUNICATION;
|
|
Demod.sub = SUB_FIRST_HALF;
|
|
Demod.bitCount = 0;
|
|
Demod.shiftReg = 0;
|
|
Demod.samples = 0;
|
|
|
|
if (Demod.posCount) {
|
|
|
|
switch (Demod.syncBit) {
|
|
case 0x08:
|
|
Demod.samples = 3;
|
|
break;
|
|
case 0x04:
|
|
Demod.samples = 2;
|
|
break;
|
|
case 0x02:
|
|
Demod.samples = 1;
|
|
break;
|
|
case 0x01:
|
|
Demod.samples = 0;
|
|
break;
|
|
}
|
|
// SOF must be long burst... otherwise stay unsynced!!!
|
|
if (!(Demod.buffer & Demod.syncBit) || !(Demod.buffer2 & Demod.syncBit))
|
|
Demod.state = DEMOD_IC_UNSYNCD;
|
|
|
|
} else {
|
|
// SOF must be long burst... otherwise stay unsynced!!!
|
|
if (!(Demod.buffer2 & Demod.syncBit) || !(Demod.buffer3 & Demod.syncBit)) {
|
|
Demod.state = DEMOD_IC_UNSYNCD;
|
|
error = 0x88;
|
|
uart_debug(error, bit);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// state is DEMOD is in SYNC from here on.
|
|
|
|
modulation = bit & Demod.syncBit;
|
|
modulation |= ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit;
|
|
Demod.samples += 4;
|
|
|
|
if (Demod.posCount == 0) {
|
|
Demod.posCount = 1;
|
|
Demod.sub = (modulation) ? SUB_FIRST_HALF : SUB_NONE;
|
|
return false;
|
|
}
|
|
|
|
Demod.posCount = 0;
|
|
|
|
if (modulation) {
|
|
|
|
if (Demod.sub == SUB_FIRST_HALF)
|
|
Demod.sub = SUB_BOTH;
|
|
else
|
|
Demod.sub = SUB_SECOND_HALF;
|
|
}
|
|
|
|
if (Demod.sub == SUB_NONE) {
|
|
if (Demod.state == DEMOD_IC_SOF_COMPLETE) {
|
|
Demod.output[Demod.len] = 0x0f;
|
|
Demod.len++;
|
|
Demod.state = DEMOD_IC_UNSYNCD;
|
|
return true;
|
|
} else {
|
|
Demod.state = DEMOD_IC_ERROR_WAIT;
|
|
error = 0x33;
|
|
}
|
|
}
|
|
|
|
switch (Demod.state) {
|
|
|
|
case DEMOD_IC_START_OF_COMMUNICATION:
|
|
if (Demod.sub == SUB_BOTH) {
|
|
|
|
Demod.state = DEMOD_IC_START_OF_COMMUNICATION2;
|
|
Demod.posCount = 1;
|
|
Demod.sub = SUB_NONE;
|
|
} else {
|
|
Demod.output[Demod.len] = 0xab;
|
|
Demod.state = DEMOD_IC_ERROR_WAIT;
|
|
error = 0xd2;
|
|
}
|
|
break;
|
|
|
|
case DEMOD_IC_START_OF_COMMUNICATION2:
|
|
if (Demod.sub == SUB_SECOND_HALF) {
|
|
Demod.state = DEMOD_IC_START_OF_COMMUNICATION3;
|
|
} else {
|
|
Demod.output[Demod.len] = 0xab;
|
|
Demod.state = DEMOD_IC_ERROR_WAIT;
|
|
error = 0xd3;
|
|
}
|
|
break;
|
|
|
|
case DEMOD_IC_START_OF_COMMUNICATION3:
|
|
if (Demod.sub == SUB_SECOND_HALF) {
|
|
Demod.state = DEMOD_IC_SOF_COMPLETE;
|
|
} else {
|
|
Demod.output[Demod.len] = 0xab;
|
|
Demod.state = DEMOD_IC_ERROR_WAIT;
|
|
error = 0xd4;
|
|
}
|
|
break;
|
|
|
|
case DEMOD_IC_SOF_COMPLETE:
|
|
case DEMOD_IC_MANCHESTER_D:
|
|
case DEMOD_IC_MANCHESTER_E:
|
|
// OPPOSITE FROM ISO14443 - 11110000 = 0 (1 in 14443)
|
|
// 00001111 = 1 (0 in 14443)
|
|
if (Demod.sub == SUB_SECOND_HALF) { // SUB_FIRST_HALF
|
|
Demod.bitCount++;
|
|
Demod.shiftReg = (Demod.shiftReg >> 1) ^ 0x100;
|
|
Demod.state = DEMOD_IC_MANCHESTER_D;
|
|
} else if (Demod.sub == SUB_FIRST_HALF) { // SUB_SECOND_HALF
|
|
Demod.bitCount++;
|
|
Demod.shiftReg >>= 1;
|
|
Demod.state = DEMOD_IC_MANCHESTER_E;
|
|
} else if (Demod.sub == SUB_BOTH) {
|
|
Demod.state = DEMOD_IC_MANCHESTER_F;
|
|
} else {
|
|
Demod.state = DEMOD_IC_ERROR_WAIT;
|
|
error = 0x55;
|
|
}
|
|
break;
|
|
|
|
case DEMOD_IC_MANCHESTER_F:
|
|
// Tag response does not need to be a complete byte!
|
|
if (Demod.len > 0 || Demod.bitCount > 0) {
|
|
if (Demod.bitCount > 1) { // was > 0, do not interpret last closing bit, is part of EOF
|
|
Demod.shiftReg >>= (9 - Demod.bitCount); // right align data
|
|
Demod.output[Demod.len] = Demod.shiftReg & 0xff;
|
|
Demod.len++;
|
|
}
|
|
|
|
Demod.state = DEMOD_IC_UNSYNCD;
|
|
return true;
|
|
} else {
|
|
Demod.output[Demod.len] = 0xad;
|
|
Demod.state = DEMOD_IC_ERROR_WAIT;
|
|
error = 0x03;
|
|
}
|
|
break;
|
|
|
|
case DEMOD_IC_ERROR_WAIT:
|
|
Demod.state = DEMOD_IC_UNSYNCD;
|
|
break;
|
|
|
|
default:
|
|
Demod.output[Demod.len] = 0xdd;
|
|
Demod.state = DEMOD_IC_UNSYNCD;
|
|
break;
|
|
}
|
|
|
|
if (Demod.bitCount >= 8) {
|
|
Demod.shiftReg >>= 1;
|
|
Demod.output[Demod.len] = (Demod.shiftReg & 0xff);
|
|
Demod.len++;
|
|
Demod.bitCount = 0;
|
|
Demod.shiftReg = 0;
|
|
}
|
|
|
|
if (error) {
|
|
uart_debug(error, bit);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================
|
|
// Finally, a `sniffer' for iClass communication
|
|
// Both sides of communication!
|
|
//=============================================================================
|
|
static void iclass_setup_sniff(void) {
|
|
if (DBGLEVEL > 3) Dbprintf("iclass_setup_sniff Enter");
|
|
|
|
LEDsoff();
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
|
|
// connect Demodulated Signal to ADC:
|
|
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
|
|
|
|
// Set up the synchronous serial port
|
|
FpgaSetupSsc();
|
|
|
|
BigBuf_free();
|
|
BigBuf_Clear_ext(false);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
// Initialize Demod and Uart structs
|
|
DemodIcInit(BigBuf_malloc(ICLASS_BUFFER_SIZE));
|
|
|
|
uart_init(BigBuf_malloc(ICLASS_BUFFER_SIZE));
|
|
//UartIcInit(BigBuf_malloc(ICLASS_BUFFER_SIZE));
|
|
|
|
if (DBGLEVEL > 1) {
|
|
// Print debug information about the buffer sizes
|
|
Dbprintf("[+] Sniffing buffers initialized:");
|
|
Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen());
|
|
Dbprintf(" Reader -> tag: %i bytes", ICLASS_BUFFER_SIZE);
|
|
Dbprintf(" tag -> Reader: %i bytes", ICLASS_BUFFER_SIZE);
|
|
Dbprintf(" DMA: %i bytes", ICLASS_DMA_BUFFER_SIZE);
|
|
}
|
|
|
|
// Set FPGA in the appropriate mode
|
|
// put the FPGA in the appropriate mode
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER);
|
|
SpinDelay(200);
|
|
|
|
// Start the SSP timer
|
|
StartCountSspClk();
|
|
|
|
LED_A_ON();
|
|
if (DBGLEVEL > 3) Dbprintf("[+] iclass_setup_sniff Exit");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Record the sequence of commands sent by the reader to the tag, with
|
|
// triggering so that we start recording at the point that the tag is moved
|
|
// near the reader.
|
|
//-----------------------------------------------------------------------------
|
|
// turn off afterwards
|
|
void RAMFUNC SniffIClass(void) {
|
|
|
|
//int datalen = 0;
|
|
uint32_t previous_data = 0;
|
|
uint32_t time_0 = 0, time_start = 0, time_stop;
|
|
uint32_t sniffCounter = 0;
|
|
bool TagIsActive = false;
|
|
bool ReaderIsActive = false;
|
|
|
|
iclass_setup_sniff();
|
|
|
|
// The DMA buffer, used to stream samples from the FPGA
|
|
// *dmaBuf is the start reference.
|
|
uint8_t *dmaBuf = BigBuf_malloc(ICLASS_DMA_BUFFER_SIZE);
|
|
// pointer to samples from fpga
|
|
uint8_t *data = dmaBuf;
|
|
|
|
// Setup and start DMA.
|
|
if (!FpgaSetupSscDma(dmaBuf, ICLASS_DMA_BUFFER_SIZE)) {
|
|
if (DBGLEVEL > 1) DbpString("[-] FpgaSetupSscDma failed. Exiting");
|
|
return;
|
|
}
|
|
|
|
// time ZERO, the point from which it all is calculated.
|
|
time_0 = GetCountSspClk();
|
|
|
|
int divi = 0;
|
|
uint8_t tag_byte = 0, foo = 0;
|
|
// loop and listen
|
|
// every sample (1byte in data),
|
|
// contains HIGH nibble = reader data
|
|
// contains LOW nibble = tag data
|
|
// so two bytes are needed in order to get 1byte of either reader or tag data. (ie 2 sample bytes)
|
|
// since reader data is manchester encoded, we need 2bytes of data in order to get one demoded byte. (ie: 4 sample bytes)
|
|
uint16_t checked = 0;
|
|
for (;;) {
|
|
WDT_HIT();
|
|
|
|
if (checked == 1000) {
|
|
if (BUTTON_PRESS() || data_available()) break;
|
|
checked = 0;
|
|
} else {
|
|
checked++;
|
|
}
|
|
|
|
previous_data <<= 8;
|
|
previous_data |= *data;
|
|
|
|
sniffCounter++;
|
|
data++;
|
|
|
|
if (data == dmaBuf + ICLASS_DMA_BUFFER_SIZE) {
|
|
data = dmaBuf;
|
|
AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf;
|
|
AT91C_BASE_PDC_SSC->PDC_RNCR = ICLASS_DMA_BUFFER_SIZE;
|
|
}
|
|
|
|
if (*data & 0xF) {
|
|
//tag_byte <<= 1;
|
|
tag_byte ^= (1 << 4);
|
|
foo ^= (1 << (3 - divi));
|
|
Dbprintf(" %d|%x == %d|%x", tag_byte, tag_byte, foo, foo);
|
|
}
|
|
divi++;
|
|
|
|
// every odd sample
|
|
if (sniffCounter & 0x01) {
|
|
// no need to try decoding reader data if the tag is sending
|
|
// READER TO CARD
|
|
if (!TagIsActive) {
|
|
LED_C_INV();
|
|
// HIGH nibble is always reader data.
|
|
uint8_t reader_byte = (previous_data & 0xF0) | (*data >> 4);
|
|
uart_samples(reader_byte);
|
|
if (Uart.frame_done) {
|
|
time_stop = GetCountSspClk() - time_0;
|
|
LogTrace(Uart.buf, Uart.len, time_start, time_stop, NULL, true);
|
|
DemodIcReset();
|
|
uart_reset();
|
|
} else {
|
|
time_start = GetCountSspClk() - time_0;
|
|
}
|
|
ReaderIsActive = Uart.frame_done;
|
|
}
|
|
}
|
|
// every four sample
|
|
if ((sniffCounter % 4) == 0) {
|
|
// need two samples to feed Manchester
|
|
// no need to try decoding tag data if the reader is sending - and we cannot afford the time
|
|
// CARD TO READER
|
|
if (!ReaderIsActive) {
|
|
LED_C_INV();
|
|
// LOW nibble is always tag data.
|
|
/*
|
|
|
|
|
|
uint32_t tag_byte =
|
|
((previous_data & 0x0F000000) >> 8 ) |
|
|
((previous_data & 0x000F0000) >> 4 ) |
|
|
((previous_data & 0x00000F00) ) |
|
|
((previous_data & 0x0000000F) << 4 ) |
|
|
(*data & 0xF);
|
|
*/
|
|
|
|
|
|
//uint8_t tag_byte = ((previous_data & 0xF) << 4 ) | (*data & 0xF);
|
|
if (ManchesterDecoding_iclass(foo)) {
|
|
time_stop = GetCountSspClk() - time_0;
|
|
LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false);
|
|
DemodIcReset();
|
|
uart_reset();
|
|
} else {
|
|
time_start = GetCountSspClk() - time_0;
|
|
}
|
|
TagIsActive = (Demod.state != DEMOD_IC_UNSYNCD);
|
|
}
|
|
tag_byte = 0;
|
|
foo = 0;
|
|
divi = 0;
|
|
}
|
|
} // end main loop
|
|
|
|
if (DBGLEVEL >= 1) {
|
|
DbpString("[+] Sniff statistics:");
|
|
Dbhexdump(ICLASS_DMA_BUFFER_SIZE, data, false);
|
|
}
|
|
|
|
switch_off();
|
|
}
|
|
|
|
void rotateCSN(uint8_t *originalCSN, uint8_t *rotatedCSN) {
|
|
int i;
|
|
for (i = 0; i < 8; i++)
|
|
rotatedCSN[i] = (originalCSN[i] >> 3) | (originalCSN[(i + 1) % 8] << 5);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// SIMULATION
|
|
// Wait for commands from reader
|
|
// Stop when button is pressed
|
|
// Or return TRUE when command is captured
|
|
//-----------------------------------------------------------------------------
|
|
static bool GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) {
|
|
// Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen
|
|
// only, since we are receiving, not transmitting).
|
|
// Signal field is off with the appropriate LED
|
|
LED_D_OFF();
|
|
uart_init(received);
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN);
|
|
// clear RXRDY:
|
|
uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
|
|
(void)b;
|
|
|
|
uint16_t checked = 0;
|
|
for (;;) {
|
|
|
|
WDT_HIT();
|
|
|
|
if (checked == 1000) {
|
|
if (BUTTON_PRESS() || data_available()) return false;
|
|
checked = 0;
|
|
} else {
|
|
checked++;
|
|
}
|
|
|
|
// keep tx buffer in a defined state anyway.
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))
|
|
AT91C_BASE_SSC->SSC_THR = 0x00;
|
|
|
|
// wait for byte to become available in rx holding register
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
|
b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
|
|
|
|
uart_samples(b);
|
|
if (Uart.frame_done) {
|
|
*len = Uart.len;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static uint8_t encode4Bits(const uint8_t b) {
|
|
// OTA, the least significant bits first
|
|
// Manchester encoding added
|
|
// The columns are
|
|
// 1 - Bit value to send
|
|
// 2 - Reversed (big-endian)
|
|
// 3 - Machester Encoded
|
|
// 4 - Hex values
|
|
|
|
uint8_t c = b & 0xF;
|
|
switch (c) {
|
|
// 1 2 3 4
|
|
case 15:
|
|
return 0x55; // 1111 -> 1111 -> 01010101 -> 0x55
|
|
case 14:
|
|
return 0x95; // 1110 -> 0111 -> 10010101 -> 0x95
|
|
case 13:
|
|
return 0x65; // 1101 -> 1011 -> 01100101 -> 0x65
|
|
case 12:
|
|
return 0xa5; // 1100 -> 0011 -> 10100101 -> 0xa5
|
|
case 11:
|
|
return 0x59; // 1011 -> 1101 -> 01011001 -> 0x59
|
|
case 10:
|
|
return 0x99; // 1010 -> 0101 -> 10011001 -> 0x99
|
|
case 9:
|
|
return 0x69; // 1001 -> 1001 -> 01101001 -> 0x69
|
|
case 8:
|
|
return 0xa9; // 1000 -> 0001 -> 10101001 -> 0xa9
|
|
case 7:
|
|
return 0x56; // 0111 -> 1110 -> 01010110 -> 0x56
|
|
case 6:
|
|
return 0x96; // 0110 -> 0110 -> 10010110 -> 0x96
|
|
case 5:
|
|
return 0x66; // 0101 -> 1010 -> 01100110 -> 0x66
|
|
case 4:
|
|
return 0xa6; // 0100 -> 0010 -> 10100110 -> 0xa6
|
|
case 3:
|
|
return 0x5a; // 0011 -> 1100 -> 01011010 -> 0x5a
|
|
case 2:
|
|
return 0x9a; // 0010 -> 0100 -> 10011010 -> 0x9a
|
|
case 1:
|
|
return 0x6a; // 0001 -> 1000 -> 01101010 -> 0x6a
|
|
default:
|
|
return 0xaa; // 0000 -> 0000 -> 10101010 -> 0xaa
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Prepare tag messages
|
|
//-----------------------------------------------------------------------------
|
|
static void CodeIClassTagAnswer(const uint8_t *cmd, int len) {
|
|
/*
|
|
* SOF comprises 3 parts;
|
|
* * An unmodulated time of 56.64 us
|
|
* * 24 pulses of 423.75 kHz (fc/32)
|
|
* * A logic 1, which starts with an unmodulated time of 18.88us
|
|
* followed by 8 pulses of 423.75kHz (fc/32)
|
|
*
|
|
*
|
|
* EOF comprises 3 parts:
|
|
* - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated
|
|
* time of 18.88us.
|
|
* - 24 pulses of fc/32
|
|
* - An unmodulated time of 56.64 us
|
|
*
|
|
*
|
|
* A logic 0 starts with 8 pulses of fc/32
|
|
* followed by an unmodulated time of 256/fc (~18,88us).
|
|
*
|
|
* A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by
|
|
* 8 pulses of fc/32 (also 18.88us)
|
|
*
|
|
* The mode FPGA_HF_SIMULATOR_MODULATE_424K_8BIT which we use to simulate tag,
|
|
* works like this.
|
|
* - A 1-bit input to the FPGA becomes 8 pulses on 423.5kHz (fc/32) (18.88us).
|
|
* - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us
|
|
*
|
|
* In this mode
|
|
* SOF can be written as 00011101 = 0x1D
|
|
* EOF can be written as 10111000 = 0xb8
|
|
* logic 1 be written as 01 = 0x1
|
|
* logic 0 be written as 10 = 0x2
|
|
*
|
|
* */
|
|
ToSendReset();
|
|
|
|
// Send SOF
|
|
ToSend[++ToSendMax] = 0x1D;
|
|
|
|
int i;
|
|
for (i = 0; i < len; i++) {
|
|
uint8_t b = cmd[i];
|
|
ToSend[++ToSendMax] = encode4Bits(b & 0xF); // least significant half
|
|
ToSend[++ToSendMax] = encode4Bits((b >> 4) & 0xF); // most significant half
|
|
}
|
|
|
|
// Send EOF
|
|
ToSend[++ToSendMax] = 0xB8;
|
|
//lastProxToAirDuration = 8*ToSendMax - 3*8 - 3*8;//Not counting zeroes in the beginning or end
|
|
// Convert from last byte pos to length
|
|
ToSendMax++;
|
|
}
|
|
|
|
// Only SOF
|
|
static void CodeIClassTagSOF() {
|
|
//So far a dummy implementation, not used
|
|
//int lastProxToAirDuration =0;
|
|
|
|
ToSendReset();
|
|
// Send SOF
|
|
ToSend[++ToSendMax] = 0x1D;
|
|
// lastProxToAirDuration = 8*ToSendMax - 3*8;//Not counting zeroes in the beginning
|
|
|
|
// Convert from last byte pos to length
|
|
ToSendMax++;
|
|
}
|
|
|
|
/**
|
|
* @brief SimulateIClass simulates an iClass card.
|
|
* @param arg0 type of simulation
|
|
* - 0 uses the first 8 bytes in usb data as CSN
|
|
* - 2 "dismantling iclass"-attack. This mode iterates through all CSN's specified
|
|
* in the usb data. This mode collects MAC from the reader, in order to do an offline
|
|
* attack on the keys. For more info, see "dismantling iclass" and proxclone.com.
|
|
* - Other : Uses the default CSN (031fec8af7ff12e0)
|
|
* @param arg1 - number of CSN's contained in datain (applicable for mode 2 only)
|
|
* @param arg2
|
|
* @param datain
|
|
*/
|
|
// turn off afterwards
|
|
void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) {
|
|
|
|
if (DBGLEVEL > 3) Dbprintf("[+] iClass_simulate Enter");
|
|
|
|
LEDsoff();
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
// this will clear out bigbuf memory, the eload command must select this before!
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
FpgaSetupSsc();
|
|
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
|
|
|
|
// Enable and clear the trace
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
uint32_t simType = arg0;
|
|
uint32_t numberOfCSNS = arg1;
|
|
|
|
//Use the emulator memory for SIM
|
|
uint8_t *emulator = BigBuf_get_EM_addr();
|
|
uint8_t mac_responses[PM3_CMD_DATA_SIZE] = { 0 };
|
|
|
|
if (simType == 0) {
|
|
// Use the CSN from commandline
|
|
memcpy(emulator, datain, 8);
|
|
doIClassSimulation(MODE_SIM_CSN, NULL);
|
|
} else if (simType == 1) {
|
|
//Default CSN
|
|
uint8_t csn_crc[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0, 0x00, 0x00 };
|
|
// Use the CSN from commandline
|
|
memcpy(emulator, csn_crc, 8);
|
|
doIClassSimulation(MODE_SIM_CSN, NULL);
|
|
} else if (simType == 2) {
|
|
|
|
Dbprintf("[+] going into attack mode, %d CSNS sent", numberOfCSNS);
|
|
// In this mode, a number of csns are within datain. We'll simulate each one, one at a time
|
|
// in order to collect MAC's from the reader. This can later be used in an offlne-attack
|
|
// in order to obtain the keys, as in the "dismantling iclass"-paper.
|
|
#define EPURSE_MAC_SIZE 16
|
|
int i = 0;
|
|
for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) {
|
|
// The usb data is 512 bytes, fitting 65 8-byte CSNs in there.
|
|
|
|
memcpy(emulator, datain + (i * 8), 8);
|
|
|
|
if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) {
|
|
// Button pressed
|
|
reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE);
|
|
goto out;
|
|
}
|
|
}
|
|
reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE);
|
|
|
|
} else if (simType == 3) {
|
|
//This is 'full sim' mode, where we use the emulator storage for data.
|
|
//ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command
|
|
doIClassSimulation(MODE_FULLSIM, NULL);
|
|
} else if (simType == 4) {
|
|
|
|
// This is the KEYROLL version of sim 2.
|
|
// the collected data (mac_response) is doubled out since we are trying to collect both keys in the keyroll process.
|
|
// Keyroll iceman 9 csns * 8 * 2 = 144
|
|
// keyroll CARL55 15csns * 8 * 2 = 15 * 8 * 2 = 240
|
|
Dbprintf("[+] going into attack keyroll mode, %d CSNS sent", numberOfCSNS);
|
|
// In this mode, a number of csns are within datain. We'll simulate each one, one at a time
|
|
// in order to collect MAC's from the reader. This can later be used in an offlne-attack
|
|
// in order to obtain the keys, as in the "dismantling iclass"-paper.
|
|
|
|
// keyroll mode, reader swaps between old key and new key alternatively when fail a authentication.
|
|
// attack below is same as SIM 2, but we run the CSN twice to collected the mac for both keys.
|
|
int i = 0;
|
|
// The usb data is 512 bytes, fitting 65 8-byte CSNs in there. iceman fork uses 9 CSNS
|
|
for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) {
|
|
|
|
memcpy(emulator, datain + (i * 8), 8);
|
|
|
|
// keyroll 1
|
|
if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) {
|
|
reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2);
|
|
// Button pressed
|
|
goto out;
|
|
}
|
|
|
|
// keyroll 2
|
|
if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + (i + numberOfCSNS) * EPURSE_MAC_SIZE)) {
|
|
reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2);
|
|
// Button pressed
|
|
goto out;
|
|
}
|
|
}
|
|
// double the amount of collected data.
|
|
reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2);
|
|
|
|
} else {
|
|
// We may want a mode here where we hardcode the csns to use (from proxclone).
|
|
// That will speed things up a little, but not required just yet.
|
|
DbpString("[-] the mode is not implemented, reserved for future use");
|
|
}
|
|
|
|
out:
|
|
switch_off();
|
|
BigBuf_free_keep_EM();
|
|
}
|
|
|
|
/**
|
|
* @brief Does the actual simulation
|
|
* @param csn - csn to use
|
|
* @param breakAfterMacReceived if true, returns after reader MAC has been received.
|
|
*/
|
|
int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) {
|
|
|
|
// free eventually allocated BigBuf memory
|
|
BigBuf_free_keep_EM();
|
|
|
|
State cipher_state;
|
|
|
|
uint8_t *csn = BigBuf_get_EM_addr();
|
|
uint8_t *emulator = csn;
|
|
uint8_t sof_data[] = { 0x0F} ;
|
|
|
|
// CSN followed by two CRC bytes
|
|
uint8_t anticoll_data[10] = { 0 };
|
|
uint8_t csn_data[10] = { 0 };
|
|
memcpy(csn_data, csn, sizeof(csn_data));
|
|
|
|
// Construct anticollision-CSN
|
|
rotateCSN(csn_data, anticoll_data);
|
|
|
|
// Compute CRC on both CSNs
|
|
AddCrc(anticoll_data, 8);
|
|
AddCrc(csn_data, 8);
|
|
|
|
uint8_t diversified_key[8] = { 0 };
|
|
// e-Purse
|
|
uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
//uint8_t card_challenge_data[8] = { 0 };
|
|
if (simulationMode == MODE_FULLSIM) {
|
|
//The diversified key should be stored on block 3
|
|
//Get the diversified key from emulator memory
|
|
memcpy(diversified_key, emulator + (8 * 3), 8);
|
|
|
|
//Card challenge, a.k.a e-purse is on block 2
|
|
memcpy(card_challenge_data, emulator + (8 * 2), 8);
|
|
//Precalculate the cipher state, feeding it the CC
|
|
cipher_state = opt_doTagMAC_1(card_challenge_data, diversified_key);
|
|
}
|
|
// set epurse of sim2,4 attack
|
|
if (reader_mac_buf != NULL) {
|
|
memcpy(reader_mac_buf, card_challenge_data, 8);
|
|
}
|
|
|
|
int exitLoop = 0;
|
|
// Reader 0a
|
|
// Tag 0f
|
|
// Reader 0c
|
|
// Tag anticoll. CSN
|
|
// Reader 81 anticoll. CSN
|
|
// Tag CSN
|
|
|
|
uint8_t *modulated_response = NULL;
|
|
int modulated_response_size = 0;
|
|
uint8_t *trace_data = NULL;
|
|
int trace_data_size = 0;
|
|
|
|
// Respond SOF -- takes 1 bytes
|
|
uint8_t *resp_sof = BigBuf_malloc(2);
|
|
int resp_sof_Len;
|
|
|
|
// Anticollision CSN (rotated CSN)
|
|
// 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte)
|
|
uint8_t *resp_anticoll = BigBuf_malloc(28);
|
|
int resp_anticoll_len;
|
|
|
|
// CSN
|
|
// 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte)
|
|
uint8_t *resp_csn = BigBuf_malloc(28);
|
|
int resp_csn_len;
|
|
|
|
// configuration picopass 2ks
|
|
uint8_t *resp_conf = BigBuf_malloc(28);
|
|
int resp_conf_len;
|
|
uint8_t conf_data[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00};
|
|
AddCrc(conf_data, 8);
|
|
|
|
// e-Purse
|
|
// 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit)
|
|
uint8_t *resp_cc = BigBuf_malloc(28);
|
|
int resp_cc_len;
|
|
|
|
// Application Issuer Area
|
|
uint8_t *resp_aia = BigBuf_malloc(28);
|
|
int resp_aia_len;
|
|
uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00};
|
|
AddCrc(aia_data, 8);
|
|
|
|
// receive command
|
|
uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE);
|
|
int len = 0;
|
|
|
|
// Prepare card messages
|
|
ToSendMax = 0;
|
|
|
|
// First card answer: SOF
|
|
CodeIClassTagSOF();
|
|
memcpy(resp_sof, ToSend, ToSendMax);
|
|
resp_sof_Len = ToSendMax;
|
|
|
|
// Anticollision CSN
|
|
CodeIClassTagAnswer(anticoll_data, sizeof(anticoll_data));
|
|
memcpy(resp_anticoll, ToSend, ToSendMax);
|
|
resp_anticoll_len = ToSendMax;
|
|
|
|
// CSN
|
|
CodeIClassTagAnswer(csn_data, sizeof(csn_data));
|
|
memcpy(resp_csn, ToSend, ToSendMax);
|
|
resp_csn_len = ToSendMax;
|
|
|
|
// Configuration
|
|
CodeIClassTagAnswer(conf_data, sizeof(conf_data));
|
|
memcpy(resp_conf, ToSend, ToSendMax);
|
|
resp_conf_len = ToSendMax;
|
|
|
|
// e-Purse
|
|
CodeIClassTagAnswer(card_challenge_data, sizeof(card_challenge_data));
|
|
memcpy(resp_cc, ToSend, ToSendMax);
|
|
resp_cc_len = ToSendMax;
|
|
|
|
// Application Issuer Area
|
|
CodeIClassTagAnswer(aia_data, sizeof(aia_data));
|
|
memcpy(resp_aia, ToSend, ToSendMax);
|
|
resp_aia_len = ToSendMax;
|
|
|
|
//This is used for responding to READ-block commands or other data which is dynamically generated
|
|
//First the 'trace'-data, not encoded for FPGA
|
|
uint8_t *data_generic_trace = BigBuf_malloc((8 * 4) + 2);//8 bytes data + 2byte CRC is max tag answer
|
|
|
|
//Then storage for the modulated data
|
|
//Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes)
|
|
uint8_t *data_response = BigBuf_malloc(((8 * 4) + 2) * 2 + 2);
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN);
|
|
SpinDelay(100);
|
|
StartCountSspClk();
|
|
|
|
// To control where we are in the protocol
|
|
uint32_t time_0 = GetCountSspClk();
|
|
uint32_t t2r_stime = 0, t2r_etime = 0;
|
|
uint32_t r2t_stime, r2t_etime = 0;
|
|
LED_A_ON();
|
|
bool buttonPressed = false;
|
|
|
|
while (!exitLoop) {
|
|
WDT_HIT();
|
|
|
|
//Signal tracer, can be used to get a trigger for an oscilloscope..
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
|
|
r2t_stime = (GetCountSspClk() - time_0) << 4;
|
|
if (!GetIClassCommandFromReader(receivedCmd, &len, 0)) {
|
|
buttonPressed = true;
|
|
exitLoop = true;
|
|
continue;
|
|
}
|
|
r2t_etime = ((GetCountSspClk() - time_0) << 4) - r2t_stime;
|
|
|
|
// 330us normal wait, adjusted for our execution
|
|
|
|
LED_C_ON(); //Signal tracer
|
|
|
|
if (receivedCmd[0] == ICLASS_CMD_ACTALL) { // 0x0A
|
|
// Reader in anticollission phase
|
|
modulated_response = resp_sof;
|
|
modulated_response_size = resp_sof_Len; //order = 1;
|
|
trace_data = sof_data;
|
|
trace_data_size = sizeof(sof_data);
|
|
// adjusted for 330 + (160*num of slot)
|
|
goto send;
|
|
} else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY) { // 0x0C
|
|
if (len == 1) {
|
|
// Reader asks for anticollission CSN
|
|
modulated_response = resp_anticoll;
|
|
modulated_response_size = resp_anticoll_len; //order = 2;
|
|
trace_data = anticoll_data;
|
|
trace_data_size = sizeof(anticoll_data);
|
|
goto send;
|
|
}
|
|
|
|
if (len == 4) {
|
|
// block0,1,2,5 is always readable.
|
|
switch (receivedCmd[1]) {
|
|
case 0: // csn (0c 00)
|
|
modulated_response = resp_csn;
|
|
modulated_response_size = resp_csn_len;
|
|
trace_data = csn_data;
|
|
trace_data_size = sizeof(csn_data);
|
|
goto send;
|
|
case 1: // configuration (0c 01)
|
|
modulated_response = resp_conf;
|
|
modulated_response_size = resp_conf_len;
|
|
trace_data = conf_data;
|
|
trace_data_size = sizeof(conf_data);
|
|
goto send;
|
|
case 2: // e-purse (0c 02)
|
|
modulated_response = resp_cc;
|
|
modulated_response_size = resp_cc_len;
|
|
trace_data = card_challenge_data;
|
|
trace_data_size = sizeof(card_challenge_data);
|
|
// set epurse of sim2,4 attack
|
|
if (reader_mac_buf != NULL) {
|
|
memcpy(reader_mac_buf, card_challenge_data, 8);
|
|
}
|
|
goto send;
|
|
case 5:// Application Issuer Area (0c 05)
|
|
modulated_response = resp_aia;
|
|
modulated_response_size = resp_aia_len;
|
|
trace_data = aia_data;
|
|
trace_data_size = sizeof(aia_data);
|
|
goto send;
|
|
default : {
|
|
if (simulationMode == MODE_FULLSIM) { // 0x0C
|
|
//Read block
|
|
//Take the data...
|
|
memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8);
|
|
AddCrc(data_generic_trace, 8);
|
|
trace_data = data_generic_trace;
|
|
trace_data_size = 10;
|
|
CodeIClassTagAnswer(trace_data, trace_data_size);
|
|
memcpy(modulated_response, ToSend, ToSendMax);
|
|
modulated_response_size = ToSendMax;
|
|
goto send;
|
|
}
|
|
break;
|
|
}
|
|
}//swith
|
|
}// if 4
|
|
} else if (receivedCmd[0] == ICLASS_CMD_SELECT) { // 0x81
|
|
// Reader selects anticollission CSN.
|
|
// Tag sends the corresponding real CSN
|
|
modulated_response = resp_csn;
|
|
modulated_response_size = resp_csn_len; //order = 3;
|
|
trace_data = csn_data;
|
|
trace_data_size = sizeof(csn_data);
|
|
goto send;
|
|
} else if (receivedCmd[0] == ICLASS_CMD_READCHECK_KD) { // 0x88
|
|
// Read e-purse (88 02)
|
|
modulated_response = resp_cc;
|
|
modulated_response_size = resp_cc_len; //order = 4;
|
|
trace_data = card_challenge_data;
|
|
trace_data_size = sizeof(card_challenge_data);
|
|
LED_B_ON();
|
|
goto send;
|
|
} else if (receivedCmd[0] == ICLASS_CMD_READCHECK_KC) { // 0x18
|
|
// Read e-purse (18 02)
|
|
modulated_response = resp_cc;
|
|
modulated_response_size = resp_cc_len; //order = 4;
|
|
trace_data = card_challenge_data;
|
|
trace_data_size = sizeof(card_challenge_data);
|
|
LED_B_ON();
|
|
goto send;
|
|
} else if (receivedCmd[0] == ICLASS_CMD_CHECK) { // 0x05
|
|
// Reader random and reader MAC!!!
|
|
if (simulationMode == MODE_FULLSIM) {
|
|
// NR, from reader, is in receivedCmd +1
|
|
opt_doTagMAC_2(cipher_state, receivedCmd + 1, data_generic_trace, diversified_key);
|
|
|
|
trace_data = data_generic_trace;
|
|
trace_data_size = 4;
|
|
CodeIClassTagAnswer(trace_data, trace_data_size);
|
|
memcpy(data_response, ToSend, ToSendMax);
|
|
modulated_response = data_response;
|
|
modulated_response_size = ToSendMax;
|
|
} else {
|
|
// Not fullsim, we don't respond
|
|
// We do not know what to answer, so lets keep quiet
|
|
modulated_response = resp_sof;
|
|
modulated_response_size = 0;
|
|
trace_data = NULL;
|
|
trace_data_size = 0;
|
|
|
|
if (simulationMode == MODE_EXIT_AFTER_MAC) {
|
|
|
|
if (DBGLEVEL == DBG_EXTENDED) {
|
|
Dbprintf("[+] CSN: %02x %02x %02x %02x %02x %02x %02x %02x", csn[0], csn[1], csn[2], csn[3], csn[4], csn[5], csn[6], csn[7]);
|
|
Dbprintf("[+] RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x", len,
|
|
receivedCmd[0], receivedCmd[1], receivedCmd[2],
|
|
receivedCmd[3], receivedCmd[4], receivedCmd[5],
|
|
receivedCmd[6], receivedCmd[7], receivedCmd[8]);
|
|
} else {
|
|
Dbprintf("[+] CSN: %02x .... %02x OK", csn[0], csn[7]);
|
|
}
|
|
if (reader_mac_buf != NULL) {
|
|
memcpy(reader_mac_buf + 8, receivedCmd + 1, 8);
|
|
}
|
|
exitLoop = true;
|
|
}
|
|
}
|
|
goto send;
|
|
} else if (receivedCmd[0] == ICLASS_CMD_HALT && len == 1) {
|
|
// Reader ends the session
|
|
modulated_response = resp_sof;
|
|
modulated_response_size = 0; //order = 0;
|
|
trace_data = NULL;
|
|
trace_data_size = 0;
|
|
goto send;
|
|
} else if (simulationMode == MODE_FULLSIM && receivedCmd[0] == ICLASS_CMD_READ4 && len == 4) { // 0x06
|
|
//Read block
|
|
//Take the data...
|
|
memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8 * 4);
|
|
AddCrc(data_generic_trace, 8 * 4);
|
|
trace_data = data_generic_trace;
|
|
trace_data_size = 34;
|
|
CodeIClassTagAnswer(trace_data, trace_data_size);
|
|
memcpy(modulated_response, ToSend, ToSendMax);
|
|
modulated_response_size = ToSendMax;
|
|
goto send;
|
|
} else if (simulationMode == MODE_FULLSIM && receivedCmd[0] == ICLASS_CMD_UPDATE) {
|
|
|
|
//Probably the reader wants to update the nonce. Let's just ignore that for now.
|
|
// OBS! If this is implemented, don't forget to regenerate the cipher_state
|
|
//We're expected to respond with the data+crc, exactly what's already in the receivedcmd
|
|
//receivedcmd is now UPDATE 1b | ADDRESS 1b| DATA 8b| Signature 4b or CRC 2b|
|
|
|
|
//Take the data...
|
|
memcpy(data_generic_trace, receivedCmd + 2, 8);
|
|
AddCrc(data_generic_trace, 8);
|
|
trace_data = data_generic_trace;
|
|
trace_data_size = 10;
|
|
CodeIClassTagAnswer(trace_data, trace_data_size);
|
|
|
|
memcpy(data_response, ToSend, ToSendMax);
|
|
modulated_response = data_response;
|
|
modulated_response_size = ToSendMax;
|
|
// response_delay = 4600 * 1.5; // tPROG 4-15ms
|
|
goto send;
|
|
// } else if(receivedCmd[0] == ICLASS_CMD_PAGESEL) { // 0x84
|
|
//Pagesel
|
|
//Pagesel enables to select a page in the selected chip memory and return its configuration block
|
|
//Chips with a single page will not answer to this command
|
|
// It appears we're fine ignoring this.
|
|
//Otherwise, we should answer 8bytes (block) + 2bytes CRC
|
|
// } else if(receivedCmd[0] == ICLASS_CMD_DETECT) { // 0x0F
|
|
} else {
|
|
//#db# Unknown command received from reader (len=5): 26 1 0 f6 a 44 44 44 44
|
|
// Never seen this command before
|
|
if (DBGLEVEL == DBG_EXTENDED)
|
|
print_result("[-] Unhandled command received ", receivedCmd, len);
|
|
|
|
// Do not respond
|
|
modulated_response = resp_sof;
|
|
modulated_response_size = 0; //order = 0;
|
|
trace_data = NULL;
|
|
trace_data_size = 0;
|
|
}
|
|
|
|
send:
|
|
/**
|
|
A legit tag has about 330us delay between reader EOT and tag SOF.
|
|
**/
|
|
if (modulated_response_size > 0) {
|
|
t2r_stime = (GetCountSspClk() - time_0) << 4;
|
|
SendIClassAnswer(modulated_response, modulated_response_size, 0);
|
|
t2r_etime = ((GetCountSspClk() - time_0) << 4) - t2r_stime;
|
|
}
|
|
|
|
LogTrace(receivedCmd, len, r2t_stime, r2t_etime, NULL, true);
|
|
|
|
if (trace_data != NULL)
|
|
LogTrace(trace_data, trace_data_size, t2r_stime, t2r_etime, NULL, false);
|
|
}
|
|
|
|
LEDsoff();
|
|
|
|
if (buttonPressed)
|
|
DbpString("[+] button pressed");
|
|
|
|
return buttonPressed;
|
|
}
|
|
|
|
/**
|
|
* @brief sends our simulated tag answer
|
|
* @param resp
|
|
* @param respLen
|
|
* @param delay
|
|
*/
|
|
static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay) {
|
|
int i = 0;
|
|
volatile uint8_t b;
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K_8BIT);
|
|
|
|
AT91C_BASE_SSC->SSC_THR = 0x00;
|
|
|
|
uint16_t checked = 0;
|
|
for (;;) {
|
|
|
|
if (checked == 1000) {
|
|
if (BUTTON_PRESS() || data_available()) return 0;
|
|
checked = 0;
|
|
} else {
|
|
checked++;
|
|
}
|
|
// Prevent rx holding register from overflowing
|
|
if ((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {
|
|
b = AT91C_BASE_SSC->SSC_RHR;
|
|
(void) b;
|
|
}
|
|
|
|
// Put byte into tx holding register as soon as it is ready
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
b = 0x00;
|
|
if (i < respLen) {
|
|
b = resp[i];
|
|
//Hack
|
|
//b = 0xAC;
|
|
}
|
|
i++;
|
|
AT91C_BASE_SSC->SSC_THR = b;
|
|
}
|
|
// if (i > respLen + 4) break;
|
|
if (i > respLen + 1) break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// THE READER CODE
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Transmit the command (to the tag) that was placed in ToSend[].
|
|
//-----------------------------------------------------------------------------
|
|
static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int *wait) {
|
|
|
|
int c = 0;
|
|
// volatile uint32_t b;
|
|
bool firstpart = true;
|
|
uint8_t sendbyte;
|
|
|
|
time_rdr = 0;
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
|
|
AT91C_BASE_SSC->SSC_THR = 0x00;
|
|
|
|
// make sure we timeout previous comms.
|
|
if (*wait)
|
|
SpinDelayUs(*wait);
|
|
|
|
for (;;) {
|
|
|
|
WDT_HIT();
|
|
|
|
// Put byte into tx holding register as soon as it is ready
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
|
|
// DOUBLE THE SAMPLES!
|
|
if (firstpart) {
|
|
sendbyte = (cmd[c] & 0xf0) | (cmd[c] >> 4);
|
|
} else {
|
|
sendbyte = (cmd[c] & 0x0f) | (cmd[c] << 4);
|
|
c++;
|
|
}
|
|
|
|
if (sendbyte == 0xff)
|
|
sendbyte = 0xfe;
|
|
|
|
AT91C_BASE_SSC->SSC_THR = sendbyte;
|
|
firstpart = !firstpart;
|
|
|
|
if (c >= len) break;
|
|
}
|
|
|
|
// Prevent rx holding register from overflowing
|
|
/*
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
|
b = AT91C_BASE_SSC->SSC_RHR;
|
|
(void)b;
|
|
}
|
|
*/
|
|
}
|
|
|
|
time_rdr = GetCountSspClk();
|
|
|
|
if (samples) {
|
|
if (wait)
|
|
*samples = (c + *wait) << 3;
|
|
else
|
|
*samples = c << 3;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Prepare iClass reader command to send to FPGA
|
|
//-----------------------------------------------------------------------------
|
|
void CodeIClassCommand(const uint8_t *cmd, int len) {
|
|
int i, j, k;
|
|
|
|
ToSendReset();
|
|
|
|
// (SOC) Start of Communication: 1 out of 4
|
|
ToSend[++ToSendMax] = 0xf0;
|
|
ToSend[++ToSendMax] = 0x00;
|
|
ToSend[++ToSendMax] = 0x0f;
|
|
ToSend[++ToSendMax] = 0x00;
|
|
|
|
// Modulate the bytes
|
|
for (i = 0; i < len; i++) {
|
|
uint8_t b = cmd[i];
|
|
for (j = 0; j < 4; j++) {
|
|
for (k = 0; k < 4; k++) {
|
|
|
|
if (k == (b & 3))
|
|
ToSend[++ToSendMax] = 0xf0;
|
|
else
|
|
ToSend[++ToSendMax] = 0x00;
|
|
}
|
|
b >>= 2;
|
|
}
|
|
}
|
|
|
|
// (EOC) End of Communication
|
|
ToSend[++ToSendMax] = 0x00;
|
|
ToSend[++ToSendMax] = 0x00;
|
|
ToSend[++ToSendMax] = 0xf0;
|
|
ToSend[++ToSendMax] = 0x00;
|
|
|
|
// Convert from last character reference to length
|
|
ToSendMax++;
|
|
}
|
|
|
|
void ReaderTransmitIClass_ext(uint8_t *frame, int len, int wait) {
|
|
|
|
int samples = 0;
|
|
|
|
// This is tied to other size changes
|
|
CodeIClassCommand(frame, len);
|
|
|
|
// Select the card
|
|
TransmitIClassCommand(ToSend, ToSendMax, &samples, &wait);
|
|
if (trigger)
|
|
LED_A_ON();
|
|
|
|
rsamples += samples;
|
|
|
|
LogTrace(frame, len, rsamples, rsamples, NULL, true);
|
|
}
|
|
void ReaderTransmitIClass(uint8_t *frame, int len) {
|
|
ReaderTransmitIClass_ext(frame, len, 330);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Wait a certain time for tag response
|
|
// If a response is captured return TRUE
|
|
// If it takes too long return FALSE
|
|
//-----------------------------------------------------------------------------
|
|
static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, int *wait) {
|
|
// buffer needs to be 512 bytes
|
|
// maxLen is not used...
|
|
|
|
int c = 0;
|
|
bool skip = false;
|
|
|
|
// Setup UART/DEMOD to receive
|
|
DemodIcInit(receivedResponse);
|
|
|
|
// Set FPGA mode to "reader listen mode", no modulation (listen
|
|
// only, since we are receiving, not transmitting).
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
SpinDelayUs(g_wait); //310 Tout= 330us (iso15603-2) (330/21.3) take consideration for clock increments.
|
|
uint32_t foo = GetCountSspClk();
|
|
|
|
// clear RXRDY:
|
|
uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
|
|
(void)b;
|
|
|
|
uint16_t checked = 0;
|
|
|
|
for (;;) {
|
|
WDT_HIT();
|
|
|
|
if (checked == 1000) {
|
|
if (BUTTON_PRESS() || data_available()) return false;
|
|
checked = 0;
|
|
} else {
|
|
checked++;
|
|
}
|
|
|
|
// keep tx buffer in a defined state anyway.
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
AT91C_BASE_SSC->SSC_THR = 0x00;
|
|
}
|
|
|
|
// Wait for byte be become available in rx holding register
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
|
|
|
c++;
|
|
b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
|
|
skip = !skip;
|
|
if (skip) continue;
|
|
|
|
if (ManchesterDecoding_iclass(b & 0x0f)) {
|
|
if (samples)
|
|
*samples = c << 3;
|
|
|
|
time_response = GetCountSspClk() - foo;
|
|
return true;
|
|
}
|
|
|
|
if (GetCountSspClk() - foo > timeout) return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int ReaderReceiveIClass(uint8_t *receivedAnswer) {
|
|
int samples = 0;
|
|
|
|
if (GetIClassAnswer(receivedAnswer, 0, &samples, NULL) == false)
|
|
return 0;
|
|
|
|
rsamples += samples;
|
|
|
|
LogTrace(receivedAnswer, Demod.len, rsamples, rsamples, NULL, false);
|
|
|
|
if (samples == 0)
|
|
return 0;
|
|
|
|
return Demod.len;
|
|
}
|
|
|
|
void setupIclassReader() {
|
|
|
|
LEDsoff();
|
|
|
|
// Start from off (no field generated)
|
|
// Signal field is off with the appropriate LED
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
|
|
FpgaSetupSsc();
|
|
|
|
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
// Now give it time to spin up.
|
|
// Signal field is on with the appropriate LED
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
|
|
SpinDelay(500);
|
|
|
|
StartCountSspClk();
|
|
|
|
LED_A_ON();
|
|
}
|
|
|
|
bool sendCmdGetResponseWithRetries(uint8_t *command, size_t cmdsize, uint8_t *resp, uint8_t expected_size, int8_t retries) {
|
|
while (retries-- > 0) {
|
|
|
|
ReaderTransmitIClass(command, cmdsize);
|
|
|
|
//iceman - if received size is bigger than expected, we smash the stack here
|
|
// since its called with fixed sized arrays
|
|
|
|
// update/write command takes 4ms to 15ms before responding
|
|
int old_wait = g_wait;
|
|
if (command[0] == ICLASS_CMD_UPDATE)
|
|
g_wait = 3900;
|
|
|
|
uint8_t got_n = ReaderReceiveIClass(resp);
|
|
|
|
g_wait = old_wait;
|
|
|
|
// 0xBB is the internal debug separator byte..
|
|
if (expected_size != got_n || (resp[0] == 0xBB || resp[7] == 0xBB || resp[2] == 0xBB)) {
|
|
//try again
|
|
SpinDelayUs(360);
|
|
continue;
|
|
}
|
|
|
|
if (got_n == expected_size)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Talks to an iclass tag, sends the commands to get CSN and CC.
|
|
* @param card_data where the CSN and CC are stored for return
|
|
* @return 0 = fail
|
|
* 1 = Got CSN
|
|
* 2 = Got CSN and CC
|
|
*/
|
|
uint8_t handshakeIclassTag_ext(uint8_t *card_data, bool use_credit_key) {
|
|
|
|
// act_all...
|
|
static uint8_t act_all[] = { ICLASS_CMD_ACTALL };
|
|
static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 };
|
|
static uint8_t select[] = { ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
uint8_t readcheck_cc[] = { ICLASS_CMD_READCHECK_KD, 0x02 };
|
|
|
|
if (use_credit_key)
|
|
readcheck_cc[0] = ICLASS_CMD_READCHECK_KC;
|
|
|
|
uint8_t resp[ICLASS_BUFFER_SIZE] = {0};
|
|
uint8_t read_status = 0;
|
|
|
|
// Send act_all
|
|
ReaderTransmitIClass_ext(act_all, 1, 330 + 160);
|
|
// Card present?
|
|
if (!ReaderReceiveIClass(resp)) return read_status;//Fail
|
|
|
|
//Send Identify
|
|
ReaderTransmitIClass(identify, 1);
|
|
|
|
//We expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC
|
|
uint8_t len = ReaderReceiveIClass(resp);
|
|
if (len != 10) return read_status;//Fail
|
|
|
|
//Copy the Anti-collision CSN to our select-packet
|
|
memcpy(&select[1], resp, 8);
|
|
|
|
//Select the card
|
|
ReaderTransmitIClass(select, sizeof(select));
|
|
|
|
//We expect a 10-byte response here, 8 byte CSN and 2 byte CRC
|
|
len = ReaderReceiveIClass(resp);
|
|
if (len != 10) return read_status;//Fail
|
|
|
|
//Success - level 1, we got CSN
|
|
//Save CSN in response data
|
|
memcpy(card_data, resp, 8);
|
|
|
|
//Flag that we got to at least stage 1, read CSN
|
|
read_status = 1;
|
|
|
|
// Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC)
|
|
// ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc));
|
|
// if (ReaderReceiveIClass(resp) == 8) {
|
|
// //Save CC (e-purse) in response data
|
|
// memcpy(card_data+8, resp, 8);
|
|
// read_status++;
|
|
// }
|
|
|
|
bool isOK = sendCmdGetResponseWithRetries(readcheck_cc, sizeof(readcheck_cc), resp, 8, 3);
|
|
if (!isOK) return read_status;
|
|
|
|
//Save CC (e-purse) in response data
|
|
memcpy(card_data + 8, resp, 8);
|
|
read_status++;
|
|
return read_status;
|
|
}
|
|
uint8_t handshakeIclassTag(uint8_t *card_data) {
|
|
return handshakeIclassTag_ext(card_data, false);
|
|
}
|
|
|
|
// Reader iClass Anticollission
|
|
// turn off afterwards
|
|
void ReaderIClass(uint8_t arg0) {
|
|
|
|
uint8_t card_data[6 * 8] = {0};
|
|
uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
uint8_t resp[ICLASS_BUFFER_SIZE];
|
|
|
|
memset(card_data, 0xFF, sizeof(card_data));
|
|
memset(resp, 0xFF, sizeof(resp));
|
|
|
|
//Read conf block CRC(0x01) => 0xfa 0x22
|
|
uint8_t readConf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22};
|
|
|
|
//Read App Issuer Area block CRC(0x05) => 0xde 0x64
|
|
uint8_t readAA[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64};
|
|
|
|
uint16_t tryCnt = 0;
|
|
|
|
bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully
|
|
bool try_once = arg0 & FLAG_ICLASS_READER_ONE_TRY; // flag to not to loop continuously, looking for tag
|
|
bool use_credit_key = arg0 & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key
|
|
bool flagReadConfig = arg0 & FLAG_ICLASS_READER_CONF; // flag to read block1, configuration
|
|
bool flagReadCC = arg0 & FLAG_ICLASS_READER_CC; // flag to read block2, e-purse
|
|
bool flagReadAIA = arg0 & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area
|
|
|
|
setupIclassReader();
|
|
|
|
uint16_t checked = 0;
|
|
bool userCancelled = BUTTON_PRESS() || data_available();
|
|
while (!userCancelled) {
|
|
|
|
WDT_HIT();
|
|
|
|
// if only looking for one card try 2 times if we missed it the first time
|
|
if (try_once && tryCnt > 2) {
|
|
if (DBGLEVEL > 1) DbpString("Failed to find a tag");
|
|
break;
|
|
}
|
|
|
|
tryCnt++;
|
|
uint8_t result_status = 0;
|
|
|
|
int read_status = handshakeIclassTag_ext(card_data, use_credit_key);
|
|
|
|
if (read_status == 0) continue;
|
|
if (read_status == 1) result_status = FLAG_ICLASS_READER_CSN;
|
|
if (read_status == 2) result_status = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC;
|
|
|
|
// handshakeIclass returns CSN|CC, but the actual block
|
|
// layout is CSN|CONFIG|CC, so here we reorder the data,
|
|
// moving CC forward 8 bytes
|
|
memcpy(card_data + 16, card_data + 8, 8);
|
|
|
|
//Read block 1, config
|
|
if (flagReadConfig) {
|
|
if (sendCmdGetResponseWithRetries(readConf, sizeof(readConf), resp, 10, 5)) {
|
|
result_status |= FLAG_ICLASS_READER_CONF;
|
|
memcpy(card_data + 8, resp, 8);
|
|
} else {
|
|
if (DBGLEVEL > 1) DbpString("Failed to dump config block");
|
|
}
|
|
}
|
|
|
|
//Read block 5, AIA
|
|
if (flagReadAIA) {
|
|
if (sendCmdGetResponseWithRetries(readAA, sizeof(readAA), resp, 10, 5)) {
|
|
result_status |= FLAG_ICLASS_READER_AIA;
|
|
memcpy(card_data + (8 * 5), resp, 8);
|
|
} else {
|
|
if (DBGLEVEL > 1) DbpString("Failed to dump AA block");
|
|
}
|
|
}
|
|
|
|
// 0 : CSN
|
|
// 1 : Configuration
|
|
// 2 : e-purse
|
|
// 3 : kd / debit / aa2 (write-only)
|
|
// 4 : kc / credit / aa1 (write-only)
|
|
// 5 : AIA, Application issuer area
|
|
//
|
|
//Then we can 'ship' back the 6 * 8 bytes of data,
|
|
// with 0xFF:s in block 3 and 4.
|
|
|
|
LED_B_ON();
|
|
//Send back to client, but don't bother if we already sent this -
|
|
// only useful if looping in arm (not try_once && not abort_after_read)
|
|
if (memcmp(last_csn, card_data, 8) != 0) {
|
|
// If caller requires that we get Conf, CC, AA, continue until we got it
|
|
if (DBGLEVEL >= DBG_EXTENDED) {
|
|
Dbprintf("STATUS %02X | CSN %c | CONF %c | CC %c | AIA %c | ONCE %c | 1TRY %c",
|
|
result_status,
|
|
(result_status & FLAG_ICLASS_READER_CSN) ? 'Y' : 'N',
|
|
(result_status & FLAG_ICLASS_READER_CONF) ? 'Y' : 'N',
|
|
(result_status & FLAG_ICLASS_READER_CC) ? 'Y' : 'N',
|
|
(result_status & FLAG_ICLASS_READER_AIA) ? 'Y' : 'N'
|
|
);
|
|
Dbprintf(" aar %c | to %c, | uc %c | frc %c | fra %c | cc %c",
|
|
abort_after_read ? 'Y' : 'N',
|
|
try_once ? 'Y' : 'N',
|
|
use_credit_key ? 'Y' : 'N',
|
|
flagReadConfig ? 'Y' : 'N',
|
|
flagReadAIA ? 'Y' : 'N',
|
|
flagReadCC ? 'Y' : 'N'
|
|
);
|
|
}
|
|
|
|
bool send = (result_status & FLAG_ICLASS_READER_CSN);
|
|
if (flagReadCC)
|
|
send |= (result_status & FLAG_ICLASS_READER_CC);
|
|
if (flagReadAIA)
|
|
send |= (result_status & FLAG_ICLASS_READER_AIA);
|
|
if (flagReadConfig)
|
|
send |= (result_status & FLAG_ICLASS_READER_CONF);
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("SEND %c", send ? 'y' : 'n');
|
|
|
|
if (send) {
|
|
reply_old(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data));
|
|
if (abort_after_read) {
|
|
LED_B_OFF();
|
|
return;
|
|
}
|
|
//Save that we already sent this....
|
|
memcpy(last_csn, card_data, 8);
|
|
}
|
|
}
|
|
LED_B_OFF();
|
|
|
|
if (checked == 1000) {
|
|
userCancelled = BUTTON_PRESS() || data_available();
|
|
checked = 0;
|
|
} else {
|
|
checked++;
|
|
}
|
|
}
|
|
|
|
if (userCancelled) {
|
|
reply_old(CMD_ACK, 0xFF, 0, 0, card_data, 0);
|
|
switch_off();
|
|
} else {
|
|
reply_old(CMD_ACK, 0, 0, 0, card_data, 0);
|
|
}
|
|
}
|
|
|
|
// turn off afterwards
|
|
void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) {
|
|
|
|
uint8_t cardsize = 0;
|
|
uint8_t mem = 0;
|
|
uint8_t check[] = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
uint8_t read[] = { 0x0c, 0x00, 0x00, 0x00 };
|
|
uint8_t card_data[PM3_CMD_DATA_SIZE] = {0};
|
|
uint8_t resp[ICLASS_BUFFER_SIZE] = {0};
|
|
|
|
static struct memory_t {
|
|
int k16;
|
|
int book;
|
|
int k2;
|
|
int lockauth;
|
|
int keyaccess;
|
|
} memory;
|
|
|
|
setupIclassReader();
|
|
|
|
while (!BUTTON_PRESS()) {
|
|
|
|
WDT_HIT();
|
|
|
|
uint8_t read_status = handshakeIclassTag(card_data);
|
|
if (read_status < 2) continue;
|
|
|
|
//for now replay captured auth (as cc not updated)
|
|
memcpy(check + 5, mac, 4);
|
|
|
|
if (!sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 5)) {
|
|
DbpString("Error: Authentication Fail!");
|
|
continue;
|
|
}
|
|
|
|
//first get configuration block (block 1)
|
|
read[1] = 1;
|
|
AddCrc(read + 1, 1);
|
|
|
|
if (!sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 5)) {
|
|
DbpString("Dump config (block 1) failed");
|
|
continue;
|
|
}
|
|
|
|
mem = resp[5];
|
|
memory.k16 = (mem & 0x80);
|
|
memory.book = (mem & 0x20);
|
|
memory.k2 = (mem & 0x8);
|
|
memory.lockauth = (mem & 0x2);
|
|
memory.keyaccess = (mem & 0x1);
|
|
|
|
cardsize = memory.k16 ? 255 : 32;
|
|
|
|
WDT_HIT();
|
|
//Set card_data to all zeroes, we'll fill it with data
|
|
memset(card_data, 0x0, PM3_CMD_DATA_SIZE);
|
|
uint8_t failedRead = 0;
|
|
uint32_t stored_data_length = 0;
|
|
|
|
//then loop around remaining blocks
|
|
for (uint16_t block = 0; block < cardsize; block++) {
|
|
|
|
read[1] = block;
|
|
AddCrc(read + 1, 1);
|
|
|
|
if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 5)) {
|
|
Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x",
|
|
block, resp[0], resp[1], resp[2],
|
|
resp[3], resp[4], resp[5],
|
|
resp[6], resp[7]
|
|
);
|
|
|
|
//Fill up the buffer
|
|
memcpy(card_data + stored_data_length, resp, 8);
|
|
stored_data_length += 8;
|
|
if (stored_data_length + 8 > PM3_CMD_DATA_SIZE) {
|
|
//Time to send this off and start afresh
|
|
reply_old(CMD_ACK,
|
|
stored_data_length,//data length
|
|
failedRead,//Failed blocks?
|
|
0,//Not used ATM
|
|
card_data,
|
|
stored_data_length
|
|
);
|
|
//reset
|
|
stored_data_length = 0;
|
|
failedRead = 0;
|
|
}
|
|
} else {
|
|
failedRead = 1;
|
|
stored_data_length += 8;//Otherwise, data becomes misaligned
|
|
Dbprintf("Failed to dump block %d", block);
|
|
}
|
|
}
|
|
|
|
//Send off any remaining data
|
|
if (stored_data_length > 0) {
|
|
reply_old(CMD_ACK,
|
|
stored_data_length,//data length
|
|
failedRead,//Failed blocks?
|
|
0,//Not used ATM
|
|
card_data,
|
|
stored_data_length
|
|
);
|
|
}
|
|
//If we got here, let's break
|
|
break;
|
|
}
|
|
//Signal end of transmission
|
|
reply_old(CMD_ACK,
|
|
0,//data length
|
|
0,//Failed blocks?
|
|
0,//Not used ATM
|
|
card_data,
|
|
0
|
|
);
|
|
switch_off();
|
|
}
|
|
|
|
// not used. ?!? ( CMD_HF_ICLASS_READCHECK)
|
|
// turn off afterwards
|
|
void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) {
|
|
uint8_t readcheck[] = { keytype, blockno };
|
|
uint8_t resp[] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
size_t isOK = 0;
|
|
isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 6);
|
|
reply_mix(CMD_ACK, isOK, 0, 0, 0, 0);
|
|
switch_off();
|
|
}
|
|
|
|
// used with function select_and_auth (cmdhficlass.c)
|
|
// which needs to authenticate before doing more things like read/write
|
|
void iClass_Authentication(uint8_t *mac) {
|
|
uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
uint8_t resp[ICLASS_BUFFER_SIZE];
|
|
|
|
// copy MAC to check command (readersignature)
|
|
check[5] = mac[0];
|
|
check[6] = mac[1];
|
|
check[7] = mac[2];
|
|
check[8] = mac[3];
|
|
//memcpy(check+5, mac, 4);
|
|
|
|
// 6 retries
|
|
uint8_t isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 6);
|
|
reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t*)&isOK ,sizeof(uint8_t));
|
|
}
|
|
|
|
typedef struct iclass_premac {
|
|
uint8_t mac[4];
|
|
} iclass_premac_t;
|
|
|
|
/* this function works on the following assumptions.
|
|
* - one select first, to get CSN / CC (e-purse)
|
|
* - calculate before diversified keys and precalc mac based on CSN/KEY.
|
|
* - data in contains of diversified keys, mac
|
|
* - key loop only test one type of authtication key. Ie two calls needed
|
|
* to cover debit and credit key. (AA1/AA2)
|
|
*/
|
|
void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) {
|
|
uint8_t i = 0, isOK = 0;
|
|
uint8_t lastChunk = ((arg0 >> 8) & 0xFF);
|
|
bool use_credit_key = ((arg0 >> 16) & 0xFF);
|
|
uint8_t keyCount = arg1 & 0xFF;
|
|
uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
uint8_t resp[ICLASS_BUFFER_SIZE];
|
|
uint8_t readcheck_cc[] = { ICLASS_CMD_READCHECK_KD, 0x02 };
|
|
|
|
if (use_credit_key)
|
|
readcheck_cc[0] = ICLASS_CMD_READCHECK_KC;
|
|
|
|
// select card / e-purse
|
|
uint8_t card_data[6 * 8] = {0};
|
|
|
|
iclass_premac_t *keys = (iclass_premac_t *)datain;
|
|
|
|
LED_A_ON();
|
|
|
|
switch_off();
|
|
SpinDelay(20);
|
|
|
|
setupIclassReader();
|
|
|
|
uint16_t checked = 0;
|
|
int read_status = 0;
|
|
uint8_t startup_limit = 10;
|
|
while (read_status != 2) {
|
|
|
|
if (checked == 1000) {
|
|
if (BUTTON_PRESS() || !data_available()) goto out;
|
|
checked = 0;
|
|
} else {
|
|
checked++;
|
|
}
|
|
|
|
read_status = handshakeIclassTag_ext(card_data, use_credit_key);
|
|
if (startup_limit-- == 0) {
|
|
Dbprintf("[-] Handshake status | %d (fail 10)", read_status);
|
|
isOK = 99;
|
|
goto out;
|
|
}
|
|
};
|
|
// since handshakeIclassTag_ext call sends s readcheck, we start with sending first response.
|
|
|
|
checked = 0;
|
|
|
|
// Keychunk loop
|
|
for (i = 0; i < keyCount; i++) {
|
|
|
|
// Allow button press / usb cmd to interrupt device
|
|
if (checked == 1000) {
|
|
if (BUTTON_PRESS() || !data_available()) goto out;
|
|
checked = 0;
|
|
} else {
|
|
checked++;
|
|
}
|
|
|
|
WDT_HIT();
|
|
LED_B_ON();
|
|
|
|
// copy MAC to check command (readersignature)
|
|
check[5] = keys[i].mac[0];
|
|
check[6] = keys[i].mac[1];
|
|
check[7] = keys[i].mac[2];
|
|
check[8] = keys[i].mac[3];
|
|
|
|
// expect 4bytes, 3 retries times..
|
|
isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 3);
|
|
if (isOK)
|
|
goto out;
|
|
|
|
// Auth Sequence MUST begin with reading e-purse. (block2)
|
|
// Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC)
|
|
ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc));
|
|
|
|
LED_B_OFF();
|
|
}
|
|
|
|
out:
|
|
// send keyindex.
|
|
reply_mix(CMD_ACK, isOK, i, 0, 0, 0);
|
|
|
|
if (isOK >= 1 || lastChunk) {
|
|
switch_off();
|
|
LED_A_OFF();
|
|
}
|
|
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
}
|
|
|
|
// Tries to read block.
|
|
// retries 10times.
|
|
bool iClass_ReadBlock(uint8_t blockno, uint8_t *data, uint8_t len) {
|
|
uint8_t resp[10];
|
|
uint8_t cmd[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00};
|
|
AddCrc(cmd + 1, 1);
|
|
// expect size 10, retry 5times
|
|
bool isOK = sendCmdGetResponseWithRetries(cmd, sizeof(cmd), resp, 10, 5);
|
|
memcpy(data, resp, len);
|
|
return isOK;
|
|
}
|
|
|
|
// turn off afterwards
|
|
// readblock 8 + 2. only want 8.
|
|
void iClass_ReadBlk(uint8_t blockno) {
|
|
struct p {
|
|
bool isOK;
|
|
uint8_t blockdata[8];
|
|
} PACKED result;
|
|
|
|
result.isOK = iClass_ReadBlock(blockno, result.blockdata, sizeof(result.blockdata));
|
|
switch_off();
|
|
reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&result, sizeof(result));
|
|
}
|
|
|
|
// turn off afterwards
|
|
void iClass_Dump(uint8_t blockno, uint8_t numblks) {
|
|
uint8_t blockdata[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
bool isOK = false;
|
|
uint8_t blkCnt = 0;
|
|
|
|
BigBuf_free();
|
|
uint8_t *dataout = BigBuf_malloc(255 * 8);
|
|
if (dataout == NULL) {
|
|
DbpString("[!] out of memory");
|
|
OnError(1);
|
|
return;
|
|
}
|
|
// fill mem with 0xFF
|
|
memset(dataout, 0xFF, 255 * 8);
|
|
|
|
for (; blkCnt < numblks; blkCnt++) {
|
|
isOK = iClass_ReadBlock(blockno + blkCnt, blockdata, sizeof(blockdata));
|
|
|
|
// 0xBB is the internal debug separator byte..
|
|
if (!isOK || (blockdata[0] == 0xBB || blockdata[7] == 0xBB || blockdata[2] == 0xBB)) { //try again
|
|
isOK = iClass_ReadBlock(blockno + blkCnt, blockdata, sizeof(blockdata));
|
|
if (!isOK) {
|
|
Dbprintf("[!] block %02X failed to read", blkCnt + blockno);
|
|
break;
|
|
}
|
|
}
|
|
memcpy(dataout + (blkCnt * 8), blockdata, 8);
|
|
}
|
|
|
|
switch_off();
|
|
//return pointer to dump memory in arg3
|
|
reply_mix(CMD_ACK, isOK, blkCnt, BigBuf_max_traceLen(), 0, 0);
|
|
BigBuf_free();
|
|
}
|
|
|
|
bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) {
|
|
uint8_t resp[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
uint8_t write[] = { ICLASS_CMD_UPDATE, blockno, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
memcpy(write + 2, data, 12); // data + mac
|
|
AddCrc(write + 1, 13);
|
|
return sendCmdGetResponseWithRetries(write, sizeof(write), resp, sizeof(resp), 5);
|
|
}
|
|
|
|
// turn off afterwards
|
|
void iClass_WriteBlock(uint8_t blockno, uint8_t *data) {
|
|
uint8_t isOK = iClass_WriteBlock_ext(blockno, data);
|
|
switch_off();
|
|
reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t*)&isOK, sizeof(uint8_t));
|
|
}
|
|
|
|
// turn off afterwards
|
|
void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) {
|
|
int i, written = 0;
|
|
int total_block = (endblock - startblock) + 1;
|
|
for (i = 0; i < total_block; i++) {
|
|
// block number
|
|
if (iClass_WriteBlock_ext(i + startblock, data + (i * 12))) {
|
|
Dbprintf("Write block [%02x] successful", i + startblock);
|
|
written++;
|
|
} else {
|
|
if (iClass_WriteBlock_ext(i + startblock, data + (i * 12))) {
|
|
Dbprintf("Write block [%02x] successful", i + startblock);
|
|
written++;
|
|
} else {
|
|
Dbprintf("Write block [%02x] failed", i + startblock);
|
|
}
|
|
}
|
|
}
|
|
if (written == total_block)
|
|
DbpString("Clone complete");
|
|
else
|
|
DbpString("Clone incomplete");
|
|
|
|
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
|
switch_off();
|
|
}
|