//-----------------------------------------------------------------------------
-// Merlok - June 2011
+// Merlok - June 2011, 2012
// Gerhard de Koning Gans - May 2008
// Hagen Fritsch - June 2010
//
#include "apps.h"
#include "util.h"
#include "string.h"
+#include "cmd.h"
#include "iso14443crc.h"
#include "iso14443a.h"
#include "crapto1.h"
#include "mifareutil.h"
-static uint8_t *trace = (uint8_t *) BigBuf;
-static int traceLen = 0;
-static int rsamples = 0;
-static int tracing = TRUE;
static uint32_t iso14a_timeout;
+uint8_t *trace = (uint8_t *) BigBuf+TRACE_OFFSET;
+int traceLen = 0;
+int rsamples = 0;
+int tracing = TRUE;
+uint8_t trigger = 0;
+// the block number for the ISO14443-4 PCB
+static uint8_t iso14_pcb_blocknum = 0;
// CARD TO READER - manchester
// Sequence D: 11110000 modulation with subcarrier during first half
#define SEC_Y 0x00
#define SEC_Z 0xc0
-static const uint8_t OddByteParity[256] = {
+const uint8_t OddByteParity[256] = {
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
};
-uint8_t trigger = 0;
-void iso14a_set_trigger(int enable) {
+
+void iso14a_set_trigger(bool enable) {
trigger = enable;
}
+void iso14a_clear_trace() {
+ memset(trace, 0x44, TRACE_SIZE);
+ traceLen = 0;
+}
+
+void iso14a_set_tracing(bool enable) {
+ tracing = enable;
+}
+
+void iso14a_set_timeout(uint32_t timeout) {
+ iso14a_timeout = timeout;
+}
+
//-----------------------------------------------------------------------------
// Generate the parity value for a byte sequence
//
ComputeCrc14443(CRC_14443_A,data,len,data+len,data+len+1);
}
-int LogTrace(const uint8_t * btBytes, int iLen, int iSamples, uint32_t dwParity, int bReader)
+// The function LogTrace() is also used by the iClass implementation in iClass.c
+int RAMFUNC LogTrace(const uint8_t * btBytes, int iLen, int iSamples, uint32_t dwParity, int bReader)
{
// Return when trace is full
- if (traceLen >= TRACE_LENGTH) return FALSE;
+ if (traceLen >= TRACE_SIZE) return FALSE;
// Trace the random, i'm curious
rsamples += iSamples;
// The software UART that receives commands from the reader, and its state
// variables.
//-----------------------------------------------------------------------------
-static struct {
- enum {
- STATE_UNSYNCD,
- STATE_START_OF_COMMUNICATION,
- STATE_MILLER_X,
- STATE_MILLER_Y,
- STATE_MILLER_Z,
- STATE_ERROR_WAIT
- } state;
- uint16_t shiftReg;
- int bitCnt;
- int byteCnt;
- int byteCntMax;
- int posCnt;
- int syncBit;
- int parityBits;
- int samples;
- int highCnt;
- int bitBuffer;
- enum {
- DROP_NONE,
- DROP_FIRST_HALF,
- DROP_SECOND_HALF
- } drop;
- uint8_t *output;
-} Uart;
+static tUart Uart;
static RAMFUNC int MillerDecoding(int bit)
{
- int error = 0;
+ //int error = 0;
int bitright;
if(!Uart.bitBuffer) {
// measured a drop in first and second half
// which should not be possible
Uart.state = STATE_ERROR_WAIT;
- error = 0x01;
+ //error = 0x01;
}
Uart.posCnt = 0;
if(Uart.drop == DROP_SECOND_HALF) {
// error, should not happen in SOC
Uart.state = STATE_ERROR_WAIT;
- error = 0x02;
+ //error = 0x02;
}
else {
// correct SOC
// Would be STATE_MILLER_Z
// but Z does not follow X, so error
Uart.state = STATE_ERROR_WAIT;
- error = 0x03;
+ //error = 0x03;
}
if(Uart.drop == DROP_SECOND_HALF) {
// We see a '1' and stay in state X
Uart.bitCnt = 0;
Uart.byteCnt = 0;
Uart.parityBits = 0;
- error = 0;
+ //error = 0;
}
else {
Uart.highCnt = 0;
//=============================================================================
// ISO 14443 Type A - Manchester
//=============================================================================
-
-static struct {
- enum {
- DEMOD_UNSYNCD,
- DEMOD_START_OF_COMMUNICATION,
- DEMOD_MANCHESTER_D,
- DEMOD_MANCHESTER_E,
- DEMOD_MANCHESTER_F,
- DEMOD_ERROR_WAIT
- } state;
- int bitCount;
- int posCount;
- int syncBit;
- int parityBits;
- uint16_t shiftReg;
- int buffer;
- int buff;
- int samples;
- int len;
- enum {
- SUB_NONE,
- SUB_FIRST_HALF,
- SUB_SECOND_HALF
- } sub;
- uint8_t *output;
-} Demod;
+static tDemod Demod;
static RAMFUNC int ManchesterDecoding(int v)
{
int bit;
int modulation;
- int error = 0;
+ //int error = 0;
if(!Demod.buff) {
Demod.buff = 1;
case 0x01: Demod.samples = 0; break;
}
}
- error = 0;
+ //error = 0;
}
}
else {
if(Demod.state!=DEMOD_ERROR_WAIT) {
Demod.state = DEMOD_ERROR_WAIT;
Demod.output[Demod.len] = 0xaa;
- error = 0x01;
+ //error = 0x01;
}
}
else if(modulation) {
else {
Demod.output[Demod.len] = 0xab;
Demod.state = DEMOD_ERROR_WAIT;
- error = 0x02;
+ //error = 0x02;
}
break;
else {
Demod.output[Demod.len] = 0xad;
Demod.state = DEMOD_ERROR_WAIT;
- error = 0x03;
+ //error = 0x03;
}
break;
// triggering so that we start recording at the point that the tag is moved
// near the reader.
//-----------------------------------------------------------------------------
-void RAMFUNC SnoopIso14443a(void)
-{
-// #define RECV_CMD_OFFSET 2032 // original (working as of 21/2/09) values
-// #define RECV_RES_OFFSET 2096 // original (working as of 21/2/09) values
-// #define DMA_BUFFER_OFFSET 2160 // original (working as of 21/2/09) values
-// #define DMA_BUFFER_SIZE 4096 // original (working as of 21/2/09) values
-// #define TRACE_LENGTH 2000 // original (working as of 21/2/09) values
-
- // We won't start recording the frames that we acquire until we trigger;
- // a good trigger condition to get started is probably when we see a
- // response from the tag.
- int triggered = FALSE; // FALSE to wait first for card
-
- // The command (reader -> tag) that we're receiving.
+void RAMFUNC SnoopIso14443a(uint8_t param) {
+ // param:
+ // bit 0 - trigger from first card answer
+ // bit 1 - trigger from first reader 7-bit request
+
+ LEDsoff();
+ // init trace buffer
+ iso14a_clear_trace();
+
+ // We won't start recording the frames that we acquire until we trigger;
+ // a good trigger condition to get started is probably when we see a
+ // response from the tag.
+ // triggered == FALSE -- to wait first for card
+ int triggered = !(param & 0x03);
+
+ // The command (reader -> tag) that we're receiving.
// The length of a received command will in most cases be no more than 18 bytes.
// So 32 should be enough!
- uint8_t *receivedCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET);
- // The response (tag -> reader) that we're receiving.
- uint8_t *receivedResponse = (((uint8_t *)BigBuf) + RECV_RES_OFFSET);
+ uint8_t *receivedCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET);
+ // The response (tag -> reader) that we're receiving.
+ uint8_t *receivedResponse = (((uint8_t *)BigBuf) + RECV_RES_OFFSET);
- // As we receive stuff, we copy it from receivedCmd or receivedResponse
- // into trace, along with its length and other annotations.
- //uint8_t *trace = (uint8_t *)BigBuf;
-
- traceLen = 0; // uncommented to fix ISSUE 15 - gerhard - jan2011
-
- // The DMA buffer, used to stream samples from the FPGA
- int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET;
- int lastRxCounter;
- int8_t *upTo;
- int smpl;
- int maxBehindBy = 0;
-
- // Count of samples received so far, so that we can include timing
- // information in the trace buffer.
- int samples = 0;
- int rsamples = 0;
-
- memset(trace, 0x44, RECV_CMD_OFFSET);
-
- // Set up the demodulator for tag -> reader responses.
- Demod.output = receivedResponse;
- Demod.len = 0;
- Demod.state = DEMOD_UNSYNCD;
-
- // Setup for the DMA.
- FpgaSetupSsc();
- upTo = dmaBuf;
- lastRxCounter = DMA_BUFFER_SIZE;
- FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE);
-
- // And the reader -> tag commands
- memset(&Uart, 0, sizeof(Uart));
- Uart.output = receivedCmd;
- Uart.byteCntMax = 32; // was 100 (greg)////////////////////////////////////////////////////////////////////////
- Uart.state = STATE_UNSYNCD;
+ // As we receive stuff, we copy it from receivedCmd or receivedResponse
+ // into trace, along with its length and other annotations.
+ //uint8_t *trace = (uint8_t *)BigBuf;
+
+ // The DMA buffer, used to stream samples from the FPGA
+ int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET;
+ int8_t *data = dmaBuf;
+ int maxDataLen = 0;
+ int dataLen = 0;
- // And put the FPGA in the appropriate mode
- // Signal field is off with the appropriate LED
- LED_D_OFF();
- FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER);
- SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
+ // Set up the demodulator for tag -> reader responses.
+ Demod.output = receivedResponse;
+ Demod.len = 0;
+ Demod.state = DEMOD_UNSYNCD;
+ // Set up the demodulator for the reader -> tag commands
+ memset(&Uart, 0, sizeof(Uart));
+ Uart.output = receivedCmd;
+ Uart.byteCntMax = 32; // was 100 (greg)//////////////////
+ Uart.state = STATE_UNSYNCD;
- // And now we loop, receiving samples.
- for(;;) {
- LED_A_ON();
- WDT_HIT();
- int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) &
- (DMA_BUFFER_SIZE-1);
- if(behindBy > maxBehindBy) {
- maxBehindBy = behindBy;
- if(behindBy > 400) {
- Dbprintf("blew circular buffer! behindBy=0x%x", behindBy);
- goto done;
- }
- }
- if(behindBy < 1) continue;
+ // Setup for the DMA.
+ FpgaSetupSsc();
+ FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE);
- LED_A_OFF();
- smpl = upTo[0];
- upTo++;
- lastRxCounter -= 1;
- if(upTo - dmaBuf > DMA_BUFFER_SIZE) {
- upTo -= DMA_BUFFER_SIZE;
- lastRxCounter += DMA_BUFFER_SIZE;
- AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo;
- AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE;
- }
+ // And put the FPGA in the appropriate mode
+ // Signal field is off with the appropriate LED
+ LED_D_OFF();
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER);
+ SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
- samples += 4;
- if(MillerDecoding((smpl & 0xF0) >> 4)) {
- rsamples = samples - Uart.samples;
- LED_C_ON();
- if(triggered) {
- trace[traceLen++] = ((rsamples >> 0) & 0xff);
- trace[traceLen++] = ((rsamples >> 8) & 0xff);
- trace[traceLen++] = ((rsamples >> 16) & 0xff);
- trace[traceLen++] = ((rsamples >> 24) & 0xff);
- trace[traceLen++] = ((Uart.parityBits >> 0) & 0xff);
- trace[traceLen++] = ((Uart.parityBits >> 8) & 0xff);
- trace[traceLen++] = ((Uart.parityBits >> 16) & 0xff);
- trace[traceLen++] = ((Uart.parityBits >> 24) & 0xff);
- trace[traceLen++] = Uart.byteCnt;
- memcpy(trace+traceLen, receivedCmd, Uart.byteCnt);
- traceLen += Uart.byteCnt;
- if(traceLen > TRACE_LENGTH) break;
- }
- /* And ready to receive another command. */
- Uart.state = STATE_UNSYNCD;
- /* And also reset the demod code, which might have been */
- /* false-triggered by the commands from the reader. */
- Demod.state = DEMOD_UNSYNCD;
- LED_B_OFF();
- }
+ // Count of samples received so far, so that we can include timing
+ // information in the trace buffer.
+ rsamples = 0;
+ // And now we loop, receiving samples.
+ while(true) {
+ if(BUTTON_PRESS()) {
+ DbpString("cancelled by button");
+ goto done;
+ }
- if(ManchesterDecoding(smpl & 0x0F)) {
- rsamples = samples - Demod.samples;
- LED_B_ON();
-
- // timestamp, as a count of samples
- trace[traceLen++] = ((rsamples >> 0) & 0xff);
- trace[traceLen++] = ((rsamples >> 8) & 0xff);
- trace[traceLen++] = ((rsamples >> 16) & 0xff);
- trace[traceLen++] = 0x80 | ((rsamples >> 24) & 0xff);
- trace[traceLen++] = ((Demod.parityBits >> 0) & 0xff);
- trace[traceLen++] = ((Demod.parityBits >> 8) & 0xff);
- trace[traceLen++] = ((Demod.parityBits >> 16) & 0xff);
- trace[traceLen++] = ((Demod.parityBits >> 24) & 0xff);
- // length
- trace[traceLen++] = Demod.len;
- memcpy(trace+traceLen, receivedResponse, Demod.len);
- traceLen += Demod.len;
- if(traceLen > TRACE_LENGTH) break;
-
- triggered = TRUE;
-
- // And ready to receive another response.
- memset(&Demod, 0, sizeof(Demod));
- Demod.output = receivedResponse;
- Demod.state = DEMOD_UNSYNCD;
- LED_C_OFF();
- }
+ LED_A_ON();
+ WDT_HIT();
- if(BUTTON_PRESS()) {
- DbpString("cancelled_a");
- goto done;
- }
- }
+ int register readBufDataP = data - dmaBuf;
+ int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR;
+ if (readBufDataP <= dmaBufDataP){
+ dataLen = dmaBufDataP - readBufDataP;
+ } else {
+ dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP + 1;
+ }
+ // test for length of buffer
+ if(dataLen > maxDataLen) {
+ maxDataLen = dataLen;
+ if(dataLen > 400) {
+ Dbprintf("blew circular buffer! dataLen=0x%x", dataLen);
+ goto done;
+ }
+ }
+ if(dataLen < 1) continue;
+
+ // primary buffer was stopped( <-- we lost data!
+ if (!AT91C_BASE_PDC_SSC->PDC_RCR) {
+ AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dmaBuf;
+ AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE;
+ }
+ // secondary buffer sets as primary, secondary buffer was stopped
+ if (!AT91C_BASE_PDC_SSC->PDC_RNCR) {
+ AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf;
+ AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE;
+ }
+
+ LED_A_OFF();
+
+ rsamples += 4;
+ if(MillerDecoding((data[0] & 0xF0) >> 4)) {
+ LED_C_ON();
+
+ // check - if there is a short 7bit request from reader
+ if ((!triggered) && (param & 0x02) && (Uart.byteCnt == 1) && (Uart.bitCnt = 9)) triggered = TRUE;
+
+ if(triggered) {
+ if (!LogTrace(receivedCmd, Uart.byteCnt, 0 - Uart.samples, Uart.parityBits, TRUE)) break;
+ }
+ /* And ready to receive another command. */
+ Uart.state = STATE_UNSYNCD;
+ /* And also reset the demod code, which might have been */
+ /* false-triggered by the commands from the reader. */
+ Demod.state = DEMOD_UNSYNCD;
+ LED_B_OFF();
+ }
- DbpString("COMMAND FINISHED");
+ if(ManchesterDecoding(data[0] & 0x0F)) {
+ LED_B_ON();
+
+ if (!LogTrace(receivedResponse, Demod.len, 0 - Demod.samples, Demod.parityBits, FALSE)) break;
+
+ if ((!triggered) && (param & 0x01)) triggered = TRUE;
+
+ // And ready to receive another response.
+ memset(&Demod, 0, sizeof(Demod));
+ Demod.output = receivedResponse;
+ Demod.state = DEMOD_UNSYNCD;
+ LED_C_OFF();
+ }
+
+ data++;
+ if(data > dmaBuf + DMA_BUFFER_SIZE) {
+ data = dmaBuf;
+ }
+ } // main cycle
- Dbprintf("%x %x %x", maxBehindBy, Uart.state, Uart.byteCnt);
- Dbprintf("%x %x %x", Uart.byteCntMax, traceLen, (int)Uart.output[0]);
+ DbpString("COMMAND FINISHED");
done:
- AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS;
- Dbprintf("%x %x %x", maxBehindBy, Uart.state, Uart.byteCnt);
- Dbprintf("%x %x %x", Uart.byteCntMax, traceLen, (int)Uart.output[0]);
- LED_A_OFF();
- LED_B_OFF();
- LED_C_OFF();
- LED_D_OFF();
+ AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS;
+ Dbprintf("maxDataLen=%x, Uart.state=%x, Uart.byteCnt=%x", maxDataLen, Uart.state, Uart.byteCnt);
+ Dbprintf("Uart.byteCntMax=%x, traceLen=%x, Uart.output[0]=%08x", Uart.byteCntMax, traceLen, (int)Uart.output[0]);
+ LEDsoff();
}
//-----------------------------------------------------------------------------
static void CodeIso14443aAsTagPar(const uint8_t *cmd, int len, uint32_t dwParity)
{
int i;
-// int oddparity;
ToSendReset();
uint8_t b = cmd[i];
// Data bits
-// oddparity = 0x01;
for(j = 0; j < 8; j++) {
-// oddparity ^= (b & 1);
if(b & 1) {
ToSend[++ToSendMax] = SEC_D;
} else {
b >>= 1;
}
- // Get the parity bit
+ // Get the parity bit
if ((dwParity >> i) & 0x01) {
ToSend[++ToSendMax] = SEC_D;
} else {
ToSend[++ToSendMax] = SEC_E;
}
-
- // Parity bit
-// if(oddparity) {
-// ToSend[++ToSendMax] = SEC_D;
-// } else {
-// ToSend[++ToSendMax] = SEC_E;
-// }
-
-// if (oddparity != ((dwParity >> i) & 0x01))
-// Dbprintf("par error. i=%d", i);
}
// Send stopbit
ToSend[++ToSendMax] = SEC_F;
- // Flush the buffer in FPGA!!
- for(i = 0; i < 5; i++) {
- ToSend[++ToSendMax] = SEC_F;
- }
-
// Convert from last byte pos to length
ToSendMax++;
-
- // Add a few more for slop
-// ToSend[ToSendMax++] = 0x00;
-// ToSend[ToSendMax++] = 0x00;
}
static void CodeIso14443aAsTag(const uint8_t *cmd, int len){
}
}
}
+
static int EmSendCmd14443aRaw(uint8_t *resp, int respLen, int correctionNeeded);
+int EmSend4bitEx(uint8_t resp, int correctionNeeded);
+int EmSend4bit(uint8_t resp);
+int EmSendCmdExPar(uint8_t *resp, int respLen, int correctionNeeded, uint32_t par);
+int EmSendCmdExPar(uint8_t *resp, int respLen, int correctionNeeded, uint32_t par);
+int EmSendCmdEx(uint8_t *resp, int respLen, int correctionNeeded);
+int EmSendCmd(uint8_t *resp, int respLen);
+int EmSendCmdPar(uint8_t *resp, int respLen, uint32_t par);
//-----------------------------------------------------------------------------
// Main loop of simulated tag: receive commands from reader, decide what
// response to send, and send it.
//-----------------------------------------------------------------------------
-void SimulateIso14443aTag(int tagType, int TagUid)
+void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data)
{
- // This function contains the tag emulation
-
- // Prepare protocol messages
- // static const uint8_t cmd1[] = { 0x26 };
-// static const uint8_t response1[] = { 0x02, 0x00 }; // Says: I am Mifare 4k - original line - greg
-//
- static const uint8_t response1[] = { 0x44, 0x03 }; // Says: I am a DESFire Tag, ph33r me
-// static const uint8_t response1[] = { 0x44, 0x00 }; // Says: I am a ULTRALITE Tag, 0wn me
-
- // UID response
- // static const uint8_t cmd2[] = { 0x93, 0x20 };
- //static const uint8_t response2[] = { 0x9a, 0xe5, 0xe4, 0x43, 0xd8 }; // original value - greg
+ // Enable and clear the trace
+ tracing = TRUE;
+ iso14a_clear_trace();
-// my desfire
- static const uint8_t response2[] = { 0x88, 0x04, 0x21, 0x3f, 0x4d }; // known uid - note cascade (0x88), 2nd byte (0x04) = NXP/Phillips
+ // This function contains the tag emulation
+ uint8_t sak;
+ // The first response contains the ATQA (note: bytes are transmitted in reverse order).
+ uint8_t response1[2];
+
+ switch (tagType) {
+ case 1: { // MIFARE Classic
+ // Says: I am Mifare 1k - original line
+ response1[0] = 0x04;
+ response1[1] = 0x00;
+ sak = 0x08;
+ } break;
+ case 2: { // MIFARE Ultralight
+ // Says: I am a stupid memory tag, no crypto
+ response1[0] = 0x04;
+ response1[1] = 0x00;
+ sak = 0x00;
+ } break;
+ case 3: { // MIFARE DESFire
+ // Says: I am a DESFire tag, ph33r me
+ response1[0] = 0x04;
+ response1[1] = 0x03;
+ sak = 0x20;
+ } break;
+ case 4: { // ISO/IEC 14443-4
+ // Says: I am a javacard (JCOP)
+ response1[0] = 0x04;
+ response1[1] = 0x00;
+ sak = 0x28;
+ } break;
+ default: {
+ Dbprintf("Error: unkown tagtype (%d)",tagType);
+ return;
+ } break;
+ }
+
+ // The second response contains the (mandatory) first 24 bits of the UID
+ uint8_t response2[5];
+
+ // Check if the uid uses the (optional) part
+ uint8_t response2a[5];
+ if (uid_2nd) {
+ response2[0] = 0x88;
+ num_to_bytes(uid_1st,3,response2+1);
+ num_to_bytes(uid_2nd,4,response2a);
+ response2a[4] = response2a[0] ^ response2a[1] ^ response2a[2] ^ response2a[3];
+
+ // Configure the ATQA and SAK accordingly
+ response1[0] |= 0x40;
+ sak |= 0x04;
+ } else {
+ num_to_bytes(uid_1st,4,response2);
+ // Configure the ATQA and SAK accordingly
+ response1[0] &= 0xBF;
+ sak &= 0xFB;
+ }
-// When reader selects us during cascade1 it will send cmd3
-//uint8_t response3[] = { 0x04, 0x00, 0x00 }; // SAK Select (cascade1) successful response (ULTRALITE)
-uint8_t response3[] = { 0x24, 0x00, 0x00 }; // SAK Select (cascade1) successful response (DESFire)
-ComputeCrc14443(CRC_14443_A, response3, 1, &response3[1], &response3[2]);
+ // Calculate the BitCountCheck (BCC) for the first 4 bytes of the UID.
+ response2[4] = response2[0] ^ response2[1] ^ response2[2] ^ response2[3];
-// send cascade2 2nd half of UID
-static const uint8_t response2a[] = { 0x51, 0x48, 0x1d, 0x80, 0x84 }; // uid - cascade2 - 2nd half (4 bytes) of UID+ BCCheck
-// NOTE : THE CRC on the above may be wrong as I have obfuscated the actual UID
+ // Prepare the mandatory SAK (for 4 and 7 byte UID)
+ uint8_t response3[3];
+ response3[0] = sak;
+ ComputeCrc14443(CRC_14443_A, response3, 1, &response3[1], &response3[2]);
-// When reader selects us during cascade2 it will send cmd3a
-//uint8_t response3a[] = { 0x00, 0x00, 0x00 }; // SAK Select (cascade2) successful response (ULTRALITE)
-uint8_t response3a[] = { 0x20, 0x00, 0x00 }; // SAK Select (cascade2) successful response (DESFire)
-ComputeCrc14443(CRC_14443_A, response3a, 1, &response3a[1], &response3a[2]);
+ // Prepare the optional second SAK (for 7 byte UID), drop the cascade bit
+ uint8_t response3a[3];
+ response3a[0] = sak & 0xFB;
+ ComputeCrc14443(CRC_14443_A, response3a, 1, &response3a[1], &response3a[2]);
- static const uint8_t response5[] = { 0x00, 0x00, 0x00, 0x00 }; // Very random tag nonce
+ uint8_t response5[] = { 0x00, 0x00, 0x00, 0x00 }; // Very random tag nonce
+ uint8_t response6[] = { 0x03, 0x3B, 0x00, 0x00, 0x00 }; // dummy ATS (pseudo-ATR), answer to RATS
+ ComputeCrc14443(CRC_14443_A, response6, 3, &response6[3], &response6[4]);
- uint8_t *resp;
- int respLen;
+ uint8_t *resp = NULL;
+ int respLen;
- // Longest possible response will be 16 bytes + 2 CRC = 18 bytes
+ // Longest possible response will be 16 bytes + 2 CRC = 18 bytes
// This will need
// 144 data bits (18 * 8)
// 18 parity bits
// 166 bytes, since every bit that needs to be send costs us a byte
//
- // Respond with card type
- uint8_t *resp1 = (((uint8_t *)BigBuf) + 800);
- int resp1Len;
+ // Respond with card type
+ uint8_t *resp1 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET);
+ int resp1Len;
- // Anticollision cascade1 - respond with uid
- uint8_t *resp2 = (((uint8_t *)BigBuf) + 970);
- int resp2Len;
+ // Anticollision cascade1 - respond with uid
+ uint8_t *resp2 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + 166);
+ int resp2Len;
- // Anticollision cascade2 - respond with 2nd half of uid if asked
- // we're only going to be asked if we set the 1st byte of the UID (during cascade1) to 0x88
- uint8_t *resp2a = (((uint8_t *)BigBuf) + 1140);
- int resp2aLen;
+ // Anticollision cascade2 - respond with 2nd half of uid if asked
+ // we're only going to be asked if we set the 1st byte of the UID (during cascade1) to 0x88
+ uint8_t *resp2a = (((uint8_t *)BigBuf) + 1140);
+ int resp2aLen;
- // Acknowledge select - cascade 1
- uint8_t *resp3 = (((uint8_t *)BigBuf) + 1310);
- int resp3Len;
+ // Acknowledge select - cascade 1
+ uint8_t *resp3 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + (166*2));
+ int resp3Len;
- // Acknowledge select - cascade 2
- uint8_t *resp3a = (((uint8_t *)BigBuf) + 1480);
- int resp3aLen;
+ // Acknowledge select - cascade 2
+ uint8_t *resp3a = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + (166*3));
+ int resp3aLen;
- // Response to a read request - not implemented atm
- uint8_t *resp4 = (((uint8_t *)BigBuf) + 1550);
- int resp4Len;
+ // Response to a read request - not implemented atm
+ uint8_t *resp4 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + (166*4));
+// int resp4Len;
- // Authenticate response - nonce
- uint8_t *resp5 = (((uint8_t *)BigBuf) + 1720);
- int resp5Len;
+ // Authenticate response - nonce
+ uint8_t *resp5 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + (166*5));
+ int resp5Len;
- uint8_t *receivedCmd = (uint8_t *)BigBuf;
- int len;
+ // Authenticate response - nonce
+ uint8_t *resp6 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + (166*6));
+ int resp6Len;
- int i;
- int u;
- uint8_t b;
+ uint8_t *receivedCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET);
+ int len;
// To control where we are in the protocol
int order = 0;
int happened = 0;
int happened2 = 0;
- int cmdsRecvd = 0;
-
- int fdt_indicator;
+ int cmdsRecvd = 0;
+ uint8_t* respdata = NULL;
+ int respsize = 0;
+// uint8_t nack = 0x04;
- memset(receivedCmd, 0x44, 400);
+ memset(receivedCmd, 0x44, RECV_CMD_SIZE);
// Prepare the responses of the anticollision phase
// there will be not enough time to do this at the moment the reader sends it REQA
// Answer to request
CodeIso14443aAsTag(response1, sizeof(response1));
- memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax;
+ memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax;
// Send our UID (cascade 1)
CodeIso14443aAsTag(response2, sizeof(response2));
- memcpy(resp2, ToSend, ToSendMax); resp2Len = ToSendMax;
+ memcpy(resp2, ToSend, ToSendMax); resp2Len = ToSendMax;
// Answer to select (cascade1)
CodeIso14443aAsTag(response3, sizeof(response3));
- memcpy(resp3, ToSend, ToSendMax); resp3Len = ToSendMax;
+ memcpy(resp3, ToSend, ToSendMax); resp3Len = ToSendMax;
// Send the cascade 2 2nd part of the uid
CodeIso14443aAsTag(response2a, sizeof(response2a));
- memcpy(resp2a, ToSend, ToSendMax); resp2aLen = ToSendMax;
+ memcpy(resp2a, ToSend, ToSendMax); resp2aLen = ToSendMax;
// Answer to select (cascade 2)
CodeIso14443aAsTag(response3a, sizeof(response3a));
- memcpy(resp3a, ToSend, ToSendMax); resp3aLen = ToSendMax;
+ memcpy(resp3a, ToSend, ToSendMax); resp3aLen = ToSendMax;
// Strange answer is an example of rare message size (3 bits)
CodeStrangeAnswerAsTag();
- memcpy(resp4, ToSend, ToSendMax); resp4Len = ToSendMax;
+ memcpy(resp4, ToSend, ToSendMax);// resp4Len = ToSendMax;
// Authentication answer (random nonce)
CodeIso14443aAsTag(response5, sizeof(response5));
- memcpy(resp5, ToSend, ToSendMax); resp5Len = ToSendMax;
+ memcpy(resp5, ToSend, ToSendMax); resp5Len = ToSendMax;
- // We need to listen to the high-frequency, peak-detected path.
- SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
- FpgaSetupSsc();
+ // dummy ATS (pseudo-ATR), answer to RATS
+ CodeIso14443aAsTag(response6, sizeof(response6));
+ memcpy(resp6, ToSend, ToSendMax); resp6Len = ToSendMax;
- cmdsRecvd = 0;
+ // We need to listen to the high-frequency, peak-detected path.
+ SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
+ FpgaSetupSsc();
- LED_A_ON();
- for(;;) {
+ cmdsRecvd = 0;
- if(!GetIso14443aCommandFromReader(receivedCmd, &len, 100)) {
- DbpString("button press");
- break;
- }
- // doob - added loads of debug strings so we can see what the reader is saying to us during the sim as hi14alist is not populated
- // Okay, look at the command now.
- lastorder = order;
- i = 1; // first byte transmitted
- if(receivedCmd[0] == 0x26) {
- // Received a REQUEST
+ LED_A_ON();
+ for(;;) {
+
+ if(!GetIso14443aCommandFromReader(receivedCmd, &len, RECV_CMD_SIZE)) {
+ DbpString("button press");
+ break;
+ }
+
+ if (tracing) {
+ LogTrace(receivedCmd,len, 0, Uart.parityBits, TRUE);
+ }
+
+ // doob - added loads of debug strings so we can see what the reader is saying to us during the sim as hi14alist is not populated
+ // Okay, look at the command now.
+ lastorder = order;
+ if(receivedCmd[0] == 0x26) { // Received a REQUEST
resp = resp1; respLen = resp1Len; order = 1;
- //DbpString("Hello request from reader:");
- } else if(receivedCmd[0] == 0x52) {
- // Received a WAKEUP
+ respdata = response1;
+ respsize = sizeof(response1);
+ } else if(receivedCmd[0] == 0x52) { // Received a WAKEUP
resp = resp1; respLen = resp1Len; order = 6;
-// //DbpString("Wakeup request from reader:");
-
- } else if(receivedCmd[1] == 0x20 && receivedCmd[0] == 0x93) { // greg - cascade 1 anti-collision
- // Received request for UID (cascade 1)
+ respdata = response1;
+ respsize = sizeof(response1);
+ } else if(receivedCmd[1] == 0x20 && receivedCmd[0] == 0x93) { // Received request for UID (cascade 1)
resp = resp2; respLen = resp2Len; order = 2;
-// DbpString("UID (cascade 1) request from reader:");
-// DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]);
-
-
- } else if(receivedCmd[1] == 0x20 && receivedCmd[0] ==0x95) { // greg - cascade 2 anti-collision
- // Received request for UID (cascade 2)
+ respdata = response2;
+ respsize = sizeof(response2);
+ } else if(receivedCmd[1] == 0x20 && receivedCmd[0] == 0x95) { // Received request for UID (cascade 2)
resp = resp2a; respLen = resp2aLen; order = 20;
-// DbpString("UID (cascade 2) request from reader:");
-// DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]);
-
-
- } else if(receivedCmd[1] == 0x70 && receivedCmd[0] ==0x93) { // greg - cascade 1 select
- // Received a SELECT
+ respdata = response2a;
+ respsize = sizeof(response2a);
+ } else if(receivedCmd[1] == 0x70 && receivedCmd[0] == 0x93) { // Received a SELECT (cascade 1)
resp = resp3; respLen = resp3Len; order = 3;
-// DbpString("Select (cascade 1) request from reader:");
-// DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]);
-
-
- } else if(receivedCmd[1] == 0x70 && receivedCmd[0] ==0x95) { // greg - cascade 2 select
- // Received a SELECT
+ respdata = response3;
+ respsize = sizeof(response3);
+ } else if(receivedCmd[1] == 0x70 && receivedCmd[0] == 0x95) { // Received a SELECT (cascade 2)
resp = resp3a; respLen = resp3aLen; order = 30;
-// DbpString("Select (cascade 2) request from reader:");
-// DbpIntegers(receivedCmd[0], receivedCmd[1], receivedCmd[2]);
-
-
- } else if(receivedCmd[0] == 0x30) {
- // Received a READ
- resp = resp4; respLen = resp4Len; order = 4; // Do nothing
- Dbprintf("Read request from reader: %x %x %x",
- receivedCmd[0], receivedCmd[1], receivedCmd[2]);
-
-
- } else if(receivedCmd[0] == 0x50) {
- // Received a HALT
- resp = resp1; respLen = 0; order = 5; // Do nothing
- DbpString("Reader requested we HALT!:");
-
- } else if(receivedCmd[0] == 0x60) {
- // Received an authentication request
- resp = resp5; respLen = resp5Len; order = 7;
- Dbprintf("Authenticate request from reader: %x %x %x",
- receivedCmd[0], receivedCmd[1], receivedCmd[2]);
-
- } else if(receivedCmd[0] == 0xE0) {
- // Received a RATS request
- resp = resp1; respLen = 0;order = 70;
- Dbprintf("RATS request from reader: %x %x %x",
- receivedCmd[0], receivedCmd[1], receivedCmd[2]);
- } else {
- // Never seen this command before
- Dbprintf("Unknown command received from reader (len=%d): %x %x %x %x %x %x %x %x %x",
- len,
- receivedCmd[0], receivedCmd[1], receivedCmd[2],
- receivedCmd[3], receivedCmd[4], receivedCmd[5],
- receivedCmd[6], receivedCmd[7], receivedCmd[8]);
+ respdata = response3a;
+ respsize = sizeof(response3a);
+ } else if(receivedCmd[0] == 0x30) { // Received a (plain) READ
+// resp = resp4; respLen = resp4Len; order = 4; // Do nothing
+// respdata = &nack;
+// respsize = sizeof(nack); // 4-bit answer
+ EmSendCmdEx(data+(4*receivedCmd[0]),16,false);
+ Dbprintf("Read request from reader: %x %x",receivedCmd[0],receivedCmd[1]);
+ // We already responded, do not send anything with the EmSendCmd14443aRaw() that is called below
+ respLen = 0;
+ } else if(receivedCmd[0] == 0x50) { // Received a HALT
+// DbpString("Reader requested we HALT!:");
// Do not respond
resp = resp1; respLen = 0; order = 0;
- }
+ respdata = NULL;
+ respsize = 0;
+ } else if(receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61) { // Received an authentication request
+ resp = resp5; respLen = resp5Len; order = 7;
+ respdata = response5;
+ respsize = sizeof(response5);
+ } else if(receivedCmd[0] == 0xE0) { // Received a RATS request
+ resp = resp6; respLen = resp6Len; order = 70;
+ respdata = response6;
+ respsize = sizeof(response6);
+ } else {
+ if (order == 7 && len ==8) {
+ uint32_t nr = bytes_to_num(receivedCmd,4);
+ uint32_t ar = bytes_to_num(receivedCmd+4,4);
+ Dbprintf("Auth attempt {nr}{ar}: %08x %08x",nr,ar);
+ } else {
+ // Never seen this command before
+ Dbprintf("Received unknown command (len=%d):",len);
+ Dbhexdump(len,receivedCmd,false);
+ }
+ // Do not respond
+ resp = resp1; respLen = 0; order = 0;
+ respdata = NULL;
+ respsize = 0;
+ }
// Count number of wakeups received after a halt
if(order == 6 && lastorder == 5) { happened++; }
// Look at last parity bit to determine timing of answer
if((Uart.parityBits & 0x01) || receivedCmd[0] == 0x52) {
// 1236, so correction bit needed
- i = 0;
+ //i = 0;
}
- memset(receivedCmd, 0x44, 32);
-
if(cmdsRecvd > 999) {
DbpString("1000 commands later...");
- break;
- }
- else {
+ break;
+ } else {
cmdsRecvd++;
}
- if(respLen <= 0) continue;
- //----------------------------
- u = 0;
- b = 0x00;
- fdt_indicator = FALSE;
-
- EmSendCmd14443aRaw(resp, respLen, receivedCmd[0] == 0x52);
-/* // Modulate Manchester
- FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD);
- AT91C_BASE_SSC->SSC_THR = 0x00;
- FpgaSetupSsc();
-
- // ### Transmit the response ###
- u = 0;
- b = 0x00;
- fdt_indicator = FALSE;
- for(;;) {
- if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
- volatile uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
- (void)b;
- }
- if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
- if(i > respLen) {
- b = 0x00;
- u++;
- } else {
- b = resp[i];
- i++;
- }
- AT91C_BASE_SSC->SSC_THR = b;
-
- if(u > 4) {
- break;
- }
- }
- if(BUTTON_PRESS()) {
- break;
+ if(respLen > 0) {
+ EmSendCmd14443aRaw(resp, respLen, receivedCmd[0] == 0x52);
+ }
+
+ if (tracing) {
+ if (respdata != NULL) {
+ LogTrace(respdata,respsize, 0, SwapBits(GetParity(respdata,respsize),respsize), FALSE);
}
- }
-*/
- }
+ if(traceLen > TRACE_SIZE) {
+ DbpString("Trace full");
+ break;
+ }
+ }
+
+ memset(receivedCmd, 0x44, RECV_CMD_SIZE);
+ }
Dbprintf("%x %x %x", happened, happened2, cmdsRecvd);
LED_A_OFF();
}
//-----------------------------------------------------------------------------
-// Code a 7-bit command without parity bit
-// This is especially for 0x26 and 0x52 (REQA and WUPA)
-//-----------------------------------------------------------------------------
-void ShortFrameFromReader(const uint8_t bt)
-{
- int j;
- int last;
- uint8_t b;
-
- ToSendReset();
-
- // Start of Communication (Seq. Z)
- ToSend[++ToSendMax] = SEC_Z;
- last = 0;
-
- b = bt;
- for(j = 0; j < 7; j++) {
- if(b & 1) {
- // Sequence X
- ToSend[++ToSendMax] = SEC_X;
- last = 1;
- } else {
- if(last == 0) {
- // Sequence Z
- ToSend[++ToSendMax] = SEC_Z;
- }
- else {
- // Sequence Y
- ToSend[++ToSendMax] = SEC_Y;
- last = 0;
- }
- }
- b >>= 1;
- }
-
- // End of Communication
- if(last == 0) {
- // Sequence Z
- ToSend[++ToSendMax] = SEC_Z;
- }
- else {
- // Sequence Y
- ToSend[++ToSendMax] = SEC_Y;
- last = 0;
- }
- // Sequence Y
- ToSend[++ToSendMax] = SEC_Y;
-
- // Just to be sure!
- ToSend[++ToSendMax] = SEC_Y;
- ToSend[++ToSendMax] = SEC_Y;
- ToSend[++ToSendMax] = SEC_Y;
-
- // Convert from last character reference to length
- ToSendMax++;
-}
-
-//-----------------------------------------------------------------------------
-// Prepare reader command to send to FPGA
-//
+// Prepare reader command (in bits, support short frames) to send to FPGA
//-----------------------------------------------------------------------------
-void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity)
+void CodeIso14443aBitsAsReaderPar(const uint8_t * cmd, int bits, uint32_t dwParity)
{
int i, j;
int last;
ToSend[++ToSendMax] = SEC_Z;
last = 0;
+ size_t bytecount = nbytes(bits);
// Generate send structure for the data bits
- for (i = 0; i < len; i++) {
+ for (i = 0; i < bytecount; i++) {
// Get the current byte to send
b = cmd[i];
+ size_t bitsleft = MIN((bits-(i*8)),8);
- for (j = 0; j < 8; j++) {
+ for (j = 0; j < bitsleft; j++) {
if (b & 1) {
// Sequence X
ToSend[++ToSendMax] = SEC_X;
b >>= 1;
}
- // Get the parity bit
- if ((dwParity >> i) & 0x01) {
- // Sequence X
- ToSend[++ToSendMax] = SEC_X;
- last = 1;
- } else {
- if (last == 0) {
- // Sequence Z
- ToSend[++ToSendMax] = SEC_Z;
+ // Only transmit (last) parity bit if we transmitted a complete byte
+ if (j == 8) {
+ // Get the parity bit
+ if ((dwParity >> i) & 0x01) {
+ // Sequence X
+ ToSend[++ToSendMax] = SEC_X;
+ last = 1;
} else {
- // Sequence Y
- ToSend[++ToSendMax] = SEC_Y;
- last = 0;
+ if (last == 0) {
+ // Sequence Z
+ ToSend[++ToSendMax] = SEC_Z;
+ } else {
+ // Sequence Y
+ ToSend[++ToSendMax] = SEC_Y;
+ last = 0;
+ }
}
}
}
ToSendMax++;
}
+//-----------------------------------------------------------------------------
+// Prepare reader command to send to FPGA
+//-----------------------------------------------------------------------------
+void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity)
+{
+ CodeIso14443aBitsAsReaderPar(cmd,len*8,dwParity);
+}
+
//-----------------------------------------------------------------------------
// Wait for commands from reader
// Stop when button is pressed (return 1) or field was gone (return 2)
}
}
-void ReaderTransmitShort(const uint8_t* bt)
-{
- int wait = 0;
- int samples = 0;
-
- ShortFrameFromReader(*bt);
-
- // Select the card
- TransmitFor14443a(ToSend, ToSendMax, &samples, &wait);
-
- // Store reader command in buffer
- if (tracing) LogTrace(bt,1,0,GetParity(bt,1),TRUE);
-}
-
-void ReaderTransmitPar(uint8_t* frame, int len, uint32_t par)
+void ReaderTransmitBitsPar(uint8_t* frame, int bits, uint32_t par)
{
int wait = 0;
int samples = 0;
-
+
// This is tied to other size changes
// uint8_t* frame_addr = ((uint8_t*)BigBuf) + 2024;
- CodeIso14443aAsReaderPar(frame,len,par);
-
+ CodeIso14443aBitsAsReaderPar(frame,bits,par);
+
// Select the card
TransmitFor14443a(ToSend, ToSendMax, &samples, &wait);
if(trigger)
LED_A_ON();
-
+
// Store reader command in buffer
- if (tracing) LogTrace(frame,len,0,par,TRUE);
+ if (tracing) LogTrace(frame,nbytes(bits),0,par,TRUE);
}
+void ReaderTransmitPar(uint8_t* frame, int len, uint32_t par)
+{
+ ReaderTransmitBitsPar(frame,len*8,par);
+}
void ReaderTransmit(uint8_t* frame, int len)
{
// Generate parity and redirect
- ReaderTransmitPar(frame,len,GetParity(frame,len));
+ ReaderTransmitBitsPar(frame,len*8,GetParity(frame,len));
}
int ReaderReceive(uint8_t* receivedAnswer)
/* performs iso14443a anticolision procedure
* fills the uid pointer unless NULL
* fills resp_data unless NULL */
-int iso14443a_select_card(uint8_t * uid_ptr, iso14a_card_select_t * resp_data, uint32_t * cuid_ptr) {
- uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP
- uint8_t sel_all[] = { 0x93,0x20 };
- uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
- uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0
-
- uint8_t* resp = (((uint8_t *)BigBuf) + 3560); // was 3560 - tied to other size changes
-
- uint8_t sak = 0x04; // cascade uid
- int cascade_level = 0;
-
- int len;
+int iso14443a_select_card(byte_t* uid_ptr, iso14a_card_select_t* p_hi14a_card, uint32_t* cuid_ptr) {
+ uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP
+ uint8_t sel_all[] = { 0x93,0x20 };
+ uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
+ uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0
+ uint8_t* resp = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); // was 3560 - tied to other size changes
+ byte_t uid_resp[4];
+ size_t uid_resp_len;
+
+ uint8_t sak = 0x04; // cascade uid
+ int cascade_level = 0;
+ int len;
+
+ // Broadcast for a card, WUPA (0x52) will force response from all cards in the field
+ ReaderTransmitBitsPar(wupa,7,0);
+ // Receive the ATQA
+ if(!ReaderReceive(resp)) return 0;
+// Dbprintf("atqa: %02x %02x",resp[0],resp[1]);
+
+ if(p_hi14a_card) {
+ memcpy(p_hi14a_card->atqa, resp, 2);
+ p_hi14a_card->uidlen = 0;
+ memset(p_hi14a_card->uid,0,10);
+ }
- // clear uid
- memset(uid_ptr, 0, 8);
-
- // Broadcast for a card, WUPA (0x52) will force response from all cards in the field
- ReaderTransmitShort(wupa);
- // Receive the ATQA
- if(!ReaderReceive(resp)) return 0;
+ // clear uid
+ if (uid_ptr) {
+ memset(uid_ptr,0,8);
+ }
- if(resp_data)
- memcpy(resp_data->atqa, resp, 2);
-
- // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in
- // which case we need to make a cascade 2 request and select - this is a long UID
- // While the UID is not complete, the 3nd bit (from the right) is set in the SAK.
- for(; sak & 0x04; cascade_level++)
- {
- // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97)
- sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2;
+ // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in
+ // which case we need to make a cascade 2 request and select - this is a long UID
+ // While the UID is not complete, the 3nd bit (from the right) is set in the SAK.
+ for(; sak & 0x04; cascade_level++) {
+ // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97)
+ sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2;
- // SELECT_ALL
- ReaderTransmit(sel_all,sizeof(sel_all));
- if (!ReaderReceive(resp)) return 0;
- if(uid_ptr) memcpy(uid_ptr + cascade_level*4, resp, 4);
-
+ // SELECT_ALL
+ ReaderTransmit(sel_all,sizeof(sel_all));
+ if (!ReaderReceive(resp)) return 0;
+
+ // First backup the current uid
+ memcpy(uid_resp,resp,4);
+ uid_resp_len = 4;
+ // Dbprintf("uid: %02x %02x %02x %02x",uid_resp[0],uid_resp[1],uid_resp[2],uid_resp[3]);
+
// calculate crypto UID
- if(cuid_ptr) *cuid_ptr = bytes_to_num(resp, 4);
+ if(cuid_ptr) {
+ *cuid_ptr = bytes_to_num(uid_resp, 4);
+ }
- // Construct SELECT UID command
+ // Construct SELECT UID command
memcpy(sel_uid+2,resp,5);
- AppendCrc14443a(sel_uid,7);
- ReaderTransmit(sel_uid,sizeof(sel_uid));
+ AppendCrc14443a(sel_uid,7);
+ ReaderTransmit(sel_uid,sizeof(sel_uid));
+
+ // Receive the SAK
+ if (!ReaderReceive(resp)) return 0;
+ sak = resp[0];
+
+ // Test if more parts of the uid are comming
+ if ((sak & 0x04) && uid_resp[0] == 0x88) {
+ // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of:
+ // http://www.nxp.com/documents/application_note/AN10927.pdf
+ memcpy(uid_resp, uid_resp + 1, 3);
+ uid_resp_len = 3;
+ }
+
+ if(uid_ptr) {
+ memcpy(uid_ptr + (cascade_level*3), uid_resp, uid_resp_len);
+ }
+
+ if(p_hi14a_card) {
+ memcpy(p_hi14a_card->uid + (cascade_level*3), uid_resp, uid_resp_len);
+ p_hi14a_card->uidlen += uid_resp_len;
+ }
+ }
- // Receive the SAK
- if (!ReaderReceive(resp)) return 0;
- sak = resp[0];
- }
- if(resp_data) {
- resp_data->sak = sak;
- resp_data->ats_len = 0;
- }
- //-- this byte not UID, it CT. http://www.nxp.com/documents/application_note/AN10927.pdf page 3
- if (uid_ptr[0] == 0x88) {
- memcpy(uid_ptr, uid_ptr + 1, 7);
- uid_ptr[7] = 0;
- }
+ if(p_hi14a_card) {
+ p_hi14a_card->sak = sak;
+ p_hi14a_card->ats_len = 0;
+ }
- if( (sak & 0x20) == 0)
- return 2; // non iso14443a compliant tag
+ if( (sak & 0x20) == 0) {
+ return 2; // non iso14443a compliant tag
+ }
- // Request for answer to select
- if(resp_data) { // JCOP cards - if reader sent RATS then there is no MIFARE session at all!!!
- AppendCrc14443a(rats, 2);
- ReaderTransmit(rats, sizeof(rats));
-
- if (!(len = ReaderReceive(resp))) return 0;
-
- memcpy(resp_data->ats, resp, sizeof(resp_data->ats));
- resp_data->ats_len = len;
- }
+ // Request for answer to select
+ AppendCrc14443a(rats, 2);
+ ReaderTransmit(rats, sizeof(rats));
+
+ if (!(len = ReaderReceive(resp))) return 0;
+
+ if(p_hi14a_card) {
+ memcpy(p_hi14a_card->ats, resp, sizeof(p_hi14a_card->ats));
+ p_hi14a_card->ats_len = len;
+ }
- return 1;
+ // reset the PCB block number
+ iso14_pcb_blocknum = 0;
+ return 1;
}
void iso14443a_setup() {
- // Setup SSC
- FpgaSetupSsc();
+ // Set up the synchronous serial port
+ FpgaSetupSsc();
// Start from off (no field generated)
// Signal field is off with the appropriate LED
LED_D_OFF();
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- SpinDelay(200);
+ SpinDelay(50);
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
// Signal field is on with the appropriate LED
LED_D_ON();
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
- SpinDelay(200);
+ SpinDelay(50);
iso14a_timeout = 2048; //default
}
int iso14_apdu(uint8_t * cmd, size_t cmd_len, void * data) {
uint8_t real_cmd[cmd_len+4];
real_cmd[0] = 0x0a; //I-Block
+ // put block number into the PCB
+ real_cmd[0] |= iso14_pcb_blocknum;
real_cmd[1] = 0x00; //CID: 0 //FIXME: allow multiple selected cards
memcpy(real_cmd+2, cmd, cmd_len);
AppendCrc14443a(real_cmd,cmd_len+2);
ReaderTransmit(real_cmd, cmd_len+4);
size_t len = ReaderReceive(data);
- if(!len)
- return -1; //DATA LINK ERROR
-
+ uint8_t * data_bytes = (uint8_t *) data;
+ if (!len)
+ return 0; //DATA LINK ERROR
+ // if we received an I- or R(ACK)-Block with a block number equal to the
+ // current block number, toggle the current block number
+ else if (len >= 4 // PCB+CID+CRC = 4 bytes
+ && ((data_bytes[0] & 0xC0) == 0 // I-Block
+ || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0
+ && (data_bytes[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers
+ {
+ iso14_pcb_blocknum ^= 1;
+ }
+
return len;
}
-
//-----------------------------------------------------------------------------
// Read an ISO 14443a tag. Send out commands and store answers.
//
//-----------------------------------------------------------------------------
-void ReaderIso14443a(UsbCommand * c, UsbCommand * ack)
+void ReaderIso14443a(UsbCommand * c)
{
iso14a_command_t param = c->arg[0];
uint8_t * cmd = c->d.asBytes;
size_t len = c->arg[1];
-
- if(param & ISO14A_REQUEST_TRIGGER) iso14a_set_trigger(1);
+ uint32_t arg0 = 0;
+ byte_t buf[USB_CMD_DATA_SIZE];
+
+ iso14a_clear_trace();
+ iso14a_set_tracing(true);
+
+ if(param & ISO14A_REQUEST_TRIGGER) {
+ iso14a_set_trigger(1);
+ }
if(param & ISO14A_CONNECT) {
iso14443a_setup();
- ack->arg[0] = iso14443a_select_card(ack->d.asBytes, (iso14a_card_select_t *) (ack->d.asBytes+12), NULL);
- UsbSendPacket((void *)ack, sizeof(UsbCommand));
+ arg0 = iso14443a_select_card(NULL,(iso14a_card_select_t*)buf,NULL);
+ cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(iso14a_card_select_t));
+// UsbSendPacket((void *)ack, sizeof(UsbCommand));
}
if(param & ISO14A_SET_TIMEOUT) {
}
if(param & ISO14A_APDU) {
- ack->arg[0] = iso14_apdu(cmd, len, ack->d.asBytes);
- UsbSendPacket((void *)ack, sizeof(UsbCommand));
+ arg0 = iso14_apdu(cmd, len, buf);
+ cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf));
+// UsbSendPacket((void *)ack, sizeof(UsbCommand));
}
if(param & ISO14A_RAW) {
len += 2;
}
ReaderTransmit(cmd,len);
- ack->arg[0] = ReaderReceive(ack->d.asBytes);
- UsbSendPacket((void *)ack, sizeof(UsbCommand));
+ arg0 = ReaderReceive(buf);
+// UsbSendPacket((void *)ack, sizeof(UsbCommand));
+ cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf));
}
- if(param & ISO14A_REQUEST_TRIGGER) iso14a_set_trigger(0);
+ if(param & ISO14A_REQUEST_TRIGGER) {
+ iso14a_set_trigger(0);
+ }
- if(param & ISO14A_NO_DISCONNECT)
+ if(param & ISO14A_NO_DISCONNECT) {
return;
+ }
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
}
+
+#define TEST_LENGTH 100
+typedef struct mftest{
+ uint8_t nt[8];
+ uint8_t count;
+}mftest ;
+
+/**
+ *@brief Tunes the mifare attack settings. This method checks the nonce entropy when
+ *using a specified timeout.
+ *Different cards behave differently, some cards require up to a second to power down (and thus reset
+ *token generator), other cards are fine with 50 ms.
+ *
+ * @param time
+ * @return the entropy. A value of 100 (%) means that every nonce was unique, while a value close to
+ *zero indicates a low entropy: the given timeout is sufficient to power down the card.
+ */
+int TuneMifare(int time)
+{
+ // Mifare AUTH
+ uint8_t mf_auth[] = { 0x60,0x00,0xf5,0x7b };
+ uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET);
+
+ iso14443a_setup();
+ int TIME1=time;
+ int TIME2=2000;
+ uint8_t uid[8];
+ uint32_t cuid;
+ byte_t nt[4];
+ Dbprintf("Tuning... testing a delay of %d ms (press button to skip)",time);
+
+
+ mftest nt_values[TEST_LENGTH];
+ int nt_size = 0;
+ int i = 0;
+ for(i = 0 ; i< 100 ; i++)
+ {
+ LED_C_OFF();
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+ SpinDelay(TIME1);
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
+ LED_C_ON();
+ SpinDelayUs(TIME2);
+ if(!iso14443a_select_card(uid, NULL, &cuid)) continue;
+
+ // Transmit MIFARE_CLASSIC_AUTH
+ ReaderTransmit(mf_auth, sizeof(mf_auth));
+
+ // Receive the (16 bit) "random" nonce
+ if (!ReaderReceive(receivedAnswer)) continue;
+ memcpy(nt, receivedAnswer, 4);
+
+ //store it
+ int already_stored = 0;
+ for(int i = 0 ; i < nt_size && !already_stored; i++)
+ {
+ if( memcmp(nt, nt_values[i].nt, 4) == 0)
+ {
+ nt_values[i].count++;
+ already_stored = 1;
+ }
+ }
+ if(!already_stored)
+ {
+ mftest* ptr= &nt_values[nt_size++];
+ //Clear it before use
+ memset(ptr, 0, sizeof(mftest));
+ memcpy(ptr->nt, nt, 4);
+ ptr->count = 1;
+ }
+
+ if(BUTTON_PRESS())
+ {
+ Dbprintf("Tuning aborted prematurely");
+ break;
+ }
+ }
+ /*
+ for(int i = 0 ; i < nt_size;i++){
+ mftest x = nt_values[i];
+ Dbprintf("%d,%d,%d,%d : %d",x.nt[0],x.nt[1],x.nt[2],x.nt[3],x.count);
+ }
+ */
+ int result = nt_size *100 / i;
+ Dbprintf(" ... results for %d ms : %d %",time, result);
+ return result;
+}
+
//-----------------------------------------------------------------------------
// Read an ISO 14443a tag. Send out commands and store answers.
//
//-----------------------------------------------------------------------------
-void ReaderMifare(uint32_t parameter)
+#define STATE_SIZE 100
+typedef struct AttackState{
+ byte_t nt[4];
+ byte_t par_list[8];
+ byte_t ks_list[8];
+ byte_t par;
+ byte_t par_low;
+ byte_t nt_diff;
+ uint8_t mf_nr_ar[8];
+} AttackState;
+
+
+int continueAttack(AttackState* pState,uint8_t* receivedAnswer)
{
- // Mifare AUTH
- uint8_t mf_auth[] = { 0x60,0x00,0xf5,0x7b };
- uint8_t mf_nr_ar[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
-
- uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + 3560); // was 3560 - tied to other size changes
- traceLen = 0;
- tracing = false;
-
- iso14443a_setup();
-
- LED_A_ON();
- LED_B_OFF();
- LED_C_OFF();
-
- byte_t nt_diff = 0;
- LED_A_OFF();
- byte_t par = 0;
- byte_t par_mask = 0xff;
- byte_t par_low = 0;
- int led_on = TRUE;
- uint8_t uid[8];
- uint32_t cuid;
- tracing = FALSE;
- byte_t nt[4] = {0,0,0,0};
- byte_t nt_attacked[4], nt_noattack[4];
- byte_t par_list[8] = {0,0,0,0,0,0,0,0};
- byte_t ks_list[8] = {0,0,0,0,0,0,0,0};
- num_to_bytes(parameter, 4, nt_noattack);
- int isOK = 0, isNULL = 0;
+ // Transmit reader nonce and reader answer
+ ReaderTransmitPar(pState->mf_nr_ar, sizeof(pState->mf_nr_ar),pState->par);
- while(TRUE)
- {
- LED_C_ON();
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- SpinDelay(200);
- FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
- LED_C_OFF();
-
- // Test if the action was cancelled
- if(BUTTON_PRESS()) {
- break;
- }
-
- if(!iso14443a_select_card(uid, NULL, &cuid)) continue;
-
- // Transmit MIFARE_CLASSIC_AUTH
- ReaderTransmit(mf_auth, sizeof(mf_auth));
-
- // Receive the (16 bit) "random" nonce
- if (!ReaderReceive(receivedAnswer)) continue;
- memcpy(nt, receivedAnswer, 4);
-
- // Transmit reader nonce and reader answer
- ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar),par);
-
- // Receive 4 bit answer
- if (ReaderReceive(receivedAnswer))
- {
- if ( (parameter != 0) && (memcmp(nt, nt_noattack, 4) == 0) ) continue;
-
- isNULL = (nt_attacked[0] = 0) && (nt_attacked[1] = 0) && (nt_attacked[2] = 0) && (nt_attacked[3] = 0);
- if ( (isNULL != 0 ) && (memcmp(nt, nt_attacked, 4) != 0) ) continue;
-
- if (nt_diff == 0)
- {
- LED_A_ON();
- memcpy(nt_attacked, nt, 4);
- par_mask = 0xf8;
- par_low = par & 0x07;
- }
-
- led_on = !led_on;
- if(led_on) LED_B_ON(); else LED_B_OFF();
- par_list[nt_diff] = par;
- ks_list[nt_diff] = receivedAnswer[0] ^ 0x05;
-
- // Test if the information is complete
- if (nt_diff == 0x07) {
- isOK = 1;
- break;
- }
-
- nt_diff = (nt_diff + 1) & 0x07;
- mf_nr_ar[3] = nt_diff << 5;
- par = par_low;
- } else {
- if (nt_diff == 0)
- {
- par++;
- } else {
- par = (((par >> 3) + 1) << 3) | par_low;
- }
- }
- }
-
- LogTrace(nt, 4, 0, GetParity(nt, 4), TRUE);
- LogTrace(par_list, 8, 0, GetParity(par_list, 8), TRUE);
- LogTrace(ks_list, 8, 0, GetParity(ks_list, 8), TRUE);
-
- UsbCommand ack = {CMD_ACK, {isOK, 0, 0}};
- memcpy(ack.d.asBytes + 0, uid, 4);
- memcpy(ack.d.asBytes + 4, nt, 4);
- memcpy(ack.d.asBytes + 8, par_list, 8);
- memcpy(ack.d.asBytes + 16, ks_list, 8);
-
- LED_B_ON();
- UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
- LED_B_OFF();
+ // Receive 4 bit answer
+ int len = ReaderReceive(receivedAnswer);
+ if (!len)
+ {
+ if (pState->nt_diff == 0)
+ {
+ pState->par++;
+ } else {
+ pState->par = (((pState->par >> 3) + 1) << 3) | pState->par_low;
+ }
+ return 2;
+ }
+ if(pState->nt_diff == 0)
+ {
+ pState->par_low = pState->par & 0x07;
+ }
+ //Dbprintf("answer received, parameter (%d), (memcmp(nt, nt_no)=%d",parameter,memcmp(nt, nt_noattack, 4));
+ //if ( (parameter != 0) && (memcmp(nt, nt_noattack, 4) == 0) ) continue;
+ //isNULL = 0;//|| !(nt_attacked[0] == 0) && (nt_attacked[1] == 0) && (nt_attacked[2] == 0) && (nt_attacked[3] == 0);
+ //
+ // if ( /*(isNULL != 0 ) && */(memcmp(nt, nt_attacked, 4) != 0) ) continue;
+
+ //led_on = !led_on;
+ //if(led_on) LED_B_ON(); else LED_B_OFF();
+ pState->par_list[pState->nt_diff] = pState->par;
+ pState->ks_list[pState->nt_diff] = receivedAnswer[0] ^ 0x05;
+
+ // Test if the information is complete
+ if (pState->nt_diff == 0x07) {
+ return 0;
+ }
- // Thats it...
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- LEDsoff();
- tracing = TRUE;
-
- if (MF_DBGLEVEL >= 1) DbpString("COMMAND mifare FINISHED");
+ pState->nt_diff = (pState->nt_diff + 1) & 0x07;
+ pState->mf_nr_ar[3] = pState->nt_diff << 5;
+ pState->par = pState->par_low;
+ return 1;
}
-//-----------------------------------------------------------------------------
-// Select, Authenticaate, Read an MIFARE tag.
-// read block
-//-----------------------------------------------------------------------------
-void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
+void reportResults(uint8_t uid[8],AttackState *pState, int isOK)
{
- // params
- uint8_t blockNo = arg0;
- uint8_t keyType = arg1;
- uint64_t ui64Key = 0;
- ui64Key = bytes_to_num(datain, 6);
-
- // variables
- byte_t isOK = 0;
- byte_t dataoutbuf[16];
- uint8_t uid[8];
- uint32_t cuid;
- struct Crypto1State mpcs = {0, 0};
- struct Crypto1State *pcs;
- pcs = &mpcs;
-
- // clear trace
- traceLen = 0;
-// tracing = false;
-
- iso14443a_setup();
-
- LED_A_ON();
- LED_B_OFF();
- LED_C_OFF();
-
- while (true) {
- if(!iso14443a_select_card(uid, NULL, &cuid)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");
- break;
- };
-
- if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Auth error");
- break;
- };
-
- if(mifare_classic_readblock(pcs, cuid, blockNo, dataoutbuf)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Read block error");
- break;
- };
-
- if(mifare_classic_halt(pcs, cuid)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");
- break;
- };
-
- isOK = 1;
- break;
- }
-
- // ----------------------------- crypto1 destroy
- crypto1_destroy(pcs);
-
- if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED");
-
- // add trace trailer
- memset(uid, 0x44, 4);
- LogTrace(uid, 4, 0, 0, TRUE);
-
- UsbCommand ack = {CMD_ACK, {isOK, 0, 0}};
- memcpy(ack.d.asBytes, dataoutbuf, 16);
-
- LED_B_ON();
- UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
- LED_B_OFF();
+ LogTrace(pState->nt, 4, 0, GetParity(pState->nt, 4), TRUE);
+ LogTrace(pState->par_list, 8, 0, GetParity(pState->par_list, 8), TRUE);
+ LogTrace(pState->ks_list, 8, 0, GetParity(pState->ks_list, 8), TRUE);
+
+ byte_t buf[48];
+ memcpy(buf + 0, uid, 4);
+ if(pState != NULL)
+ {
+ memcpy(buf + 4, pState->nt, 4);
+ memcpy(buf + 8, pState->par_list, 8);
+ memcpy(buf + 16, pState->ks_list, 8);
+ }
+ LED_B_ON();
+ cmd_send(CMD_ACK,isOK,0,0,buf,48);
+ LED_B_OFF();
- // Thats it...
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- LEDsoff();
-// tracing = TRUE;
+ // Thats it...
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+ LEDsoff();
+ tracing = TRUE;
+ if (MF_DBGLEVEL >= 1) DbpString("COMMAND mifare FINISHED");
}
-//-----------------------------------------------------------------------------
-// Select, Authenticaate, Read an MIFARE tag.
-// read sector (data = 4 x 16 bytes = 64 bytes)
-//-----------------------------------------------------------------------------
-void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
+void ReaderMifareBegin(uint32_t offset_time, uint32_t powerdown_time);
+
+/**
+ * @brief New implementation of ReaderMifare, the classic mifare attack.
+ * This implementation is backwards-compatible, but has some added parameters.
+ * @param c the usbcommand in complete
+ * c->arg[0] - nt_noattack (deprecated)
+ * c->arg[1] - offset_time us (0 => random)
+ * c->arg[2] - powerdown_time ms (0=> tuning)
+ *
+ */
+void ReaderMifare(UsbCommand *c)
{
- // params
- uint8_t sectorNo = arg0;
- uint8_t keyType = arg1;
- uint64_t ui64Key = 0;
- ui64Key = bytes_to_num(datain, 6);
-
- // variables
- byte_t isOK = 0;
- byte_t dataoutbuf[16 * 4];
- uint8_t uid[8];
- uint32_t cuid;
- struct Crypto1State mpcs = {0, 0};
- struct Crypto1State *pcs;
- pcs = &mpcs;
-
- // clear trace
- traceLen = 0;
-// tracing = false;
-
- iso14443a_setup();
-
- LED_A_ON();
- LED_B_OFF();
- LED_C_OFF();
-
- while (true) {
- if(!iso14443a_select_card(uid, NULL, &cuid)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");
- break;
- };
-
- if(mifare_classic_auth(pcs, cuid, sectorNo * 4, keyType, ui64Key, AUTH_FIRST)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Auth error");
- break;
- };
-
- if(mifare_classic_readblock(pcs, cuid, sectorNo * 4 + 0, dataoutbuf + 16 * 0)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Read block 0 error");
- break;
- };
- if(mifare_classic_readblock(pcs, cuid, sectorNo * 4 + 1, dataoutbuf + 16 * 1)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Read block 1 error");
- break;
- };
- if(mifare_classic_readblock(pcs, cuid, sectorNo * 4 + 2, dataoutbuf + 16 * 2)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Read block 2 error");
- break;
- };
- if(mifare_classic_readblock(pcs, cuid, sectorNo * 4 + 3, dataoutbuf + 16 * 3)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Read block 3 error");
- break;
- };
-
- if(mifare_classic_halt(pcs, cuid)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");
- break;
- };
-
- isOK = 1;
- break;
- }
-
- // ----------------------------- crypto1 destroy
- crypto1_destroy(pcs);
-
- if (MF_DBGLEVEL >= 2) DbpString("READ SECTOR FINISHED");
-
- // add trace trailer
- memset(uid, 0x44, 4);
- LogTrace(uid, 4, 0, 0, TRUE);
-
- UsbCommand ack = {CMD_ACK, {isOK, 0, 0}};
- memcpy(ack.d.asBytes, dataoutbuf, 16 * 2);
-
- LED_B_ON();
- UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
-
- SpinDelay(100);
-
- memcpy(ack.d.asBytes, dataoutbuf + 16 * 2, 16 * 2);
- UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
- LED_B_OFF();
-
- // Thats it...
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- LEDsoff();
-// tracing = TRUE;
-
+ /*
+ * The 'no-attack' is not used anymore, with the introduction of
+ * state tables. Instead, we use an offset which is random. This means that we
+ * should not get stuck on a 'bad' nonce, so no-attack is not needed.
+ * Anyway, arg[0] is reserved for backwards compatibility
+ uint32_t nt_noattack_uint = c->arg[0];
+ byte_t nt_noattack[4];
+ num_to_bytes(parameter, 4, nt_noattack_uint);
+
+ */
+ /*
+ *IF, for some reason, you want to attack a specific nonce or whatever,
+ *you can specify the offset time yourself, in which case it won't be random.
+ *
+ * The offset time is microseconds, MICROSECONDS, not ms.
+ */
+ uint32_t offset_time = c->arg[1];
+ if(offset_time == 0)
+ {
+ //[Martin:]I would like to have used rand(), but linking problems prevented it
+ //offset_time = rand() % 4000;
+ //So instead, I found this nifty thingy, which seems to fit the bill
+ offset_time = GetTickCount() % 2000;
+ }
+ /*
+ * There is an implementation of tuning. Tuning will try to determine
+ * a good power-down time, which is different for different cards.
+ * If a value is specified from the packet, we won't do any tuning.
+ * A value of zero will initialize a tuning.
+ * The power-down time is milliseconds, that MILLI-seconds .
+ */
+ uint32_t powerdown_time = c->arg[2];
+ if(powerdown_time == 0)
+ {
+ //Tuning required
+ int entropy = 100;
+ int time = 25;
+ entropy = TuneMifare(time);
+
+ while(entropy > 50 && time < 2000){
+ //Increase timeout, but never more than 500ms at a time
+ time = MIN(time*2, time+500);
+ entropy = TuneMifare(time);
+ }
+ if(entropy > 50){
+ Dbprintf("OBS! This card has high entropy (%d) and slow power-down. This may take a while", entropy);
+ }
+ powerdown_time = time;
+ }
+ //The actual attack
+ ReaderMifareBegin(offset_time, powerdown_time);
}
-
-//-----------------------------------------------------------------------------
-// Select, Authenticaate, Read an MIFARE tag.
-// read block
-//-----------------------------------------------------------------------------
-void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
+void ReaderMifareBegin(uint32_t offset_time, uint32_t powerdown_time)
{
- // params
- uint8_t blockNo = arg0;
- uint8_t keyType = arg1;
- uint64_t ui64Key = 0;
- byte_t blockdata[16];
-
- ui64Key = bytes_to_num(datain, 6);
- memcpy(blockdata, datain + 10, 16);
-
- // variables
- byte_t isOK = 0;
- uint8_t uid[8];
- uint32_t cuid;
- struct Crypto1State mpcs = {0, 0};
- struct Crypto1State *pcs;
- pcs = &mpcs;
-
- // clear trace
- traceLen = 0;
-// tracing = false;
-
- iso14443a_setup();
-
- LED_A_ON();
- LED_B_OFF();
- LED_C_OFF();
-
- while (true) {
- if(!iso14443a_select_card(uid, NULL, &cuid)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");
- break;
- };
-
- if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Auth error");
- break;
- };
-
- if(mifare_classic_writeblock(pcs, cuid, blockNo, blockdata)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Write block error");
- break;
- };
+ Dbprintf("Using power-down-time of %d ms, offset time %d us", powerdown_time, offset_time);
- if(mifare_classic_halt(pcs, cuid)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Halt error");
- break;
- };
-
- isOK = 1;
- break;
- }
-
- // ----------------------------- crypto1 destroy
- crypto1_destroy(pcs);
-
- if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED");
+ /**
+ *Allocate our state-table and initialize with zeroes
+ **/
- // add trace trailer
- memset(uid, 0x44, 4);
- LogTrace(uid, 4, 0, 0, TRUE);
-
- UsbCommand ack = {CMD_ACK, {isOK, 0, 0}};
-
- LED_B_ON();
- UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
- LED_B_OFF();
-
-
- // Thats it...
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- LEDsoff();
-// tracing = TRUE;
-
-}
-
-// Return 1 if the nonce is invalid else return 0
-int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, byte_t * parity) {
- return ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \
- (oddparity((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \
- (oddparity((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0;
-}
+ AttackState states[STATE_SIZE] ;
+ //Dbprintf("Memory allocated ok! (%d bytes)",STATE_SIZE*sizeof(AttackState) );
+ memset(states, 0, STATE_SIZE*sizeof(AttackState));
+ // Mifare AUTH
+ uint8_t mf_auth[] = { 0x60,0x00,0xf5,0x7b };
+ uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); // was 3560 - tied to other size changes
-//-----------------------------------------------------------------------------
-// MIFARE nested authentication.
-//
-//-----------------------------------------------------------------------------
-void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain)
-{
- // params
- uint8_t blockNo = arg0;
- uint8_t keyType = arg1;
- uint8_t targetBlockNo = arg2 & 0xff;
- uint8_t targetKeyType = (arg2 >> 8) & 0xff;
- uint64_t ui64Key = 0;
-
- ui64Key = bytes_to_num(datain, 6);
-
- // variables
- int rtr, i, j, m, len;
- int davg, dmin, dmax;
- uint8_t uid[8];
- uint32_t cuid, nt1, nt2, nttmp, nttest, par, ks1;
- uint8_t par_array[4];
- nestedVector nvector[NES_MAX_INFO + 1][10];
- int nvectorcount[NES_MAX_INFO + 1];
- int ncount = 0;
- UsbCommand ack = {CMD_ACK, {0, 0, 0}};
- struct Crypto1State mpcs = {0, 0};
- struct Crypto1State *pcs;
- pcs = &mpcs;
- uint8_t* receivedAnswer = mifare_get_bigbufptr();
+ traceLen = 0;
+ tracing = false;
- //init
- for (i = 0; i < NES_MAX_INFO + 1; i++) nvectorcount[i] = 11; // 11 - empty block;
-
- // clear trace
- traceLen = 0;
- tracing = false;
-
iso14443a_setup();
-
LED_A_ON();
- LED_B_ON();
- LED_C_OFF();
-
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- SpinDelay(200);
-
- davg = dmax = 0;
- dmin = 2000;
-
- // test nonce distance
- for (rtr = 0; rtr < 10; rtr++) {
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- SpinDelay(100);
- FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
-
- // Test if the action was cancelled
- if(BUTTON_PRESS()) {
- break;
- }
-
- if(!iso14443a_select_card(uid, NULL, &cuid)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");
- break;
- };
-
- if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Auth1 error");
- break;
- };
-
- if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Auth2 error");
- break;
- };
-
- nttmp = prng_successor(nt1, 500);
- for (i = 501; i < 2000; i++) {
- nttmp = prng_successor(nttmp, 1);
- if (nttmp == nt2) break;
- }
-
- if (i != 2000) {
- davg += i;
- if (dmin > i) dmin = i;
- if (dmax < i) dmax = i;
- if (MF_DBGLEVEL >= 4) Dbprintf("r=%d nt1=%08x nt2=%08x distance=%d", rtr, nt1, nt2, i);
- }
- }
-
- if (rtr == 0) return;
-
- davg = davg / rtr;
- if (MF_DBGLEVEL >= 3) Dbprintf("distance: min=%d max=%d avg=%d", dmin, dmax, davg);
-
LED_B_OFF();
-
-// -------------------------------------------------------------------------------------------------
-
- LED_C_ON();
-
- // get crypted nonces for target sector
- for (rtr = 0; rtr < NS_RETRIES_GETNONCE; rtr++) {
- if (MF_DBGLEVEL >= 4) Dbprintf("------------------------------");
-
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- SpinDelay(100);
- FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
-
- // Test if the action was cancelled
- if(BUTTON_PRESS()) {
- break;
- }
-
- if(!iso14443a_select_card(uid, NULL, &cuid)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card");
- break;
- };
-
- if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1)) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Auth1 error");
- break;
- };
-
- // nested authentication
- len = mifare_sendcmd_shortex(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, &par);
- if (len != 4) {
- if (MF_DBGLEVEL >= 1) Dbprintf("Auth2 error len=%d", len);
- break;
- };
-
- nt2 = bytes_to_num(receivedAnswer, 4);
- if (MF_DBGLEVEL >= 4) Dbprintf("r=%d nt1=%08x nt2enc=%08x nt2par=%08x", rtr, nt1, nt2, par);
-
- // Parity validity check
- for (i = 0; i < 4; i++) {
- par_array[i] = (oddparity(receivedAnswer[i]) != ((par & 0x08) >> 3));
- par = par << 1;
- }
-
- ncount = 0;
- for (m = dmin - NS_TOLERANCE; m < dmax + NS_TOLERANCE; m++) {
- nttest = prng_successor(nt1, m);
- ks1 = nt2 ^ nttest;
-
- if (valid_nonce(nttest, nt2, ks1, par_array) && (ncount < 11)){
-
- nvector[NES_MAX_INFO][ncount].nt = nttest;
- nvector[NES_MAX_INFO][ncount].ks1 = ks1;
- ncount++;
- nvectorcount[NES_MAX_INFO] = ncount;
- if (MF_DBGLEVEL >= 4) Dbprintf("valid m=%d ks1=%08x nttest=%08x", m, ks1, nttest);
- }
-
- }
-
- // select vector with length less than got
- if (nvectorcount[NES_MAX_INFO] != 0) {
- m = NES_MAX_INFO;
-
- for (i = 0; i < NES_MAX_INFO; i++)
- if (nvectorcount[i] > 10) {
- m = i;
- break;
- }
-
- if (m == NES_MAX_INFO)
- for (i = 0; i < NES_MAX_INFO; i++)
- if (nvectorcount[NES_MAX_INFO] < nvectorcount[i]) {
- m = i;
- break;
- }
-
- if (m != NES_MAX_INFO) {
- for (i = 0; i < nvectorcount[m]; i++) {
- nvector[m][i] = nvector[NES_MAX_INFO][i];
- }
- nvectorcount[m] = nvectorcount[NES_MAX_INFO];
- }
- }
- }
-
LED_C_OFF();
-
- // ----------------------------- crypto1 destroy
- crypto1_destroy(pcs);
-
- // add trace trailer
- memset(uid, 0x44, 4);
- LogTrace(uid, 4, 0, 0, TRUE);
- for (i = 0; i < NES_MAX_INFO; i++) {
- if (nvectorcount[i] > 10) continue;
-
- for (j = 0; j < nvectorcount[i]; j += 5) {
- ncount = nvectorcount[i] - j;
- if (ncount > 5) ncount = 5;
-
- ack.arg[0] = 0; // isEOF = 0
- ack.arg[1] = ncount;
- ack.arg[2] = targetBlockNo + (targetKeyType * 0x100);
- memset(ack.d.asBytes, 0x00, sizeof(ack.d.asBytes));
-
- memcpy(ack.d.asBytes, &cuid, 4);
- for (m = 0; m < ncount; m++) {
- memcpy(ack.d.asBytes + 8 + m * 8 + 0, &nvector[i][m + j].nt, 4);
- memcpy(ack.d.asBytes + 8 + m * 8 + 4, &nvector[i][m + j].ks1, 4);
- }
-
- LED_B_ON();
- SpinDelay(100);
- UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
- LED_B_OFF();
- }
- }
-
- // finalize list
- ack.arg[0] = 1; // isEOF = 1
- ack.arg[1] = 0;
- ack.arg[2] = 0;
- memset(ack.d.asBytes, 0x00, sizeof(ack.d.asBytes));
-
- LED_B_ON();
- SpinDelay(300);
- UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
- LED_B_OFF();
-
- if (MF_DBGLEVEL >= 4) DbpString("NESTED FINISHED");
-
- // Thats it...
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- LEDsoff();
-
- tracing = TRUE;
-}
-
-//-----------------------------------------------------------------------------
-// MIFARE check keys. key count up to 8.
-//
-//-----------------------------------------------------------------------------
-void MifareChkKeys(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
-{
- // params
- uint8_t blockNo = arg0;
- uint8_t keyType = arg1;
- uint8_t keyCount = arg2;
- uint64_t ui64Key = 0;
-
- // variables
- int i;
- byte_t isOK = 0;
+ LED_A_OFF();
uint8_t uid[8];
uint32_t cuid;
- struct Crypto1State mpcs = {0, 0};
- struct Crypto1State *pcs;
- pcs = &mpcs;
-
- // clear debug level
- int OLD_MF_DBGLEVEL = MF_DBGLEVEL;
- MF_DBGLEVEL = MF_DBG_NONE;
-
- // clear trace
- traceLen = 0;
- tracing = TRUE;
-
- iso14443a_setup();
- LED_A_ON();
- LED_B_OFF();
- LED_C_OFF();
-
- SpinDelay(300);
- for (i = 0; i < keyCount; i++) {
+ byte_t nt[4];
+ int nts_attacked= 0;
+ //Keeps track of progress (max value of nt_diff for our states)
+ int progress = 0;
+ int high_entropy_warning_issued = 0;
+ while(!BUTTON_PRESS())
+ {
+ LED_C_OFF();
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- SpinDelay(100);
- FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
+ SpinDelay(powerdown_time);
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD);
+ LED_C_ON();
+ SpinDelayUs(offset_time);
- if(!iso14443a_select_card(uid, NULL, &cuid)) {
- if (OLD_MF_DBGLEVEL >= 1) Dbprintf("Can't select card");
- break;
- };
+ if(!iso14443a_select_card(uid, NULL, &cuid)) continue;
- ui64Key = bytes_to_num(datain + i * 6, 6);
- if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {
- continue;
- };
-
- isOK = 1;
- break;
- }
-
- // ----------------------------- crypto1 destroy
- crypto1_destroy(pcs);
-
- // add trace trailer
- memset(uid, 0x44, 4);
- LogTrace(uid, 4, 0, 0, TRUE);
+ // Transmit MIFARE_CLASSIC_AUTH
+ ReaderTransmit(mf_auth, sizeof(mf_auth));
- UsbCommand ack = {CMD_ACK, {isOK, 0, 0}};
- if (isOK) memcpy(ack.d.asBytes, datain + i * 6, 6);
-
- LED_B_ON();
- UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
- LED_B_OFF();
+ // Receive the (16 bit) "random" nonce
+ if (!ReaderReceive(receivedAnswer)) continue;
+ memcpy(nt, receivedAnswer, 4);
+
+ //Now we have the NT. Check if this NT is already under attack
+ AttackState* pState = NULL;
+ int i = 0;
+ for(i = 0 ; i < nts_attacked && pState == NULL; i++)
+ {
+ if( memcmp(nt, states[i].nt, 4) == 0)
+ {
+ //we have it
+ pState = &states[i];
+ //Dbprintf("Existing state found (%d)", i);
+ }
+ }
- // Thats it...
- FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- LEDsoff();
+ if(pState == NULL){
+ if(nts_attacked < STATE_SIZE )
+ {
+ //Initialize a new state
+ pState = &states[nts_attacked++];
+ //Clear it before use
+ memset(pState, 0, sizeof(AttackState));
+ memcpy(pState->nt, nt, 4);
+ i = nts_attacked;
+ //Dbprintf("New state created, nt=");
+ }else if(!high_entropy_warning_issued){
+ /**
+ *If we wound up here, it means that the state table was eaten up by potential nonces. This could be fixed by
+ *increasing the size of the state buffer, however, it points to some other problem. Ideally, we should get the same nonce
+ *every time. Realistically we should get a few different nonces, but if we get more than 50, there is probably somehting
+ *else that is wrong. An attack using too high nonce entropy will take **LONG** time to finish.
+ */
+ DbpString("WARNING: Nonce entropy is suspiciously high, something is wrong. Check timeouts (and perhaps increase STATE_SIZE)");
+ high_entropy_warning_issued = 1;
+ }
+ }
+ if(pState == NULL) continue;
- // restore debug level
- MF_DBGLEVEL = OLD_MF_DBGLEVEL;
-}
+ int result = continueAttack(pState, receivedAnswer);
+ if(result == 1){
+ //One state progressed another step
+ if(pState->nt_diff > progress)
+ {
+ progress = pState->nt_diff;
+ //Alert the user
+ Dbprintf("Recovery progress: %d/8, NTs attacked: %d ", progress,nts_attacked );
+ }
+ //Dbprintf("State increased to %d in state %d", pState->nt_diff, i);
+ }
+ else if(result == 2){
+ //Dbprintf("Continue attack no answer, par is now %d", pState->par);
+ }
+ else if(result == 0){
+ reportResults(uid,pState,1);
+ return;
+ }
+ }
+ reportResults(uid,NULL,0);
+}
//-----------------------------------------------------------------------------
// MIFARE 1K simulate.
//
void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
{
int cardSTATE = MFEMUL_NOFIELD;
+ int _7BUID = 0;
int vHf = 0; // in mV
- int nextCycleTimeout = 0;
+ //int nextCycleTimeout = 0;
int res;
- uint32_t timer = 0;
+// uint32_t timer = 0;
uint32_t selTimer = 0;
uint32_t authTimer = 0;
uint32_t par = 0;
uint8_t cardWRBL = 0;
uint8_t cardAUTHSC = 0;
uint8_t cardAUTHKEY = 0xff; // no authentication
+ //uint32_t cardRn = 0;
+ uint32_t cardRr = 0;
uint32_t cuid = 0;
+ //uint32_t rn_enc = 0;
+ uint32_t ans = 0;
+ uint32_t cardINTREG = 0;
+ uint8_t cardINTBLOCK = 0;
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
- uint64_t key64 = 0xffffffffffffULL;
-
uint8_t* receivedCmd = eml_get_bigbufptr_recbuf();
uint8_t *response = eml_get_bigbufptr_sendbuf();
- static uint8_t rATQA[] = {0x04, 0x00}; // Mifare classic 1k
+ static uint8_t rATQA[] = {0x04, 0x00}; // Mifare classic 1k 4BUID
static uint8_t rUIDBCC1[] = {0xde, 0xad, 0xbe, 0xaf, 0x62};
static uint8_t rUIDBCC2[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; // !!!
static uint8_t rSAK[] = {0x08, 0xb6, 0xdd};
+ static uint8_t rSAK1[] = {0x04, 0xda, 0x17};
- static uint8_t rAUTH_NT[] = {0x1a, 0xac, 0xff, 0x4f};
+ static uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04};
+// static uint8_t rAUTH_NT[] = {0x1a, 0xac, 0xff, 0x4f};
static uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00};
-
+
// clear trace
traceLen = 0;
tracing = true;
+
+ // Authenticate response - nonce
+ uint32_t nonce = bytes_to_num(rAUTH_NT, 4);
- // emulator memory
- emlClearMem();
- emlGetMemBt(rUIDBCC1, 0, 4);
- rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
-
+ // get UID from emul memory
+ emlGetMemBt(receivedCmd, 7, 1);
+ _7BUID = !(receivedCmd[0] == 0x00);
+ if (!_7BUID) { // ---------- 4BUID
+ rATQA[0] = 0x04;
+
+ emlGetMemBt(rUIDBCC1, 0, 4);
+ rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
+ } else { // ---------- 7BUID
+ rATQA[0] = 0x44;
+
+ rUIDBCC1[0] = 0x88;
+ emlGetMemBt(&rUIDBCC1[1], 0, 3);
+ rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
+ emlGetMemBt(rUIDBCC2, 3, 4);
+ rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3];
+ }
+
// -------------------------------------- test area
- // Authenticate response - nonce
- uint8_t *resp1 = (((uint8_t *)BigBuf) + EML_RESPONSES);
- int resp1Len;
-// uint8_t *resp2 = (((uint8_t *)BigBuf) + EML_RESPONSES + 200);
-// int resp2Len;
- CodeIso14443aAsTag(rAUTH_NT, sizeof(rAUTH_NT));
- memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax;
-
- timer = GetTickCount();
- uint32_t nonce = bytes_to_num(rAUTH_NT, 4);
- uint32_t rn_enc = 0x98d76b77; // !!!!!!!!!!!!!!!!!
- uint32_t ans = 0;
- cuid = bytes_to_num(rUIDBCC1, 4);
-/*
- crypto1_create(pcs, key64);
- crypto1_word(pcs, cuid ^ nonce, 0);
- crypto1_word(pcs, rn_enc , 1);
- crypto1_word(pcs, 0, 0);
- ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0);
- num_to_bytes(ans, 4, rAUTH_AT);
- CodeIso14443aAsTag(rAUTH_AT, sizeof(rAUTH_AT));
- memcpy(resp2, ToSend, ToSendMax); resp2Len = ToSendMax;
- Dbprintf("crypto auth time: %d", GetTickCount() - timer);
-*/
// -------------------------------------- END test area
// start mkseconds counter
StartCountUS();
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN);
SpinDelay(200);
- Dbprintf("--> start");
+ if (MF_DBGLEVEL >= 1) Dbprintf("Started. 7buid=%d", _7BUID);
// calibrate mkseconds counter
GetDeltaCountUS();
while (true) {
if (cardSTATE == MFEMUL_NOFIELD) {
vHf = (33000 * AvgAdc(ADC_CHAN_HF)) >> 10;
if (vHf > MF_MINFIELDV) {
- cardSTATE = MFEMUL_IDLE;
+ cardSTATE_TO_IDLE();
LED_A_ON();
}
}
if (cardSTATE != MFEMUL_NOFIELD) {
- res = EmGetCmd(receivedCmd, &len, 100); // (+ nextCycleTimeout)
+ res = EmGetCmd(receivedCmd, &len, RECV_CMD_SIZE); // (+ nextCycleTimeout)
if (res == 2) {
cardSTATE = MFEMUL_NOFIELD;
LEDsoff();
if(res) break;
}
- nextCycleTimeout = 0;
+ //nextCycleTimeout = 0;
// if (len) Dbprintf("len:%d cmd: %02x %02x %02x %02x", len, receivedCmd[0], receivedCmd[1], receivedCmd[2], receivedCmd[3]);
// select all
if (len == 2 && (receivedCmd[0] == 0x93 && receivedCmd[1] == 0x20)) {
EmSendCmd(rUIDBCC1, sizeof(rUIDBCC1));
-
- if (rUIDBCC1[0] == 0x88) {
- cardSTATE = MFEMUL_SELECT2;
- }
+ break;
}
// select card
if (len == 9 &&
(receivedCmd[0] == 0x93 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC1, 4) == 0)) {
- EmSendCmd(rSAK, sizeof(rSAK));
+ if (!_7BUID)
+ EmSendCmd(rSAK, sizeof(rSAK));
+ else
+ EmSendCmd(rSAK1, sizeof(rSAK1));
cuid = bytes_to_num(rUIDBCC1, 4);
- cardSTATE = MFEMUL_WORK;
- LED_B_ON();
- Dbprintf("--> WORK. anticol1 time: %d", GetTickCount() - selTimer);
+ if (!_7BUID) {
+ cardSTATE = MFEMUL_WORK;
+ LED_B_ON();
+ if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol1 time: %d", GetTickCount() - selTimer);
+ break;
+ } else {
+ cardSTATE = MFEMUL_SELECT2;
+ break;
+ }
}
break;
}
case MFEMUL_SELECT2:{
+ if (!len) break;
+
+ if (len == 2 && (receivedCmd[0] == 0x95 && receivedCmd[1] == 0x20)) {
EmSendCmd(rUIDBCC2, sizeof(rUIDBCC2));
+ break;
+ }
- cuid = bytes_to_num(rUIDBCC2, 4);
+ // select 2 card
+ if (len == 9 &&
+ (receivedCmd[0] == 0x95 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC2, 4) == 0)) {
+ EmSendCmd(rSAK, sizeof(rSAK));
+
+ cuid = bytes_to_num(rUIDBCC2, 4);
+ cardSTATE = MFEMUL_WORK;
+ LED_B_ON();
+ if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol2 time: %d", GetTickCount() - selTimer);
+ break;
+ }
+
+ // i guess there is a command). go into the work state.
+ if (len != 4) break;
cardSTATE = MFEMUL_WORK;
- LED_B_ON();
-Dbprintf("--> WORK. anticol2 time: %d", GetTickCount() - selTimer);
- break;
+ goto lbWORK;
}
case MFEMUL_AUTH1:{
if (len == 8) {
-// ---------------------------------
- rn_enc = bytes_to_num(receivedCmd, 4);
- crypto1_create(pcs, key64);
- crypto1_word(pcs, cuid ^ nonce, 0);
- crypto1_word(pcs, rn_enc , 1);
- crypto1_word(pcs, 0, 0);
- ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0);
- num_to_bytes(ans, 4, rAUTH_AT);
-// ---------------------------------
- EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT));
+ // --- crypto
+ //rn_enc = bytes_to_num(receivedCmd, 4);
+ //cardRn = rn_enc ^ crypto1_word(pcs, rn_enc , 1);
+ cardRr = bytes_to_num(&receivedCmd[4], 4) ^ crypto1_word(pcs, 0, 0);
+ // test if auth OK
+ if (cardRr != prng_successor(nonce, 64)){
+ if (MF_DBGLEVEL >= 4) Dbprintf("AUTH FAILED. cardRr=%08x, succ=%08x", cardRr, prng_successor(nonce, 64));
+ cardSTATE_TO_IDLE();
+ break;
+ }
+ ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0);
+ num_to_bytes(ans, 4, rAUTH_AT);
+ // --- crypto
+ EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT));
cardSTATE = MFEMUL_AUTH2;
} else {
- cardSTATE = MFEMUL_IDLE;
- LED_B_OFF();
- LED_C_OFF();
+ cardSTATE_TO_IDLE();
}
if (cardSTATE != MFEMUL_AUTH2) break;
}
case MFEMUL_AUTH2:{
- // test auth info here...
-
LED_C_ON();
cardSTATE = MFEMUL_WORK;
-Dbprintf("AUTH COMPLETED. sec=%d, key=%d time=%d", cardAUTHSC, cardAUTHKEY, GetTickCount() - authTimer);
+ if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED. sec=%d, key=%d time=%d", cardAUTHSC, cardAUTHKEY, GetTickCount() - authTimer);
break;
}
case MFEMUL_WORK:{
- // auth
- if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) {
-authTimer = GetTickCount();
-// EmSendCmd(rAUTH_NT, sizeof(rAUTH_NT));
- EmSendCmd14443aRaw(resp1, resp1Len, 0);
-LogTrace(NULL, 0, GetDeltaCountUS(), 0, TRUE);
-// crypto1_create(pcs, key64);
-// if (cardAUTHKEY == 0xff) { // first auth
-// crypto1_word(pcs, cuid ^ bytes_to_num(rAUTH_NT, 4), 0); // uid ^ nonce
-// } else { // nested auth
-// }
-
- cardAUTHSC = receivedCmd[1] / 4; // received block num
- cardAUTHKEY = receivedCmd[0] - 0x60;
- cardSTATE = MFEMUL_AUTH1;
- nextCycleTimeout = 10;
- break;
- }
-
- if (len == 0) break;
+lbWORK: if (len == 0) break;
- // decrypt seqence
- if (cardAUTHKEY != 0xff) mf_crypto1_decrypt(pcs, receivedCmd, len);
+ if (cardAUTHKEY == 0xff) {
+ // first authentication
+ if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) {
+ authTimer = GetTickCount();
+
+ cardAUTHSC = receivedCmd[1] / 4; // received block num
+ cardAUTHKEY = receivedCmd[0] - 0x60;
+
+ // --- crypto
+ crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY));
+ ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0);
+ num_to_bytes(nonce, 4, rAUTH_AT);
+ EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT));
+ // --- crypto
+
+// last working revision
+// EmSendCmd14443aRaw(resp1, resp1Len, 0);
+// LogTrace(NULL, 0, GetDeltaCountUS(), 0, true);
+
+ cardSTATE = MFEMUL_AUTH1;
+ //nextCycleTimeout = 10;
+ break;
+ }
+ } else {
+ // decrypt seqence
+ mf_crypto1_decrypt(pcs, receivedCmd, len);
+
+ // nested authentication
+ if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) {
+ authTimer = GetTickCount();
+
+ cardAUTHSC = receivedCmd[1] / 4; // received block num
+ cardAUTHKEY = receivedCmd[0] - 0x60;
+
+ // --- crypto
+ crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY));
+ ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0);
+ num_to_bytes(ans, 4, rAUTH_AT);
+ EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT));
+ // --- crypto
+
+ cardSTATE = MFEMUL_AUTH1;
+ //nextCycleTimeout = 10;
+ break;
+ }
+ }
// rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued
// BUT... ACK --> NACK
// read block
if (len == 4 && receivedCmd[0] == 0x30) {
- if (receivedCmd[1] >= 16 * 4) {
+ if (receivedCmd[1] >= 16 * 4 || receivedCmd[1] / 4 != cardAUTHSC) {
EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
break;
}
// write block
if (len == 4 && receivedCmd[0] == 0xA0) {
- if (receivedCmd[1] >= 16 * 4) {
+ if (receivedCmd[1] >= 16 * 4 || receivedCmd[1] / 4 != cardAUTHSC) {
EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
break;
}
EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK));
- nextCycleTimeout = 50;
+ //nextCycleTimeout = 50;
cardSTATE = MFEMUL_WRITEBL2;
cardWRBL = receivedCmd[1];
break;
}
+ // works with cardINTREG
+
+ // increment, decrement, restore
+ if (len == 4 && (receivedCmd[0] == 0xC0 || receivedCmd[0] == 0xC1 || receivedCmd[0] == 0xC2)) {
+ if (receivedCmd[1] >= 16 * 4 ||
+ receivedCmd[1] / 4 != cardAUTHSC ||
+ emlCheckValBl(receivedCmd[1])) {
+ EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
+ break;
+ }
+ EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK));
+ if (receivedCmd[0] == 0xC1)
+ cardSTATE = MFEMUL_INTREG_INC;
+ if (receivedCmd[0] == 0xC0)
+ cardSTATE = MFEMUL_INTREG_DEC;
+ if (receivedCmd[0] == 0xC2)
+ cardSTATE = MFEMUL_INTREG_REST;
+ cardWRBL = receivedCmd[1];
+
+ break;
+ }
+
+
+ // transfer
+ if (len == 4 && receivedCmd[0] == 0xB0) {
+ if (receivedCmd[1] >= 16 * 4 || receivedCmd[1] / 4 != cardAUTHSC) {
+ EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
+ break;
+ }
+
+ if (emlSetValBl(cardINTREG, cardINTBLOCK, receivedCmd[1]))
+ EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
+ else
+ EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK));
+
+ break;
+ }
+
// halt
if (len == 4 && (receivedCmd[0] == 0x50 && receivedCmd[1] == 0x00)) {
- cardSTATE = MFEMUL_HALTED;
LED_B_OFF();
LED_C_OFF();
- Dbprintf("--> HALTED. Selected time: %d ms", GetTickCount() - selTimer);
+ cardSTATE = MFEMUL_HALTED;
+ if (MF_DBGLEVEL >= 4) Dbprintf("--> HALTED. Selected time: %d ms", GetTickCount() - selTimer);
break;
}
- break;
-
+
// command not allowed
if (len == 4) {
EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
break;
}
+
+ // case break
+ break;
}
case MFEMUL_WRITEBL2:{
if (len == 18){
EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK));
cardSTATE = MFEMUL_WORK;
break;
+ } else {
+ cardSTATE_TO_IDLE();
+ break;
}
-Dbprintf("err write block: %d len:%d", cardWRBL, len);
break;
}
-
+
+ case MFEMUL_INTREG_INC:{
+ mf_crypto1_decrypt(pcs, receivedCmd, len);
+ memcpy(&ans, receivedCmd, 4);
+ if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) {
+ EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
+ cardSTATE_TO_IDLE();
+ break;
+ }
+ cardINTREG = cardINTREG + ans;
+ cardSTATE = MFEMUL_WORK;
+ break;
+ }
+ case MFEMUL_INTREG_DEC:{
+ mf_crypto1_decrypt(pcs, receivedCmd, len);
+ memcpy(&ans, receivedCmd, 4);
+ if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) {
+ EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
+ cardSTATE_TO_IDLE();
+ break;
+ }
+ cardINTREG = cardINTREG - ans;
+ cardSTATE = MFEMUL_WORK;
+ break;
+ }
+ case MFEMUL_INTREG_REST:{
+ mf_crypto1_decrypt(pcs, receivedCmd, len);
+ memcpy(&ans, receivedCmd, 4);
+ if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) {
+ EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA));
+ cardSTATE_TO_IDLE();
+ break;
+ }
+ cardSTATE = MFEMUL_WORK;
+ break;
+ }
}
-
}
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
memset(rAUTH_NT, 0x44, 4);
LogTrace(rAUTH_NT, 4, 0, 0, TRUE);
- DbpString("Emulator stopped.");
+ if (MF_DBGLEVEL >= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, traceLen);
+}
+
+//-----------------------------------------------------------------------------
+// MIFARE sniffer.
+//
+//-----------------------------------------------------------------------------
+void RAMFUNC SniffMifare(uint8_t param) {
+ // param:
+ // bit 0 - trigger from first card answer
+ // bit 1 - trigger from first reader 7-bit request
+
+ // C(red) A(yellow) B(green)
+ LEDsoff();
+ // init trace buffer
+ iso14a_clear_trace();
+
+ // The command (reader -> tag) that we're receiving.
+ // The length of a received command will in most cases be no more than 18 bytes.
+ // So 32 should be enough!
+ uint8_t *receivedCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET);
+ // The response (tag -> reader) that we're receiving.
+ uint8_t *receivedResponse = (((uint8_t *)BigBuf) + RECV_RES_OFFSET);
+
+ // As we receive stuff, we copy it from receivedCmd or receivedResponse
+ // into trace, along with its length and other annotations.
+ //uint8_t *trace = (uint8_t *)BigBuf;
+
+ // The DMA buffer, used to stream samples from the FPGA
+ int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET;
+ int8_t *data = dmaBuf;
+ int maxDataLen = 0;
+ int dataLen = 0;
+
+ // Set up the demodulator for tag -> reader responses.
+ Demod.output = receivedResponse;
+ Demod.len = 0;
+ Demod.state = DEMOD_UNSYNCD;
+
+ // Set up the demodulator for the reader -> tag commands
+ memset(&Uart, 0, sizeof(Uart));
+ Uart.output = receivedCmd;
+ Uart.byteCntMax = 32; // was 100 (greg)//////////////////
+ Uart.state = STATE_UNSYNCD;
+
+ // Setup for the DMA.
+ FpgaSetupSsc();
+ FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE);
+
+ // And put the FPGA in the appropriate mode
+ // Signal field is off with the appropriate LED
+ LED_D_OFF();
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER);
+ SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
+
+ // init sniffer
+ MfSniffInit();
+ int sniffCounter = 0;
+
+ // And now we loop, receiving samples.
+ while(true) {
+ if(BUTTON_PRESS()) {
+ DbpString("cancelled by button");
+ goto done;
+ }
+
+ LED_A_ON();
+ WDT_HIT();
+
+ if (++sniffCounter > 65) {
+ if (MfSniffSend(2000)) {
+ FpgaEnableSscDma();
+ }
+ sniffCounter = 0;
+ }
+
+ int register readBufDataP = data - dmaBuf;
+ int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR;
+ if (readBufDataP <= dmaBufDataP){
+ dataLen = dmaBufDataP - readBufDataP;
+ } else {
+ dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP + 1;
+ }
+ // test for length of buffer
+ if(dataLen > maxDataLen) {
+ maxDataLen = dataLen;
+ if(dataLen > 400) {
+ Dbprintf("blew circular buffer! dataLen=0x%x", dataLen);
+ goto done;
+ }
+ }
+ if(dataLen < 1) continue;
+
+ // primary buffer was stopped( <-- we lost data!
+ if (!AT91C_BASE_PDC_SSC->PDC_RCR) {
+ AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dmaBuf;
+ AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE;
+ Dbprintf("RxEmpty ERROR!!! data length:%d", dataLen); // temporary
+ }
+ // secondary buffer sets as primary, secondary buffer was stopped
+ if (!AT91C_BASE_PDC_SSC->PDC_RNCR) {
+ AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf;
+ AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE;
+ }
+
+ LED_A_OFF();
+
+ if(MillerDecoding((data[0] & 0xF0) >> 4)) {
+ LED_C_INV();
+ // check - if there is a short 7bit request from reader
+ if (MfSniffLogic(receivedCmd, Uart.byteCnt, Uart.parityBits, Uart.bitCnt, TRUE)) break;
+
+ /* And ready to receive another command. */
+ Uart.state = STATE_UNSYNCD;
+
+ /* And also reset the demod code */
+ Demod.state = DEMOD_UNSYNCD;
+ }
+
+ if(ManchesterDecoding(data[0] & 0x0F)) {
+ LED_C_INV();
+
+ if (MfSniffLogic(receivedResponse, Demod.len, Demod.parityBits, Demod.bitCount, FALSE)) break;
+
+ // And ready to receive another response.
+ memset(&Demod, 0, sizeof(Demod));
+ Demod.output = receivedResponse;
+ Demod.state = DEMOD_UNSYNCD;
+
+ /* And also reset the uart code */
+ Uart.state = STATE_UNSYNCD;
+ }
+
+ data++;
+ if(data > dmaBuf + DMA_BUFFER_SIZE) {
+ data = dmaBuf;
+ }
+ } // main cycle
+
+ DbpString("COMMAND FINISHED");
+
+done:
+ FpgaDisableSscDma();
+ MfSniffEnd();
+
+ Dbprintf("maxDataLen=%x, Uart.state=%x, Uart.byteCnt=%x Uart.byteCntMax=%x", maxDataLen, Uart.state, Uart.byteCnt, Uart.byteCntMax);
+ LEDsoff();
}