From 67ac4bf75c6953cae546159acd3e887d757bd0c3 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 2 Jun 2015 07:22:23 +0200 Subject: [PATCH 01/18] fix issue #103: revert type change. Samples from FPGA are signed. Renamed iso14443.c to iso14443b.c --- armsrc/Makefile | 2 +- armsrc/{iso14443.c => iso14443b.c} | 34 +++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 11 deletions(-) rename armsrc/{iso14443.c => iso14443b.c} (98%) diff --git a/armsrc/Makefile b/armsrc/Makefile index 899b03075..502ab958c 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -17,7 +17,7 @@ APP_CFLAGS = -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ SRC_LF = lfops.c hitag2.c lfsampling.c SRC_ISO15693 = iso15693.c iso15693tools.c SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c -SRC_ISO14443b = iso14443.c +SRC_ISO14443b = iso14443b.c SRC_CRAPTO1 = crapto1.c crypto1.c des.c aes.c SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c diff --git a/armsrc/iso14443.c b/armsrc/iso14443b.c similarity index 98% rename from armsrc/iso14443.c rename to armsrc/iso14443b.c index c202e312d..8add8f9c3 100644 --- a/armsrc/iso14443.c +++ b/armsrc/iso14443b.c @@ -619,6 +619,8 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) if (Demod.state == DEMOD_UNSYNCD) LED_C_OFF(); // Not synchronized... return FALSE; } + + static void DemodReset() { // Clear out the state of the "UART" that receives from the tag. @@ -626,12 +628,15 @@ static void DemodReset() Demod.state = DEMOD_UNSYNCD; memset(Demod.output, 0x00, MAX_FRAME_SIZE); } + + static void DemodInit(uint8_t *data) { Demod.output = data; DemodReset(); } + static void UartReset() { Uart.byteCntMax = MAX_FRAME_SIZE; @@ -639,12 +644,15 @@ static void UartReset() Uart.byteCnt = 0; Uart.bitCnt = 0; } + + static void UartInit(uint8_t *data) { Uart.output = data; UartReset(); } + /* * Demodulate the samples we received from the tag, also log to tracebuffer * weTx: set to 'TRUE' if we behave like a reader @@ -665,15 +673,15 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); // Setup and start DMA. - FpgaSetupSscDma(dmaBuf, DMA_BUFFER_SIZE); + FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); - uint8_t *upTo= dmaBuf; + int8_t *upTo = dmaBuf; lastRxCounter = DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: @@ -724,6 +732,7 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) } } + //----------------------------------------------------------------------------- // Read the tag's response. We just receive a stream of slightly-processed // samples from the FPGA, which we will later do some signal processing on, @@ -756,6 +765,7 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) } }*/ + //----------------------------------------------------------------------------- // Transmit the command (to the tag) that was placed in ToSend[]. //----------------------------------------------------------------------------- @@ -806,6 +816,7 @@ static void TransmitFor14443(void) LED_B_OFF(); // Finished sending } + //----------------------------------------------------------------------------- // Code a layer 2 command (string of octets, including CRC) into ToSend[], // so that it is ready to transmit to the tag using TransmitFor14443(). @@ -862,6 +873,7 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) ToSendMax++; } + //----------------------------------------------------------------------------- // Read an ISO 14443 tag. We send it some set of commands, and record the // responses. @@ -877,6 +889,7 @@ void AcquireRawAdcSamplesIso14443(uint32_t parameter) SendRawCommand14443B(sizeof(cmd1),1,1,cmd1); } + /** Convenience function to encode, transmit and trace iso 14443b comms **/ @@ -891,6 +904,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) } } + //----------------------------------------------------------------------------- // Read a SRI512 ISO 14443 tag. // @@ -1059,9 +1073,9 @@ void RAMFUNC SnoopIso14443(void) set_tracing(TRUE); // The DMA buffer, used to stream samples from the FPGA - uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); int lastRxCounter; - uint8_t *upTo; + int8_t *upTo; int ci, cq; int maxBehindBy = 0; @@ -1092,7 +1106,7 @@ void RAMFUNC SnoopIso14443(void) FpgaSetupSsc(); upTo = dmaBuf; lastRxCounter = DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); + FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; LED_A_ON(); @@ -1138,7 +1152,7 @@ void RAMFUNC SnoopIso14443(void) if(Handle14443UartBit(cq & 1)) { if(triggered && tracing) { GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); + LogTrace(Uart.output,Uart.byteCnt,samples, samples, parity, TRUE); } if(Uart.byteCnt==0) Dbprintf("[2] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); @@ -1156,7 +1170,7 @@ void RAMFUNC SnoopIso14443(void) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(Demod.output, Demod.len, parity); - LogTrace(Demod.output,Demod.len,samples, samples,parity,FALSE); + LogTrace(Demod.output, Demod.len,samples, samples, parity, FALSE); } triggered = TRUE; LED_A_OFF(); @@ -1190,6 +1204,7 @@ void RAMFUNC SnoopIso14443(void) Dbprintf(" Trace length: %i", BigBuf_get_traceLen()); } + /* * Send raw command to tag ISO14443B * @Input @@ -1202,8 +1217,7 @@ void RAMFUNC SnoopIso14443(void) * none * */ - -void SendRawCommand14443B(uint32_t datalen, uint32_t recv,uint8_t powerfield, uint8_t data[]) +void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, uint8_t data[]) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); if(!powerfield) From 5b95953d4227d9af4b5a5f20156b668bba55aac8 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 2 Jun 2015 22:27:14 +0200 Subject: [PATCH 02/18] fixing iso14443b (issue #103): - most significant bit of tag data (which happens to be the sign bit) had been dropped when snooping (FPGA change) - avoid trying to decode both tag and reader data when snooping (we don't have the time to do so). --- armsrc/iso14443b.c | 96 ++++++++++++++++++++++------------------ fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 13 +++--- 3 files changed, 60 insertions(+), 49 deletions(-) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 8add8f9c3..d65955867 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -158,7 +158,6 @@ static int Handle14443UartBit(int bit) { switch(Uart.state) { case STATE_UNSYNCD: - LED_A_OFF(); if(!bit) { // we went low, so this could be the beginning // of an SOF @@ -272,8 +271,7 @@ static int Handle14443UartBit(int bit) break; } - // This row make the error blew circular buffer in hf 14b snoop - //if (Uart.state == STATE_ERROR_WAIT) LED_A_OFF(); // Error + if (Uart.state == STATE_UNSYNCD) LED_A_OFF(); return FALSE; } @@ -1054,17 +1052,17 @@ void ReadSTMemoryIso14443(uint32_t dwLast) //----------------------------------------------------------------------------- /* * Memory usage for this function, (within BigBuf) - * 0-4095 : Demodulated samples receive (4096 bytes) - DEMOD_TRACE_SIZE - * 4096-6143 : Last Received command, 2048 bytes (reader->tag) - READER_TAG_BUFFER_SIZE - * 6144-8191 : Last Received command, 2048 bytes(tag->reader) - TAG_READER_BUFFER_SIZE - * 8192-9215 : DMA Buffer, 1024 bytes (samples) - DEMOD_DMA_BUFFER_SIZE + * Last Received command (reader->tag) - MAX_FRAME_SIZE + * Last Received command (tag->reader) - MAX_FRAME_SIZE + * DMA Buffer, 1024 bytes (samples) - DMA_BUFFER_SIZE + * Demodulated samples received - all the rest */ void RAMFUNC SnoopIso14443(void) { // 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 = TRUE; + int triggered = TRUE; // TODO: set and evaluate trigger condition FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BigBuf_free(); @@ -1109,7 +1107,10 @@ void RAMFUNC SnoopIso14443(void) FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; LED_A_ON(); - + + bool TagIsActive = FALSE; + bool ReaderIsActive = FALSE; + // And now we loop, receiving samples. for(;;) { int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & @@ -1136,49 +1137,56 @@ void RAMFUNC SnoopIso14443(void) samples += 2; - if(Handle14443UartBit(ci & 1)) { - if(triggered && tracing) { - GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); - } - if(Uart.byteCnt==0) Dbprintf("[1] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); + if (!TagIsActive) { // no need to try decoding reader data if the tag is sending + if(Handle14443UartBit(ci & 0x01)) { + if(triggered && tracing) { + GetParity(Uart.output, Uart.byteCnt, parity); + LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); + } + if(Uart.byteCnt==0) Dbprintf("[1] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); - /* And ready to receive another command. */ - UartReset(); - /* And also reset the demod code, which might have been */ - /* false-triggered by the commands from the reader. */ - DemodReset(); - } - if(Handle14443UartBit(cq & 1)) { - if(triggered && tracing) { - GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt,samples, samples, parity, TRUE); + /* And ready to receive another command. */ + UartReset(); + /* And also reset the demod code, which might have been */ + /* false-triggered by the commands from the reader. */ + DemodReset(); } - if(Uart.byteCnt==0) Dbprintf("[2] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); + if(Handle14443UartBit(cq & 0x01)) { + if(triggered && tracing) { + GetParity(Uart.output, Uart.byteCnt, parity); + LogTrace(Uart.output,Uart.byteCnt,samples, samples, parity, TRUE); + } + if(Uart.byteCnt==0) Dbprintf("[2] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); - /* And ready to receive another command. */ - UartReset(); - /* And also reset the demod code, which might have been */ - /* false-triggered by the commands from the reader. */ - DemodReset(); + /* And ready to receive another command. */ + UartReset(); + /* And also reset the demod code, which might have been */ + /* false-triggered by the commands from the reader. */ + DemodReset(); + } + ReaderIsActive = (Uart.state != STATE_UNSYNCD); } - if(Handle14443SamplesDemod(ci, cq)) { + if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time + if(Handle14443SamplesDemod(ci, cq)) { - //Use samples as a time measurement - if(tracing) - { - uint8_t parity[MAX_PARITY_SIZE]; - GetParity(Demod.output, Demod.len, parity); - LogTrace(Demod.output, Demod.len,samples, samples, parity, FALSE); + //Use samples as a time measurement + if(tracing) + { + uint8_t parity[MAX_PARITY_SIZE]; + GetParity(Demod.output, Demod.len, parity); + LogTrace(Demod.output, Demod.len,samples, samples, parity, FALSE); + } + triggered = TRUE; + LED_A_OFF(); + LED_B_ON(); + + // And ready to receive another response. + DemodReset(); } - triggered = TRUE; - LED_A_OFF(); - LED_B_ON(); - - // And ready to receive another response. - DemodReset(); + TagIsActive = (Demod.state != DEMOD_UNSYNCD); } + WDT_HIT(); if(!tracing) { diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 20fb2bd4401254d899d6273451bfccbd827f562b..53078a782422c09596f006d95c85ae45da20072d 100644 GIT binary patch literal 42175 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 literal 42175 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 diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index dece2db31..ec6583b27 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -99,8 +99,10 @@ end reg [5:0] corr_i_cnt; reg [5:0] corr_q_cnt; // And a couple of registers in which to accumulate the correlations. -reg signed [15:0] corr_i_accum; -reg signed [15:0] corr_q_accum; +// we would add at most 32 times adc_d, the result can be held in 13 bits. +// Need one additional bit because it can be negative as well +reg signed [13:0] corr_i_accum; +reg signed [13:0] corr_q_accum; reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; @@ -114,12 +116,13 @@ begin begin if(snoop) begin - corr_i_out <= {corr_i_accum[12:6], after_hysteresis_prev}; - corr_q_out <= {corr_q_accum[12:6], after_hysteresis}; + // highest 7 significant bits of tag signal (signed), 1 bit reader signal: + corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev}; + corr_q_out <= {corr_q_accum[13:7], after_hysteresis}; end else begin - // Only correlations need to be delivered. + // highest 8 significant bits of tag signal corr_i_out <= corr_i_accum[13:6]; corr_q_out <= corr_q_accum[13:6]; end From 09c66f1f09776989fda2d1005a2c9feb1db6b3ac Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 3 Jun 2015 13:28:28 +0200 Subject: [PATCH 03/18] fixing iso14443b (issue #103): fix timing issue (speeding up the decoders) --- armsrc/iso14443b.c | 22 +++++++++++----------- fpga/hi_read_rx_xcorr.v | 4 +++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index d65955867..4e40bb688 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -210,7 +210,6 @@ static int Handle14443UartBit(int bit) Uart.bitCnt = 0; Uart.shiftReg = 0; Uart.state = STATE_RECEIVING_DATA; - LED_A_ON(); // Indicate we're receiving } break; @@ -263,6 +262,7 @@ static int Handle14443UartBit(int bit) Uart.posCnt++; if(Uart.posCnt > 10) { Uart.state = STATE_UNSYNCD; + LED_A_OFF(); } break; @@ -271,8 +271,6 @@ static int Handle14443UartBit(int bit) break; } - if (Uart.state == STATE_UNSYNCD) LED_A_OFF(); - return FALSE; } @@ -548,6 +546,7 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) } else { if(Demod.posCount > 100) { Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); } } Demod.posCount++; @@ -558,6 +557,7 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) if(v > 0) { if(Demod.posCount > 10) { Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); } } else { Demod.bitCount = 0; @@ -596,13 +596,13 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) Demod.output[Demod.len] = b; Demod.len++; Demod.state = DEMOD_AWAITING_START_BIT; - } else if(s == 0x000) { - // This is EOF - LED_C_OFF(); - Demod.state = DEMOD_UNSYNCD; - return TRUE; } else { Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + if(s == 0x000) { + // This is EOF + return TRUE; + } } } Demod.posCount = 0; @@ -611,10 +611,10 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) default: Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); break; } - if (Demod.state == DEMOD_UNSYNCD) LED_C_OFF(); // Not synchronized... return FALSE; } @@ -1168,14 +1168,14 @@ void RAMFUNC SnoopIso14443(void) } if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if(Handle14443SamplesDemod(ci, cq)) { + if(Handle14443SamplesDemod(ci & 0xFE, cq & 0xFE)) { //Use samples as a time measurement if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(Demod.output, Demod.len, parity); - LogTrace(Demod.output, Demod.len,samples, samples, parity, FALSE); + LogTrace(Demod.output, Demod.len, samples, samples, parity, FALSE); } triggered = TRUE; LED_A_OFF(); diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index ec6583b27..06142637b 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -171,7 +171,9 @@ begin end end - if(corr_i_cnt[5:2] == 4'b000 || corr_i_cnt[5:2] == 4'b1000) + // set ssp_frame signal for corr_i_cnt = 0..3 and corr_i_cnt = 32..35 + // (two frames with 8 Bits each) + if(corr_i_cnt[5:2] == 4'b0000 || corr_i_cnt[5:2] == 4'b1000) ssp_frame = 1'b1; else ssp_frame = 1'b0; From 4a3f1a379314368330fab879a616cf7b9e5e25c4 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Thu, 11 Jun 2015 23:52:40 -0400 Subject: [PATCH 04/18] revert t55xx start gap and write timing changes also noted specs in comments --- armsrc/lfops.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index c3fa8a0e6..7e53d4a56 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -1024,10 +1024,10 @@ void CmdIOdemodFSK(int findone, int *high, int *low, int ledcontrol) * To compensate antenna falling times shorten the write times * and enlarge the gap ones. */ -#define START_GAP 50*8 // 10 - 50fc 250 -#define WRITE_GAP 20*8 // - 30fc 160 -#define WRITE_0 24*8 // 16 - 63fc 54fc 144 -#define WRITE_1 54*8 // 48 - 63fc 54fc 432 for T55x7; 448 for E5550 //400 +#define START_GAP 31*8 // was 250 // SPEC: 1*8 to 50*8 - typ 15*8 (or 15fc) +#define WRITE_GAP 20*8 // was 160 // SPEC: 1*8 to 20*8 - typ 10*8 (or 10fc) +#define WRITE_0 18*8 // was 144 // SPEC: 16*8 to 32*8 - typ 24*8 (or 24fc) +#define WRITE_1 50*8 // was 400 // SPEC: 48*8 to 64*8 - typ 56*8 (or 56fc) 432 for T55x7; 448 for E5550 #define T55xx_SAMPLES_SIZE 12000 // 32 x 32 x 10 (32 bit times numofblock (7), times clock skip..) From 6ac4cb270a202d6f723b82753d57ee48c9a6980e Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Mon, 15 Jun 2015 00:44:57 -0400 Subject: [PATCH 05/18] minor fixes see @icemant1001 - https://github.com/iceman1001/proxmark3/commit/0ad1a1d492ab62eb42c1eb7b4ce6fff05d90e0a3 for more details --- client/cmddata.c | 2 +- client/cmdlfem4x.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/cmddata.c b/client/cmddata.c index 309044e10..aa1170fc7 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -392,7 +392,7 @@ int Cmdmandecoderaw(const char *Cmd) int errCnt=0; size_t size=0; int invert=0; - size_t maxErr = 20; + int maxErr = 20; char cmdp = param_getchar(Cmd, 0); if (strlen(Cmd) > 5 || cmdp == 'h' || cmdp == 'H') { PrintAndLog("Usage: data manrawdecode [invert] [maxErr]"); diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index c492a64d5..eddeec560 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -20,6 +20,9 @@ #include "cmdlf.h" #include "cmdlfem4x.h" #include "lfdemod.h" + +#define llx PRIx64 + char *global_em410xId; static int CmdHelp(const char *Cmd); From 51d4f6f1146b083f12381419bbfb6addd550e6a3 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 12 Jun 2015 07:43:00 +0200 Subject: [PATCH 06/18] fixing iso14443b (issue #103): - fix: IQ demodulator (FPGA) - fix: approximately align reader signal delay to tag response delay (FPGA) - fix: remove deprecated RSSI calculation to improve decoder speed (iso14443b.c) - fix: better approximation of signal amplitude to avoid false carrier detection (iso14443b.c) - fix: remove initial power off in iso14443b raw command (iso14443b.c) - add: enable tracing for iso14443b raw command (iso14443b.c) - fix: client crashed when checking CRC for incomplete responses (iso14433b.c) - speeding up snoop to avoid circular buffer overflow - added some comments for better documentation - rename functions (iso14443 -> iso14443b) - remove unused code in hi_read_rx_xcorr.v --- armsrc/appmain.c | 12 +- armsrc/apps.h | 8 +- armsrc/iso14443b.c | 476 ++++++++++++++++++++-------------------- client/cmdhf14b.c | 14 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/fpga_hf.v | 11 +- fpga/hi_read_rx_xcorr.v | 78 ++----- 7 files changed, 279 insertions(+), 320 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index c226c7263..9bfa5ea7a 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -263,7 +263,7 @@ void SimulateTagHfListen(void) // We're using this mode just so that I can test it out; the simulated // tag mode would work just as well and be simpler. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_SNOOP); // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -783,19 +783,19 @@ void UsbPacketReceived(uint8_t *packet, int len) #ifdef WITH_ISO14443b case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443: - AcquireRawAdcSamplesIso14443(c->arg[0]); + AcquireRawAdcSamplesIso14443b(c->arg[0]); break; case CMD_READ_SRI512_TAG: - ReadSTMemoryIso14443(0x0F); + ReadSTMemoryIso14443b(0x0F); break; case CMD_READ_SRIX4K_TAG: - ReadSTMemoryIso14443(0x7F); + ReadSTMemoryIso14443b(0x7F); break; case CMD_SNOOP_ISO_14443: - SnoopIso14443(); + SnoopIso14443b(); break; case CMD_SIMULATE_TAG_ISO_14443: - SimulateIso14443Tag(); + SimulateIso14443bTag(); break; case CMD_ISO_14443B_COMMAND: SendRawCommand14443B(c->arg[0],c->arg[1],c->arg[2],c->d.asBytes); diff --git a/armsrc/apps.h b/armsrc/apps.h index 6360b664b..542f3a650 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -141,10 +141,10 @@ void EM4xReadWord(uint8_t Address, uint32_t Pwd, uint8_t PwdMode); void EM4xWriteWord(uint32_t Data, uint8_t Address, uint32_t Pwd, uint8_t PwdMode); /// iso14443.h -void SimulateIso14443Tag(void); -void AcquireRawAdcSamplesIso14443(uint32_t parameter); -void ReadSTMemoryIso14443(uint32_t); -void RAMFUNC SnoopIso14443(void); +void SimulateIso14443bTag(void); +void AcquireRawAdcSamplesIso14443b(uint32_t parameter); +void ReadSTMemoryIso14443b(uint32_t); +void RAMFUNC SnoopIso14443b(void); void SendRawCommand14443B(uint32_t, uint32_t, uint8_t, uint8_t[]); /// iso14443a.h diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 4e40bb688..f598df3c2 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -5,9 +5,8 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Routines to support ISO 14443. This includes both the reader software and -// the `fake tag' modes. At the moment only the Type B modulation is -// supported. +// Routines to support ISO 14443B. This includes both the reader software and +// the `fake tag' modes. //----------------------------------------------------------------------------- #include "proxmark3.h" @@ -17,15 +16,8 @@ #include "iso14443crc.h" -//static void GetSamplesFor14443(int weTx, int n); - -/*#define DEMOD_TRACE_SIZE 4096 -#define READER_TAG_BUFFER_SIZE 2048 -#define TAG_READER_BUFFER_SIZE 2048 -#define DEMOD_DMA_BUFFER_SIZE 1024 -*/ - #define RECEIVE_SAMPLES_TIMEOUT 2000 +#define ISO14443B_DMA_BUFFER_SIZE 512 //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using @@ -104,14 +96,14 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) ToSendStuffBit(1); } - // Send SOF. + // Send EOF. for(i = 0; i < 10; i++) { ToSendStuffBit(0); ToSendStuffBit(0); ToSendStuffBit(0); ToSendStuffBit(0); } - for(i = 0; i < 10; i++) { + for(i = 0; i < 2; i++) { ToSendStuffBit(1); ToSendStuffBit(1); ToSendStuffBit(1); @@ -120,9 +112,6 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) // Convert from last byte pos to length ToSendMax++; - - // Add a few more for slop - ToSendMax += 2; } //----------------------------------------------------------------------------- @@ -146,6 +135,9 @@ static struct { } Uart; /* Receive & handle a bit coming from the reader. + * + * This function is called 4 times per bit (every 2 subcarrier cycles). + * Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 2,36us * * LED handling: * LED A -> ON once we have received the SOF and are expecting the rest. @@ -154,7 +146,7 @@ static struct { * Returns: true if we received a EOF * false if we are still waiting for some more */ -static int Handle14443UartBit(int bit) +static int Handle14443bUartBit(int bit) { switch(Uart.state) { case STATE_UNSYNCD: @@ -169,9 +161,9 @@ static int Handle14443UartBit(int bit) case STATE_GOT_FALLING_EDGE_OF_SOF: Uart.posCnt++; - if(Uart.posCnt == 2) { + if(Uart.posCnt == 2) { // sample every 4 1/fs in the middle of a bit if(bit) { - if(Uart.bitCnt >= 10) { + if(Uart.bitCnt > 9) { // we've seen enough consecutive // zeros that it's a valid SOF Uart.posCnt = 0; @@ -189,7 +181,7 @@ static int Handle14443UartBit(int bit) Uart.bitCnt++; } if(Uart.posCnt >= 4) Uart.posCnt = 0; - if(Uart.bitCnt > 14) { + if(Uart.bitCnt > 12) { // Give up if we see too many zeros without // a one, too. Uart.state = STATE_ERROR_WAIT; @@ -199,7 +191,7 @@ static int Handle14443UartBit(int bit) case STATE_AWAITING_START_BIT: Uart.posCnt++; if(bit) { - if(Uart.posCnt > 25) { + if(Uart.posCnt > 50/2) { // max 57us between characters = 49 1/fs, max 3 etus after low phase of SOF = 24 1/fs // stayed high for too long between // characters, error Uart.state = STATE_ERROR_WAIT; @@ -283,12 +275,12 @@ static int Handle14443UartBit(int bit) // Assume that we're called with the SSC (to the FPGA) and ADC path set // correctly. //----------------------------------------------------------------------------- -static int GetIso14443CommandFromReader(uint8_t *received, int *len, int maxLen) +static int GetIso14443bCommandFromReader(uint8_t *received, int *len, int maxLen) { uint8_t mask; int i, bit; - // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen + // Set FPGA mode to "simulated ISO 14443B tag", no modulation (listen // only, since we are receiving, not transmitting). // Signal field is off with the appropriate LED LED_D_OFF(); @@ -314,7 +306,7 @@ static int GetIso14443CommandFromReader(uint8_t *received, int *len, int maxLen) mask = 0x80; for(i = 0; i < 8; i++, mask >>= 1) { bit = (b & mask); - if(Handle14443UartBit(bit)) { + if(Handle14443bUartBit(bit)) { *len = Uart.byteCnt; return TRUE; } @@ -327,9 +319,13 @@ static int GetIso14443CommandFromReader(uint8_t *received, int *len, int maxLen) // Main loop of simulated tag: receive commands from reader, decide what // response to send, and send it. //----------------------------------------------------------------------------- -void SimulateIso14443Tag(void) +void SimulateIso14443bTag(void) { + // the only command we understand is REQB, AFI=0, Select All, N=0: static const uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; + // ... and we respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, + // supports only 106kBit/s in both directions, max frame size = 32Bytes, + // supports ISO14443-4, FWI=8 (77ms), NAD supported, CID not supported: static const uint8_t response1[] = { 0x50, 0x82, 0x0d, 0xe1, 0x74, 0x20, 0x38, 0x19, 0x22, 0x00, 0x21, 0x85, 0x5e, 0xd7 @@ -338,10 +334,9 @@ void SimulateIso14443Tag(void) uint8_t *resp; int respLen; - uint8_t *resp1 = BigBuf_get_addr() + 800; - int resp1Len; - - uint8_t *receivedCmd = BigBuf_get_addr(); + // allocate command receive buffer + BigBuf_free(); + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); int len; int i; @@ -349,10 +344,12 @@ void SimulateIso14443Tag(void) int cmdsRecvd = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - memset(receivedCmd, 0x44, 400); + // prepare the (only one) tag answer: CodeIso14443bAsTag(response1, sizeof(response1)); - memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax; + uint8_t *resp1 = BigBuf_malloc(ToSendMax); + memcpy(resp1, ToSend, ToSendMax); + uint16_t resp1Len = ToSendMax; // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -363,14 +360,14 @@ void SimulateIso14443Tag(void) for(;;) { uint8_t b1, b2; - if(!GetIso14443CommandFromReader(receivedCmd, &len, 100)) { - Dbprintf("button pressed, received %d commands", cmdsRecvd); - break; - } + if(!GetIso14443bCommandFromReader(receivedCmd, &len, 100)) { + Dbprintf("button pressed, received %d commands", cmdsRecvd); + break; + } // Good, look at the command now. - if(len == sizeof(cmd1) && memcmp(receivedCmd, cmd1, len)==0) { + if(len == sizeof(cmd1) && memcmp(receivedCmd, cmd1, len) == 0) { resp = resp1; respLen = resp1Len; } else { Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsRecvd); @@ -385,8 +382,6 @@ void SimulateIso14443Tag(void) break; } - memset(receivedCmd, 0x44, 32); - cmdsRecvd++; if(cmdsRecvd > 0x30) { @@ -444,8 +439,10 @@ static struct { int bitCount; int posCount; int thisBit; +/* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. int metric; int metricN; +*/ uint16_t shiftReg; uint8_t *output; int len; @@ -456,6 +453,9 @@ static struct { /* * Handles reception of a bit from the tag * + * This function is called 2 times per bit (every 4 subcarrier cycles). + * Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 4,72us + * * LED handling: * LED C -> ON once we have received the SOF and are expecting the rest. * LED C -> OFF once we have received EOF or are unsynced @@ -464,12 +464,12 @@ static struct { * false if we are still waiting for some more * */ -static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) +static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) { int v; - // The soft decision on the bit uses an estimate of just the - // quadrant of the reference angle, not the exact angle. +// The soft decision on the bit uses an estimate of just the +// quadrant of the reference angle, not the exact angle. #define MAKE_SOFT_DECISION() { \ if(Demod.sumI > 0) { \ v = ci; \ @@ -483,47 +483,87 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) } \ } +#define SUBCARRIER_DETECT_THRESHOLD 8 + +// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) +/* #define CHECK_FOR_SUBCARRIER() { \ + v = ci; \ + if(v < 0) v = -v; \ + if(cq > 0) { \ + v += cq; \ + } else { \ + v -= cq; \ + } \ + } + */ +// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) +#define CHECK_FOR_SUBCARRIER() { \ + if(ci < 0) { \ + if(cq < 0) { /* ci < 0, cq < 0 */ \ + if (cq < ci) { \ + v = -cq - (ci >> 1); \ + } else { \ + v = -ci - (cq >> 1); \ + } \ + } else { /* ci < 0, cq >= 0 */ \ + if (cq < -ci) { \ + v = -ci + (cq >> 1); \ + } else { \ + v = cq - (ci >> 1); \ + } \ + } \ + } else { \ + if(cq < 0) { /* ci >= 0, cq < 0 */ \ + if (-cq < ci) { \ + v = ci - (cq >> 1); \ + } else { \ + v = -cq + (ci >> 1); \ + } \ + } else { /* ci >= 0, cq >= 0 */ \ + if (cq < ci) { \ + v = ci + (cq >> 1); \ + } else { \ + v = cq + (ci >> 1); \ + } \ + } \ + } \ + } + switch(Demod.state) { case DEMOD_UNSYNCD: - v = ci; - if(v < 0) v = -v; - if(cq > 0) { - v += cq; - } else { - v -= cq; - } - if(v > 40) { - Demod.posCount = 0; + CHECK_FOR_SUBCARRIER(); + if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected Demod.state = DEMOD_PHASE_REF_TRAINING; - Demod.sumI = 0; - Demod.sumQ = 0; - } + Demod.sumI = ci; + Demod.sumQ = cq; + Demod.posCount = 1; + } break; case DEMOD_PHASE_REF_TRAINING: if(Demod.posCount < 8) { - Demod.sumI += ci; - Demod.sumQ += cq; - } else if(Demod.posCount > 100) { - // error, waited too long - Demod.state = DEMOD_UNSYNCD; - } else { - MAKE_SOFT_DECISION(); - if(v < 0) { - Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; - Demod.posCount = 0; + CHECK_FOR_SUBCARRIER(); + if (v > SUBCARRIER_DETECT_THRESHOLD) { + // set the reference phase (will code a logic '1') by averaging over 32 1/fs. + // note: synchronization time > 80 1/fs + Demod.sumI += ci; + Demod.sumQ += cq; + Demod.posCount++; + } else { // subcarrier lost + Demod.state = DEMOD_UNSYNCD; } + } else { + Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; } - Demod.posCount++; break; case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: MAKE_SOFT_DECISION(); - if(v < 0) { + if(v < 0) { // logic '0' detected Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; - Demod.posCount = 0; + Demod.posCount = 0; // start of SOF sequence } else { - if(Demod.posCount > 100) { + if(Demod.posCount > 200/4) { // maximum length of TR1 = 200 1/fs Demod.state = DEMOD_UNSYNCD; } } @@ -531,37 +571,40 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) break; case DEMOD_GOT_FALLING_EDGE_OF_SOF: + Demod.posCount++; MAKE_SOFT_DECISION(); if(v > 0) { - if(Demod.posCount < 12) { + if(Demod.posCount < 9*2) { // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges Demod.state = DEMOD_UNSYNCD; } else { LED_C_ON(); // Got SOF Demod.state = DEMOD_AWAITING_START_BIT; Demod.posCount = 0; Demod.len = 0; +/* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. Demod.metricN = 0; Demod.metric = 0; +*/ } } else { - if(Demod.posCount > 100) { + if(Demod.posCount > 12*2) { // low phase of SOF too long (> 12 etu) Demod.state = DEMOD_UNSYNCD; LED_C_OFF(); } } - Demod.posCount++; break; case DEMOD_AWAITING_START_BIT: + Demod.posCount++; MAKE_SOFT_DECISION(); if(v > 0) { - if(Demod.posCount > 10) { + if(Demod.posCount > 3*2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs Demod.state = DEMOD_UNSYNCD; LED_C_OFF(); } - } else { + } else { // start bit detected Demod.bitCount = 0; - Demod.posCount = 1; + Demod.posCount = 1; // this was the first half Demod.thisBit = v; Demod.shiftReg = 0; Demod.state = DEMOD_RECEIVING_DATA; @@ -570,28 +613,30 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) case DEMOD_RECEIVING_DATA: MAKE_SOFT_DECISION(); - if(Demod.posCount == 0) { + if(Demod.posCount == 0) { // first half of bit Demod.thisBit = v; Demod.posCount = 1; - } else { + } else { // second half of bit Demod.thisBit += v; +/* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. if(Demod.thisBit > 0) { Demod.metric += Demod.thisBit; } else { Demod.metric -= Demod.thisBit; } (Demod.metricN)++; +*/ Demod.shiftReg >>= 1; - if(Demod.thisBit > 0) { + if(Demod.thisBit > 0) { // logic '1' Demod.shiftReg |= 0x200; } Demod.bitCount++; if(Demod.bitCount == 10) { uint16_t s = Demod.shiftReg; - if((s & 0x200) && !(s & 0x001)) { + if((s & 0x200) && !(s & 0x001)) { // stop bit == '1', start bit == '0' uint8_t b = (s >> 1); Demod.output[Demod.len] = b; Demod.len++; @@ -600,7 +645,7 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) Demod.state = DEMOD_UNSYNCD; LED_C_OFF(); if(s == 0x000) { - // This is EOF + // This is EOF (start, stop and all data bits == '0' return TRUE; } } @@ -624,6 +669,7 @@ static void DemodReset() // Clear out the state of the "UART" that receives from the tag. Demod.len = 0; Demod.state = DEMOD_UNSYNCD; + Demod.posCount = 0; memset(Demod.output, 0x00, MAX_FRAME_SIZE); } @@ -653,14 +699,12 @@ static void UartInit(uint8_t *data) /* * Demodulate the samples we received from the tag, also log to tracebuffer - * weTx: set to 'TRUE' if we behave like a reader - * set to 'FALSE' if we behave like a snooper * quiet: set to 'TRUE' to disable debug output */ -static void GetSamplesFor14443Demod(int weTx, int n, int quiet) +static void GetSamplesFor14443bDemod(int n, bool quiet) { int max = 0; - int gotFrame = FALSE; + bool gotFrame = FALSE; int lastRxCounter, ci, cq, samples = 0; // Allocate memory from BigBuf for some buffers @@ -671,57 +715,56 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); // Setup and start DMA. - FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); int8_t *upTo = dmaBuf; - lastRxCounter = DMA_BUFFER_SIZE; + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: - if (weTx) LED_D_ON(); else LED_D_OFF(); + LED_D_ON(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - (weTx ? 0 : FPGA_HF_READER_RX_XCORR_SNOOP)); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); for(;;) { int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; if(behindBy > max) max = behindBy; - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (DMA_BUFFER_SIZE-1)) - > 2) - { + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1)) > 2) { ci = upTo[0]; cq = upTo[1]; upTo += 2; - if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { upTo = dmaBuf; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; } lastRxCounter -= 2; if(lastRxCounter <= 0) { - lastRxCounter += DMA_BUFFER_SIZE; + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; } samples += 2; - if(Handle14443SamplesDemod(ci, cq)) { - gotFrame = 1; + if(Handle14443bSamplesDemod(ci, cq)) { + gotFrame = TRUE; + break; } } - if(samples > n) { + if(samples > n || gotFrame) { break; } } + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; - if (!quiet) Dbprintf("%x %x %x", max, gotFrame, Demod.len); + + if (!quiet) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", max, samples, gotFrame, Demod.len, Demod.sumI, Demod.sumQ); //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; @@ -731,43 +774,10 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) } -//----------------------------------------------------------------------------- -// Read the tag's response. We just receive a stream of slightly-processed -// samples from the FPGA, which we will later do some signal processing on, -// to get the bits. -//----------------------------------------------------------------------------- -/*static void GetSamplesFor14443(int weTx, int n) -{ - uint8_t *dest = (uint8_t *)BigBuf; - int c; - - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - (weTx ? 0 : FPGA_HF_READER_RX_XCORR_SNOOP)); - - c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - - dest[c++] = (uint8_t)b; - - if(c >= n) { - break; - } - } - } -}*/ - - //----------------------------------------------------------------------------- // Transmit the command (to the tag) that was placed in ToSend[]. //----------------------------------------------------------------------------- -static void TransmitFor14443(void) +static void TransmitFor14443b(void) { int c; @@ -781,8 +791,7 @@ static void TransmitFor14443(void) LED_D_ON(); // Signal we are transmitting with the Green LED LED_B_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); for(c = 0; c < 10;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { @@ -817,7 +826,7 @@ static void TransmitFor14443(void) //----------------------------------------------------------------------------- // Code a layer 2 command (string of octets, including CRC) into ToSend[], -// so that it is ready to transmit to the tag using TransmitFor14443(). +// so that it is ready to transmit to the tag using TransmitFor14443b(). //----------------------------------------------------------------------------- static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { @@ -873,16 +882,16 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) //----------------------------------------------------------------------------- -// Read an ISO 14443 tag. We send it some set of commands, and record the +// Read an ISO 14443B tag. We send it some set of commands, and record the // responses. // The command name is misleading, it actually decodes the reponse in HEX // into the output buffer (read the result using hexsamples, not hisamples) // // obsolete function only for test //----------------------------------------------------------------------------- -void AcquireRawAdcSamplesIso14443(uint32_t parameter) +void AcquireRawAdcSamplesIso14443b(uint32_t parameter) { - uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; + uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; // REQB with AFI=0, Request All, N=0 SendRawCommand14443B(sizeof(cmd1),1,1,cmd1); } @@ -894,7 +903,7 @@ void AcquireRawAdcSamplesIso14443(uint32_t parameter) static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) { CodeIso14443bAsReader(cmd, len); - TransmitFor14443(); + TransmitFor14443b(); if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(cmd, len, parity); @@ -904,7 +913,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) //----------------------------------------------------------------------------- -// Read a SRI512 ISO 14443 tag. +// Read a SRI512 ISO 14443B tag. // // SRI512 tags are just simple memory tags, here we're looking at making a dump // of the contents of the memory. No anticollision algorithm is done, we assume @@ -912,7 +921,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) // // I tried to be systematic and check every answer of the tag, every CRC, etc... //----------------------------------------------------------------------------- -void ReadSTMemoryIso14443(uint32_t dwLast) +void ReadSTMemoryIso14443b(uint32_t dwLast) { clear_trace(); set_tracing(TRUE); @@ -933,15 +942,15 @@ void ReadSTMemoryIso14443(uint32_t dwLast) // Signal field is on with the appropriate LED LED_D_ON(); FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(200); // First command: wake up the tag using the INITIATE command - uint8_t cmd1[] = { 0x06, 0x00, 0x97, 0x5b}; + uint8_t cmd1[] = {0x06, 0x00, 0x97, 0x5b}; CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); // LED_A_ON(); - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len == 0) { @@ -949,7 +958,7 @@ void ReadSTMemoryIso14443(uint32_t dwLast) return; } else { Dbprintf("Randomly generated UID from tag (+ 2 byte CRC): %x %x %x", - Demod.output[0], Demod.output[1],Demod.output[2]); + Demod.output[0], Demod.output[1], Demod.output[2]); } // There is a response, SELECT the uid DbpString("Now SELECT tag:"); @@ -959,22 +968,22 @@ void ReadSTMemoryIso14443(uint32_t dwLast) CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); // LED_A_ON(); - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len != 3) { - Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); - return; + Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); + return; } // Check the CRC of the answer: ComputeCrc14443(CRC_14443_B, Demod.output, 1 , &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[1] || cmd1[3] != Demod.output[2]) { - DbpString("CRC Error reading select response."); - return; + DbpString("CRC Error reading select response."); + return; } // Check response from the tag: should be the same UID as the command we just sent: if (cmd1[1] != Demod.output[0]) { - Dbprintf("Bad response to SELECT from Tag, aborting: %x %x", cmd1[1], Demod.output[0]); - return; + Dbprintf("Bad response to SELECT from Tag, aborting: %x %x", cmd1[1], Demod.output[0]); + return; } // Tag is now selected, // First get the tag's UID: @@ -983,22 +992,22 @@ void ReadSTMemoryIso14443(uint32_t dwLast) CodeAndTransmit14443bAsReader(cmd1, 3); // Only first three bytes for this one // LED_A_ON(); - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len != 10) { - Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); - return; + Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); + return; } // The check the CRC of the answer (use cmd1 as temporary variable): ComputeCrc14443(CRC_14443_B, Demod.output, 8, &cmd1[2], &cmd1[3]); - if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); - // Do not return;, let's go on... (we should retry, maybe ?) + if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { + Dbprintf("CRC Error reading block! - Below: expected, got %x %x", + (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); + // Do not return;, let's go on... (we should retry, maybe ?) } Dbprintf("Tag UID (64 bits): %08x %08x", - (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); + (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); // Now loop to read all 16 blocks, address from 0 to last block Dbprintf("Tag memory dump, block 0 to %d",dwLast); @@ -1006,7 +1015,7 @@ void ReadSTMemoryIso14443(uint32_t dwLast) i = 0x00; dwLast++; for (;;) { - if (i == dwLast) { + if (i == dwLast) { DbpString("System area block (0xff):"); i = 0xff; } @@ -1015,25 +1024,25 @@ void ReadSTMemoryIso14443(uint32_t dwLast) CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); // LED_A_ON(); - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len != 6) { // Check if we got an answer from the tag - DbpString("Expected 6 bytes from tag, got less..."); - return; + DbpString("Expected 6 bytes from tag, got less..."); + return; } // The check the CRC of the answer (use cmd1 as temporary variable): ComputeCrc14443(CRC_14443_B, Demod.output, 4, &cmd1[2], &cmd1[3]); - if(cmd1[2] != Demod.output[4] || cmd1[3] != Demod.output[5]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); - // Do not return;, let's go on... (we should retry, maybe ?) + if(cmd1[2] != Demod.output[4] || cmd1[3] != Demod.output[5]) { + Dbprintf("CRC Error reading block! - Below: expected, got %x %x", + (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); + // Do not return;, let's go on... (we should retry, maybe ?) } // Now print out the memory location: Dbprintf("Address=%x, Contents=%x, CRC=%x", i, - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], - (Demod.output[4]<<8)+Demod.output[5]); + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], + (Demod.output[4]<<8)+Demod.output[5]); if (i == 0xff) { - break; + break; } i++; } @@ -1054,10 +1063,10 @@ void ReadSTMemoryIso14443(uint32_t dwLast) * Memory usage for this function, (within BigBuf) * Last Received command (reader->tag) - MAX_FRAME_SIZE * Last Received command (tag->reader) - MAX_FRAME_SIZE - * DMA Buffer, 1024 bytes (samples) - DMA_BUFFER_SIZE + * DMA Buffer - ISO14443B_DMA_BUFFER_SIZE * Demodulated samples received - all the rest */ -void RAMFUNC SnoopIso14443(void) +void RAMFUNC SnoopIso14443b(void) { // 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 @@ -1071,7 +1080,7 @@ void RAMFUNC SnoopIso14443(void) set_tracing(TRUE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); int lastRxCounter; int8_t *upTo; int ci, cq; @@ -1089,24 +1098,21 @@ void RAMFUNC SnoopIso14443(void) Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); Dbprintf(" Reader -> tag: %i bytes", MAX_FRAME_SIZE); Dbprintf(" tag -> Reader: %i bytes", MAX_FRAME_SIZE); - Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE); + Dbprintf(" DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); - // Signal field is off with the appropriate LED - LED_D_OFF(); + // Signal field is off, no reader signal, no tag signal + LEDsoff(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - FPGA_HF_READER_RX_XCORR_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_SNOOP); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. FpgaSetupSsc(); upTo = dmaBuf; - lastRxCounter = DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; - LED_A_ON(); bool TagIsActive = FALSE; bool ReaderIsActive = FALSE; @@ -1114,50 +1120,56 @@ void RAMFUNC SnoopIso14443(void) // And now we loop, receiving samples. for(;;) { int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (DMA_BUFFER_SIZE-1); + (ISO14443B_DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; - if(behindBy > (9*DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? - Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); - break; - } } + if(behindBy < 2) continue; ci = upTo[0]; cq = upTo[1]; upTo += 2; lastRxCounter -= 2; - if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { upTo = dmaBuf; - lastRxCounter += DMA_BUFFER_SIZE; + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + WDT_HIT(); + if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? + Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); + break; + } + if(!tracing) { + DbpString("Reached trace limit"); + break; + } + if(BUTTON_PRESS()) { + DbpString("cancelled"); + break; + } } samples += 2; if (!TagIsActive) { // no need to try decoding reader data if the tag is sending - if(Handle14443UartBit(ci & 0x01)) { + if(Handle14443bUartBit(ci & 0x01)) { if(triggered && tracing) { GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); + LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); } - if(Uart.byteCnt==0) Dbprintf("[1] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); - /* And ready to receive another command. */ UartReset(); /* And also reset the demod code, which might have been */ /* false-triggered by the commands from the reader. */ DemodReset(); } - if(Handle14443UartBit(cq & 0x01)) { + if(Handle14443bUartBit(cq & 0x01)) { if(triggered && tracing) { GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt,samples, samples, parity, TRUE); + LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); } - if(Uart.byteCnt==0) Dbprintf("[2] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); - /* And ready to receive another command. */ UartReset(); /* And also reset the demod code, which might have been */ @@ -1168,7 +1180,7 @@ void RAMFUNC SnoopIso14443(void) } if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if(Handle14443SamplesDemod(ci & 0xFE, cq & 0xFE)) { + if(Handle14443bSamplesDemod(ci & 0xFE, cq & 0xFE)) { //Use samples as a time measurement if(tracing) @@ -1178,31 +1190,17 @@ void RAMFUNC SnoopIso14443(void) LogTrace(Demod.output, Demod.len, samples, samples, parity, FALSE); } triggered = TRUE; - LED_A_OFF(); - LED_B_ON(); // And ready to receive another response. DemodReset(); } - TagIsActive = (Demod.state != DEMOD_UNSYNCD); + TagIsActive = (Demod.state > DEMOD_PHASE_REF_TRAINING); } - WDT_HIT(); - - if(!tracing) { - DbpString("Reached trace limit"); - break; - } - - if(BUTTON_PRESS()) { - DbpString("cancelled"); - break; - } } + FpgaDisableSscDma(); - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); + LEDsoff(); AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; DbpString("Snoop statistics:"); Dbprintf(" Max behind by: %i", maxBehindBy); @@ -1228,38 +1226,36 @@ void RAMFUNC SnoopIso14443(void) void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, uint8_t data[]) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - if(!powerfield) - { + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); + + set_tracing(TRUE); + +/* if(!powerfield) { // Make sure that we start from off, since the tags are stateful; // confusing things will happen if we don't reset them between reads. FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(200); } + */ - if(!GETBIT(GPIO_LED_D)) - { - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - - // Now give it time to spin up. - // Signal field is on with the appropriate LED - LED_D_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); - SpinDelay(200); - } + // if(!GETBIT(GPIO_LED_D)) { // if field is off + // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + // // Signal field is on with the appropriate LED + // LED_D_ON(); + // SpinDelay(200); + // } CodeAndTransmit14443bAsReader(data, datalen); - if(recv) - { - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); - uint16_t iLen = MIN(Demod.len,USB_CMD_DATA_SIZE); - cmd_send(CMD_ACK,iLen,0,0,Demod.output,iLen); + if(recv) { + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + uint16_t iLen = MIN(Demod.len, USB_CMD_DATA_SIZE); + cmd_send(CMD_ACK, iLen, 0, 0, Demod.output, iLen); } - if(!powerfield) - { + + if(!powerfield) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); } diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index 525ffcc63..21a4e1799 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -288,7 +288,7 @@ int CmdHF14BCmdRaw (const char *cmd) { if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { recv = resp.d.asBytes; PrintAndLog("received %i octets",resp.arg[0]); - if(!resp.arg[0]) + if(resp.arg[0] == 0) return 0; hexout = (char *)malloc(resp.arg[0] * 3 + 1); if (hexout != NULL) { @@ -298,11 +298,13 @@ int CmdHF14BCmdRaw (const char *cmd) { } PrintAndLog("%s", hexout); free(hexout); - ComputeCrc14443(CRC_14443_B, recv, resp.arg[0]-2, &first, &second); - if(recv[resp.arg[0]-2]==first && recv[resp.arg[0]-1]==second) { - PrintAndLog("CRC OK"); - } else { - PrintAndLog("CRC failed"); + if (resp.arg[0] > 2) { + ComputeCrc14443(CRC_14443_B, recv, resp.arg[0]-2, &first, &second); + if(recv[resp.arg[0]-2]==first && recv[resp.arg[0]-1]==second) { + PrintAndLog("CRC OK"); + } else { + PrintAndLog("CRC failed"); + } } } else { PrintAndLog("malloc failed your client has low memory?"); diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 53078a782422c09596f006d95c85ae45da20072d..717dad13eb67e5ef2cfeb39da3c4a0f6e9e04fd3 100644 GIT binary patch literal 42175 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 literal 42175 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 diff --git a/fpga/fpga_hf.v b/fpga/fpga_hf.v index a2100df65..e84081b38 100644 --- a/fpga/fpga_hf.v +++ b/fpga/fpga_hf.v @@ -67,15 +67,10 @@ assign major_mode = conf_word[7:5]; // some fraction of the buffers) wire hi_read_tx_shallow_modulation = conf_word[0]; -// For the high-frequency receive correlator: frequency against which to -// correlate. -wire hi_read_rx_xcorr_848 = conf_word[0]; -// and whether to drive the coil (reader) or just short it (snooper) +// For the high-frequency receive correlator: +// whether to drive the coil (reader) or just short it (snooper) wire hi_read_rx_xcorr_snoop = conf_word[1]; -// Divide the expected subcarrier frequency for hi_read_rx_xcorr by 4 -wire hi_read_rx_xcorr_quarter = conf_word[2]; - // For the high-frequency simulated tag: what kind of modulation to use. wire [2:0] hi_simulate_mod_type = conf_word[2:0]; @@ -102,7 +97,7 @@ hi_read_rx_xcorr hrxc( hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk, cross_hi, cross_lo, hrxc_dbg, - hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter + hi_read_rx_xcorr_snoop ); hi_simulate hs( diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index 06142637b..a6a99cd57 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -10,7 +10,7 @@ module hi_read_rx_xcorr( ssp_frame, ssp_din, ssp_dout, ssp_clk, cross_hi, cross_lo, dbg, - xcorr_is_848, snoop, xcorr_quarter_freq + snoop ); input pck0, ck_1356meg, ck_1356megb; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; @@ -20,58 +20,20 @@ module hi_read_rx_xcorr( output ssp_frame, ssp_din, ssp_clk; input cross_hi, cross_lo; output dbg; - input xcorr_is_848, snoop, xcorr_quarter_freq; + input snoop; // Carrier is steady on through this, unless we're snooping. assign pwr_hi = ck_1356megb & (~snoop); assign pwr_oe1 = 1'b0; -assign pwr_oe2 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; -reg ssp_clk; -reg ssp_frame; - -reg fc_div_2; -always @(posedge ck_1356meg) - fc_div_2 = ~fc_div_2; - -reg fc_div_4; -always @(posedge fc_div_2) - fc_div_4 = ~fc_div_4; - -reg fc_div_8; -always @(posedge fc_div_4) - fc_div_8 = ~fc_div_8; - -reg adc_clk; - -always @(xcorr_is_848 or xcorr_quarter_freq or ck_1356meg) - if(~xcorr_quarter_freq) - begin - if(xcorr_is_848) - // The subcarrier frequency is fc/16; we will sample at fc, so that - // means the subcarrier is 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 ... - adc_clk <= ck_1356meg; - else - // The subcarrier frequency is fc/32; we will sample at fc/2, and - // the subcarrier will look identical. - adc_clk <= fc_div_2; - end - else - begin - if(xcorr_is_848) - // The subcarrier frequency is fc/64 - adc_clk <= fc_div_4; - else - // The subcarrier frequency is fc/128 - adc_clk <= fc_div_8; - end +wire adc_clk = ck_1356megb; // When we're a reader, we just need to do the BPSK demod; but when we're an // eavesdropper, we also need to pick out the commands sent by the reader, // using AM. Do this the same way that we do it for the simulated tag. -reg after_hysteresis, after_hysteresis_prev; +reg after_hysteresis, after_hysteresis_prev, after_hysteresis_prev_prev; reg [11:0] has_been_low_for; always @(negedge adc_clk) begin @@ -97,7 +59,6 @@ end // Let us report a correlation every 4 subcarrier cycles, or 4*16 samples, // so we need a 6-bit counter. reg [5:0] corr_i_cnt; -reg [5:0] corr_q_cnt; // And a couple of registers in which to accumulate the correlations. // we would add at most 32 times adc_d, the result can be held in 13 bits. // Need one additional bit because it can be negative as well @@ -105,32 +66,38 @@ reg signed [13:0] corr_i_accum; reg signed [13:0] corr_q_accum; reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; +// clock and frame signal for communication to ARM +reg ssp_clk; +reg ssp_frame; + + // ADC data appears on the rising edge, so sample it on the falling edge always @(negedge adc_clk) begin + corr_i_cnt <= corr_i_cnt + 1; + // These are the correlators: we correlate against in-phase and quadrature // versions of our reference signal, and keep the (signed) result to // send out later over the SSP. - if(corr_i_cnt == 7'd63) + if(corr_i_cnt == 7'd0) begin if(snoop) begin - // highest 7 significant bits of tag signal (signed), 1 bit reader signal: - corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev}; - corr_q_out <= {corr_q_accum[13:7], after_hysteresis}; + // 7 most significant bits of tag signal (signed), 1 bit reader signal: + corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev_prev}; + corr_q_out <= {corr_q_accum[13:7], after_hysteresis_prev}; + after_hysteresis_prev_prev <= after_hysteresis; end else begin - // highest 8 significant bits of tag signal + // 8 most significant bits of tag signal corr_i_out <= corr_i_accum[13:6]; corr_q_out <= corr_q_accum[13:6]; end corr_i_accum <= adc_d; corr_q_accum <= adc_d; - corr_q_cnt <= 4; - corr_i_cnt <= 0; end else begin @@ -139,13 +106,11 @@ begin else corr_i_accum <= corr_i_accum + adc_d; - if(corr_q_cnt[3]) - corr_q_accum <= corr_q_accum - adc_d; - else + if(corr_i_cnt[3] == corr_i_cnt[2]) // phase shifted by pi/2 corr_q_accum <= corr_q_accum + adc_d; + else + corr_q_accum <= corr_q_accum - adc_d; - corr_i_cnt <= corr_i_cnt + 1; - corr_q_cnt <= corr_q_cnt + 1; end // The logic in hi_simulate.v reports 4 samples per bit. We report two @@ -172,7 +137,7 @@ begin end // set ssp_frame signal for corr_i_cnt = 0..3 and corr_i_cnt = 32..35 - // (two frames with 8 Bits each) + // (send two frames with 8 Bits each) if(corr_i_cnt[5:2] == 4'b0000 || corr_i_cnt[5:2] == 4'b1000) ssp_frame = 1'b1; else @@ -186,5 +151,6 @@ assign dbg = corr_i_cnt[3]; // Unused. assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; endmodule From d5875804a3555b9d6305a4c9dcdbfeac380d9f00 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 18 Jun 2015 07:56:08 +0200 Subject: [PATCH 07/18] fixing iso14443b (issue #103): fix: don't waste time to calculate parity bits. Instead add void parity bits to trace and ignore them on client side --- armsrc/iso14443b.c | 11 +++++------ client/cmdhf.c | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index f598df3c2..989b477ca 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -17,7 +17,6 @@ #include "iso14443crc.h" #define RECEIVE_SAMPLES_TIMEOUT 2000 -#define ISO14443B_DMA_BUFFER_SIZE 512 //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using @@ -768,7 +767,7 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; - GetParity(Demod.output, Demod.len, parity); + //GetParity(Demod.output, Demod.len, parity); LogTrace(Demod.output, Demod.len, 0, 0, parity, FALSE); } } @@ -1156,7 +1155,7 @@ void RAMFUNC SnoopIso14443b(void) if (!TagIsActive) { // no need to try decoding reader data if the tag is sending if(Handle14443bUartBit(ci & 0x01)) { if(triggered && tracing) { - GetParity(Uart.output, Uart.byteCnt, parity); + //GetParity(Uart.output, Uart.byteCnt, parity); LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); } /* And ready to receive another command. */ @@ -1167,7 +1166,7 @@ void RAMFUNC SnoopIso14443b(void) } if(Handle14443bUartBit(cq & 0x01)) { if(triggered && tracing) { - GetParity(Uart.output, Uart.byteCnt, parity); + //GetParity(Uart.output, Uart.byteCnt, parity); LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); } /* And ready to receive another command. */ @@ -1186,7 +1185,7 @@ void RAMFUNC SnoopIso14443b(void) if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; - GetParity(Demod.output, Demod.len, parity); + //GetParity(Demod.output, Demod.len, parity); LogTrace(Demod.output, Demod.len, samples, samples, parity, FALSE); } triggered = TRUE; @@ -1194,7 +1193,7 @@ void RAMFUNC SnoopIso14443b(void) // And ready to receive another response. DemodReset(); } - TagIsActive = (Demod.state > DEMOD_PHASE_REF_TRAINING); + TagIsActive = (Demod.state > DEMOD_GOT_FALLING_EDGE_OF_SOF); } } diff --git a/client/cmdhf.c b/client/cmdhf.c index 16f7bb0f3..fc6127c2d 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -378,7 +378,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01); } uint8_t parityBits = parityBytes[j>>3]; - if (isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) { + if (protocol != ISO_14443B && isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) { snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); } else { From 132a02179cfad11fbf484b191544fb3c8475eda8 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 18 Jun 2015 09:49:22 +0200 Subject: [PATCH 08/18] fixing iso 14443b (issue #103): - fix: treat empty commands as error - deleting dead code - rename USB-Commands (ISO14443 -> iso14443B) --- armsrc/appmain.c | 60 +-------------- armsrc/iso14443b.c | 70 +++++++---------- client/cmdhf14b.c | 144 ++--------------------------------- client/hid-flasher/usb_cmd.h | 6 +- client/lualibs/commands.lua | 6 +- include/usb_cmd.h | 6 +- 6 files changed, 42 insertions(+), 250 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 9bfa5ea7a..fb3c0f18c 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -250,55 +250,6 @@ void MeasureAntennaTuningHf(void) } -void SimulateTagHfListen(void) -{ - // ToDo: historically this used the free buffer, which was 2744 Bytes long. - // There might be a better size to be defined: - #define HF_14B_SNOOP_BUFFER_SIZE 2744 - uint8_t *dest = BigBuf_malloc(HF_14B_SNOOP_BUFFER_SIZE); - uint8_t v = 0; - int i; - int p = 0; - - // We're using this mode just so that I can test it out; the simulated - // tag mode would work just as well and be simpler. - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_SNOOP); - - // We need to listen to the high-frequency, peak-detected path. - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - FpgaSetupSsc(); - - i = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint8_t r = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - v <<= 1; - if(r & 1) { - v |= 1; - } - p++; - - if(p >= 8) { - dest[i] = v; - v = 0; - p = 0; - i++; - - if(i >= HF_14B_SNOOP_BUFFER_SIZE) { - break; - } - } - } - } - DbpString("simulate tag (now type bitsamples)"); -} - void ReadMem(int addr) { const uint8_t *data = ((uint8_t *)addr); @@ -782,19 +733,16 @@ void UsbPacketReceived(uint8_t *packet, int len) #endif #ifdef WITH_ISO14443b - case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443: - AcquireRawAdcSamplesIso14443b(c->arg[0]); - break; case CMD_READ_SRI512_TAG: ReadSTMemoryIso14443b(0x0F); break; case CMD_READ_SRIX4K_TAG: ReadSTMemoryIso14443b(0x7F); break; - case CMD_SNOOP_ISO_14443: + case CMD_SNOOP_ISO_14443B: SnoopIso14443b(); break; - case CMD_SIMULATE_TAG_ISO_14443: + case CMD_SIMULATE_TAG_ISO_14443B: SimulateIso14443bTag(); break; case CMD_ISO_14443B_COMMAND: @@ -911,10 +859,6 @@ void UsbPacketReceived(uint8_t *packet, int len) break; #endif - case CMD_SIMULATE_TAG_HF_LISTEN: - SimulateTagHfListen(); - break; - case CMD_BUFF_CLEAR: BigBuf_Clear(); break; diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 989b477ca..80f93678f 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -237,7 +237,11 @@ static int Handle14443bUartBit(int bit) } else if(Uart.shiftReg == 0x000) { // this is an EOF byte LED_A_OFF(); // Finished receiving - return TRUE; + if (Uart.byteCnt != 0) { + return TRUE; + } + Uart.posCnt = 0; + Uart.state = STATE_ERROR_WAIT; } else { // this is an error Uart.posCnt = 0; @@ -714,16 +718,16 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); // Setup and start DMA. - FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); + FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); int8_t *upTo = dmaBuf; - lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + lastRxCounter = DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: LED_D_ON(); @@ -734,18 +738,18 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; if(behindBy > max) max = behindBy; - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1)) > 2) { + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (DMA_BUFFER_SIZE-1)) > 2) { ci = upTo[0]; cq = upTo[1]; upTo += 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { upTo = dmaBuf; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } lastRxCounter -= 2; if(lastRxCounter <= 0) { - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + lastRxCounter += DMA_BUFFER_SIZE; } samples += 2; @@ -880,22 +884,6 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) } -//----------------------------------------------------------------------------- -// Read an ISO 14443B tag. We send it some set of commands, and record the -// responses. -// The command name is misleading, it actually decodes the reponse in HEX -// into the output buffer (read the result using hexsamples, not hisamples) -// -// obsolete function only for test -//----------------------------------------------------------------------------- -void AcquireRawAdcSamplesIso14443b(uint32_t parameter) -{ - uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; // REQB with AFI=0, Request All, N=0 - - SendRawCommand14443B(sizeof(cmd1),1,1,cmd1); -} - - /** Convenience function to encode, transmit and trace iso 14443b comms **/ @@ -956,7 +944,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) DbpString("No response from tag"); return; } else { - Dbprintf("Randomly generated UID from tag (+ 2 byte CRC): %x %x %x", + Dbprintf("Randomly generated UID from tag (+ 2 byte CRC): %02x %02x %02x", Demod.output[0], Demod.output[1], Demod.output[2]); } // There is a response, SELECT the uid @@ -981,7 +969,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) } // Check response from the tag: should be the same UID as the command we just sent: if (cmd1[1] != Demod.output[0]) { - Dbprintf("Bad response to SELECT from Tag, aborting: %x %x", cmd1[1], Demod.output[0]); + Dbprintf("Bad response to SELECT from Tag, aborting: %02x %02x", cmd1[1], Demod.output[0]); return; } // Tag is now selected, @@ -1000,7 +988,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) // The check the CRC of the answer (use cmd1 as temporary variable): ComputeCrc14443(CRC_14443_B, Demod.output, 8, &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", + Dbprintf("CRC Error reading block! Expected: %04x got: %04x", (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); // Do not return;, let's go on... (we should retry, maybe ?) } @@ -1009,7 +997,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); // Now loop to read all 16 blocks, address from 0 to last block - Dbprintf("Tag memory dump, block 0 to %d",dwLast); + Dbprintf("Tag memory dump, block 0 to %d", dwLast); cmd1[0] = 0x08; i = 0x00; dwLast++; @@ -1032,12 +1020,12 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) // The check the CRC of the answer (use cmd1 as temporary variable): ComputeCrc14443(CRC_14443_B, Demod.output, 4, &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[4] || cmd1[3] != Demod.output[5]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", + Dbprintf("CRC Error reading block! Expected: %04x got: %04x", (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); // Do not return;, let's go on... (we should retry, maybe ?) } // Now print out the memory location: - Dbprintf("Address=%x, Contents=%x, CRC=%x", i, + Dbprintf("Address=%02x, Contents=%08x, CRC=%04x", i, (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], (Demod.output[4]<<8)+Demod.output[5]); if (i == 0xff) { @@ -1062,7 +1050,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) * Memory usage for this function, (within BigBuf) * Last Received command (reader->tag) - MAX_FRAME_SIZE * Last Received command (tag->reader) - MAX_FRAME_SIZE - * DMA Buffer - ISO14443B_DMA_BUFFER_SIZE + * DMA Buffer - DMA_BUFFER_SIZE * Demodulated samples received - all the rest */ void RAMFUNC SnoopIso14443b(void) @@ -1079,7 +1067,7 @@ void RAMFUNC SnoopIso14443b(void) set_tracing(TRUE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); int lastRxCounter; int8_t *upTo; int ci, cq; @@ -1097,7 +1085,7 @@ void RAMFUNC SnoopIso14443b(void) Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); Dbprintf(" Reader -> tag: %i bytes", MAX_FRAME_SIZE); Dbprintf(" tag -> Reader: %i bytes", MAX_FRAME_SIZE); - Dbprintf(" DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); + Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE); // Signal field is off, no reader signal, no tag signal LEDsoff(); @@ -1109,8 +1097,8 @@ void RAMFUNC SnoopIso14443b(void) // Setup for the DMA. FpgaSetupSsc(); upTo = dmaBuf; - lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); + lastRxCounter = DMA_BUFFER_SIZE; + FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; bool TagIsActive = FALSE; @@ -1119,7 +1107,7 @@ void RAMFUNC SnoopIso14443b(void) // And now we loop, receiving samples. for(;;) { int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (ISO14443B_DMA_BUFFER_SIZE-1); + (DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; } @@ -1130,14 +1118,14 @@ void RAMFUNC SnoopIso14443b(void) cq = upTo[1]; upTo += 2; lastRxCounter -= 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { upTo = dmaBuf; - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + lastRxCounter += DMA_BUFFER_SIZE; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; WDT_HIT(); - if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? - Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); + if(behindBy > (9*DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? + Dbprintf("blew circular buffer! behindBy=%d", behindBy); break; } if(!tracing) { diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index 21a4e1799..496267cd4 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -25,154 +25,23 @@ static int CmdHelp(const char *Cmd); -int CmdHF14BDemod(const char *Cmd) -{ - int i, j, iold; - int isum, qsum; - int outOfWeakAt; - bool negateI, negateQ; - - uint8_t data[256]; - int dataLen = 0; - - // As received, the samples are pairs, correlations against I and Q - // square waves. So estimate angle of initial carrier (or just - // quadrant, actually), and then do the demod. - - // First, estimate where the tag starts modulating. - for (i = 0; i < GraphTraceLen; i += 2) { - if (abs(GraphBuffer[i]) + abs(GraphBuffer[i + 1]) > 40) { - break; - } - } - if (i >= GraphTraceLen) { - PrintAndLog("too weak to sync"); - return 0; - } - PrintAndLog("out of weak at %d", i); - outOfWeakAt = i; - - // Now, estimate the phase in the initial modulation of the tag - isum = 0; - qsum = 0; - for (; i < (outOfWeakAt + 16); i += 2) { - isum += GraphBuffer[i + 0]; - qsum += GraphBuffer[i + 1]; - } - negateI = (isum < 0); - negateQ = (qsum < 0); - - // Turn the correlation pairs into soft decisions on the bit. - j = 0; - for (i = 0; i < GraphTraceLen / 2; i++) { - int si = GraphBuffer[j]; - int sq = GraphBuffer[j + 1]; - if (negateI) si = -si; - if (negateQ) sq = -sq; - GraphBuffer[i] = si + sq; - j += 2; - } - GraphTraceLen = i; - - i = outOfWeakAt / 2; - while (GraphBuffer[i] > 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - - iold = i; - while (GraphBuffer[i] < 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - if ((i - iold) > 23) goto demodError; - - PrintAndLog("make it to demod loop"); - - for (;;) { - iold = i; - while (GraphBuffer[i] >= 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - if ((i - iold) > 6) goto demodError; - - uint16_t shiftReg = 0; - if (i + 20 >= GraphTraceLen) goto demodError; - - for (j = 0; j < 10; j++) { - int soft = GraphBuffer[i] + GraphBuffer[i + 1]; - - if (abs(soft) < (abs(isum) + abs(qsum)) / 20) { - PrintAndLog("weak bit"); - } - - shiftReg >>= 1; - if(GraphBuffer[i] + GraphBuffer[i+1] >= 0) { - shiftReg |= 0x200; - } - - i+= 2; - } - - if ((shiftReg & 0x200) && !(shiftReg & 0x001)) - { - // valid data byte, start and stop bits okay - PrintAndLog(" %02x", (shiftReg >> 1) & 0xff); - data[dataLen++] = (shiftReg >> 1) & 0xff; - if (dataLen >= sizeof(data)) { - return 0; - } - } else if (shiftReg == 0x000) { - // this is EOF - break; - } else { - goto demodError; - } - } - - uint8_t first, second; - ComputeCrc14443(CRC_14443_B, data, dataLen-2, &first, &second); - PrintAndLog("CRC: %02x %02x (%s)\n", first, second, - (first == data[dataLen-2] && second == data[dataLen-1]) ? - "ok" : "****FAIL****"); - - RepaintGraphWindow(); - return 0; - -demodError: - PrintAndLog("demod error"); - RepaintGraphWindow(); - return 0; -} - int CmdHF14BList(const char *Cmd) { PrintAndLog("Deprecated command, use 'hf list 14b' instead"); return 0; } -int CmdHF14BRead(const char *Cmd) -{ - UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443, {strtol(Cmd, NULL, 0), 0, 0}}; - SendCommand(&c); - return 0; -} -int CmdHF14Sim(const char *Cmd) +int CmdHF14BSim(const char *Cmd) { - UsbCommand c={CMD_SIMULATE_TAG_ISO_14443}; - SendCommand(&c); - return 0; -} - -int CmdHFSimlisten(const char *Cmd) -{ - UsbCommand c = {CMD_SIMULATE_TAG_HF_LISTEN}; + UsbCommand c={CMD_SIMULATE_TAG_ISO_14443B}; SendCommand(&c); return 0; } int CmdHF14BSnoop(const char *Cmd) { - UsbCommand c = {CMD_SNOOP_ISO_14443}; + UsbCommand c = {CMD_SNOOP_ISO_14443B}; SendCommand(&c); return 0; } @@ -387,12 +256,9 @@ int CmdHF14BWrite( const char *Cmd){ static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, - {"demod", CmdHF14BDemod, 1, "Demodulate ISO14443 Type B from tag"}, {"list", CmdHF14BList, 0, "[Deprecated] List ISO 14443b history"}, - {"read", CmdHF14BRead, 0, "Read HF tag (ISO 14443)"}, - {"sim", CmdHF14Sim, 0, "Fake ISO 14443 tag"}, - {"simlisten", CmdHFSimlisten, 0, "Get HF samples as fake tag"}, - {"snoop", CmdHF14BSnoop, 0, "Eavesdrop ISO 14443"}, + {"sim", CmdHF14BSim, 0, "Fake ISO 14443B tag"}, + {"snoop", CmdHF14BSnoop, 0, "Eavesdrop ISO 14443B"}, {"sri512read", CmdSri512Read, 0, "Read contents of a SRI512 tag"}, {"srix4kread", CmdSrix4kRead, 0, "Read contents of a SRIX4K tag"}, {"raw", CmdHF14BCmdRaw, 0, "Send raw hex data to tag"}, diff --git a/client/hid-flasher/usb_cmd.h b/client/hid-flasher/usb_cmd.h index c5b91f997..b3a7f4ec9 100644 --- a/client/hid-flasher/usb_cmd.h +++ b/client/hid-flasher/usb_cmd.h @@ -89,7 +89,6 @@ typedef struct { // For the 13.56 MHz tags #define CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693 0x0300 -#define CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443 0x0301 #define CMD_READ_SRI512_TAG 0x0303 #define CMD_READ_SRIX4K_TAG 0x0304 #define CMD_READER_ISO_15693 0x0310 @@ -105,9 +104,8 @@ typedef struct { #define CMD_SIMULATE_HITAG 0x0371 #define CMD_READER_HITAG 0x0372 -#define CMD_SIMULATE_TAG_HF_LISTEN 0x0380 -#define CMD_SIMULATE_TAG_ISO_14443 0x0381 -#define CMD_SNOOP_ISO_14443 0x0382 +#define CMD_SIMULATE_TAG_ISO_14443B 0x0381 +#define CMD_SNOOP_ISO_14443B 0x0382 #define CMD_SNOOP_ISO_14443a 0x0383 #define CMD_SIMULATE_TAG_ISO_14443a 0x0384 #define CMD_READER_ISO_14443a 0x0385 diff --git a/client/lualibs/commands.lua b/client/lualibs/commands.lua index 678c745ec..4c7bc6383 100644 --- a/client/lualibs/commands.lua +++ b/client/lualibs/commands.lua @@ -59,7 +59,6 @@ local _commands = { --// For the 13.56 MHz tags CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693 = 0x0300, - CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443 = 0x0301, CMD_READ_SRI512_TAG = 0x0303, CMD_READ_SRIX4K_TAG = 0x0304, CMD_READER_ISO_15693 = 0x0310, @@ -76,9 +75,8 @@ local _commands = { CMD_SIMULATE_HITAG = 0x0371, CMD_READER_HITAG = 0x0372, - CMD_SIMULATE_TAG_HF_LISTEN = 0x0380, - CMD_SIMULATE_TAG_ISO_14443 = 0x0381, - CMD_SNOOP_ISO_14443 = 0x0382, + CMD_SIMULATE_TAG_ISO_14443B = 0x0381, + CMD_SNOOP_ISO_14443B = 0x0382, CMD_SNOOP_ISO_14443a = 0x0383, CMD_SIMULATE_TAG_ISO_14443a = 0x0384, CMD_READER_ISO_14443a = 0x0385, diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 357395d43..132e1805d 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -100,7 +100,6 @@ typedef struct{ // For the 13.56 MHz tags #define CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693 0x0300 -#define CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443 0x0301 #define CMD_READ_SRI512_TAG 0x0303 #define CMD_READ_SRIX4K_TAG 0x0304 #define CMD_ISO_14443B_COMMAND 0x0305 @@ -118,9 +117,8 @@ typedef struct{ #define CMD_SIMULATE_HITAG 0x0371 #define CMD_READER_HITAG 0x0372 -#define CMD_SIMULATE_TAG_HF_LISTEN 0x0380 -#define CMD_SIMULATE_TAG_ISO_14443 0x0381 -#define CMD_SNOOP_ISO_14443 0x0382 +#define CMD_SIMULATE_TAG_ISO_14443B 0x0381 +#define CMD_SNOOP_ISO_14443B 0x0382 #define CMD_SNOOP_ISO_14443a 0x0383 #define CMD_SIMULATE_TAG_ISO_14443a 0x0384 #define CMD_READER_ISO_14443a 0x0385 From 50365fedcbaf91ce530ca8507d2f377dc76ffd59 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 18 Jun 2015 11:33:53 +0200 Subject: [PATCH 09/18] fix issue #116: remove includes of stdio.h in armsrc --- armsrc/aes.c | 4 +++- armsrc/optimized_cipher.c | 1 - common/protocols.c | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/armsrc/aes.c b/armsrc/aes.c index 3df006bb3..a199d04b7 100644 --- a/armsrc/aes.c +++ b/armsrc/aes.c @@ -1,4 +1,3 @@ -#include "stdio.h" #include "aes.h" static const unsigned int Te0[256] = { @@ -1138,6 +1137,9 @@ int AesDecrypt(AesCtx *pCtx, unsigned char *pCipher, unsigned char *pData, unsig ////////////////////////////////////////////////////////////////////////////// #ifndef EMBEDDED + +#include + int main() { AesCtx ctx; diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 444b93d09..235626211 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -61,7 +61,6 @@ **/ #include "optimized_cipher.h" -#include #include #include #include diff --git a/common/protocols.c b/common/protocols.c index 6a4c9a103..aa80491b5 100644 --- a/common/protocols.c +++ b/common/protocols.c @@ -1,4 +1,3 @@ -#include #include #include #include From da586b170276fd44963ec4a8934beaf563feb132 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 18 Jun 2015 15:30:56 +0200 Subject: [PATCH 10/18] fixing iso14443b (issue #103): - revert removal of FPGA_HF_READER_RX_XCORR_848_KHZ. Need to be able to switch to 424kHz for ISO15693. --- armsrc/iso14443b.c | 8 ++++---- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/fpga_hf.v | 8 +++++--- fpga/hi_read_rx_xcorr.v | 20 +++++++++++++++++--- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 80f93678f..8d1a5cca4 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -732,7 +732,7 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) // Signal field is ON with the appropriate LED: LED_D_ON(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); for(;;) { int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; @@ -929,7 +929,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) // Signal field is on with the appropriate LED LED_D_ON(); FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); SpinDelay(200); // First command: wake up the tag using the INITIATE command @@ -1091,7 +1091,7 @@ void RAMFUNC SnoopIso14443b(void) LEDsoff(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. @@ -1228,7 +1228,7 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, u */ // if(!GETBIT(GPIO_LED_D)) { // if field is off - // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); // // Signal field is on with the appropriate LED // LED_D_ON(); // SpinDelay(200); diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 717dad13eb67e5ef2cfeb39da3c4a0f6e9e04fd3..49bec2242cb7720524289f3eba1397bbc2a22867 100644 GIT binary patch literal 42175 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 diff --git a/fpga/fpga_hf.v b/fpga/fpga_hf.v index e84081b38..8a465e75c 100644 --- a/fpga/fpga_hf.v +++ b/fpga/fpga_hf.v @@ -67,8 +67,10 @@ assign major_mode = conf_word[7:5]; // some fraction of the buffers) wire hi_read_tx_shallow_modulation = conf_word[0]; -// For the high-frequency receive correlator: -// whether to drive the coil (reader) or just short it (snooper) +// For the high-frequency receive correlator: frequency against which to +// correlate. +wire hi_read_rx_xcorr_848 = conf_word[0]; +// and whether to drive the coil (reader) or just short it (snooper) wire hi_read_rx_xcorr_snoop = conf_word[1]; // For the high-frequency simulated tag: what kind of modulation to use. @@ -97,7 +99,7 @@ hi_read_rx_xcorr hrxc( hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk, cross_hi, cross_lo, hrxc_dbg, - hi_read_rx_xcorr_snoop + hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop ); hi_simulate hs( diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index a6a99cd57..4a5de5536 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -10,7 +10,7 @@ module hi_read_rx_xcorr( ssp_frame, ssp_din, ssp_dout, ssp_clk, cross_hi, cross_lo, dbg, - snoop + xcorr_is_848, snoop ); input pck0, ck_1356meg, ck_1356megb; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; @@ -20,7 +20,7 @@ module hi_read_rx_xcorr( output ssp_frame, ssp_din, ssp_clk; input cross_hi, cross_lo; output dbg; - input snoop; + input xcorr_is_848, snoop; // Carrier is steady on through this, unless we're snooping. assign pwr_hi = ck_1356megb & (~snoop); @@ -28,8 +28,22 @@ assign pwr_oe1 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; -wire adc_clk = ck_1356megb; +(* clock_signal = "yes" *) reg fc_div_2; +always @(negedge ck_1356megb) + fc_div_2 <= fc_div_2 + 1; +(* clock_signal = "yes" *) reg adc_clk; +always @(xcorr_is_848, ck_1356megb, fc_div_2) +if (xcorr_is_848) + // The subcarrier frequency is fc/16; we will sample at fc, so that + // means the subcarrier is 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 ... + adc_clk <= ck_1356megb; +else + // The subcarrier frequency is fc/32; we will sample at fc/2, and + // the subcarrier will look identical. + adc_clk <= fc_div_2; + + // When we're a reader, we just need to do the BPSK demod; but when we're an // eavesdropper, we also need to pick out the commands sent by the reader, // using AM. Do this the same way that we do it for the simulated tag. From f4217d58b68ef3e7828892d8401ea1eb3da4696d Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Thu, 18 Jun 2015 17:19:43 -0400 Subject: [PATCH 11/18] add ultralight compatible test --- client/cmdhfmfu.c | 33 +++++++++++++++++++++++++++++---- client/cmdhfmfu.h | 3 ++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 48f549ba8..a631b6b1f 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -57,13 +57,13 @@ uint8_t default_pwd_pack[KEYS_PWD_COUNT][4] = { {0x32,0x0C,0x16,0x17}, // PACK 0x80,0x80 -- AMiiboo (sniffed) }; -#define MAX_UL_TYPES 17 -uint16_t UL_TYPES_ARRAY[MAX_UL_TYPES] = {UNKNOWN, UL, UL_C, UL_EV1_48, UL_EV1_128, NTAG, NTAG_203, - NTAG_210, NTAG_212, NTAG_213, NTAG_215, NTAG_216, MY_D, MY_D_NFC, MY_D_MOVE, MY_D_MOVE_NFC, MY_D_MOVE_LEAN}; +#define MAX_UL_TYPES 18 +uint32_t UL_TYPES_ARRAY[MAX_UL_TYPES] = {UNKNOWN, UL, UL_C, UL_EV1_48, UL_EV1_128, NTAG, NTAG_203, + NTAG_210, NTAG_212, NTAG_213, NTAG_215, NTAG_216, MY_D, MY_D_NFC, MY_D_MOVE, MY_D_MOVE_NFC, MY_D_MOVE_LEAN, FUDAN_UL}; uint8_t UL_MEMORY_ARRAY[MAX_UL_TYPES] = {MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_ULEV1a_BLOCKS, MAX_ULEV1b_BLOCKS, MAX_NTAG_203, MAX_NTAG_203, MAX_NTAG_210, MAX_NTAG_212, MAX_NTAG_213, - MAX_NTAG_215, MAX_NTAG_216, MAX_UL_BLOCKS, MAX_MY_D_NFC, MAX_MY_D_MOVE, MAX_MY_D_MOVE, MAX_MY_D_MOVE_LEAN}; + MAX_NTAG_215, MAX_NTAG_216, MAX_UL_BLOCKS, MAX_MY_D_NFC, MAX_MY_D_MOVE, MAX_MY_D_MOVE, MAX_MY_D_MOVE_LEAN, MAX_UL_BLOCKS}; static int CmdHelp(const char *Cmd); @@ -276,6 +276,25 @@ static int ulev1_readSignature( uint8_t *response, uint16_t responseLength ){ return len; } +//make sure field is off before calling this function +static int ul_fudan_check( void ){ + iso14a_card_select_t card; + if ( !ul_select(&card) ) + return UL_ERROR; + + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_NO_DISCONNECT, 4, 0}}; + + uint8_t cmd[4] = {0x30,0x00,0x02,0xa7}; //wrong crc on purpose should be 0xa8 + memcpy(c.d.asBytes, cmd, 4); + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return UL_ERROR; + if (resp.arg[0] != 1) return UL_ERROR; + + return (!resp.d.asBytes[0]) ? FUDAN_UL : UL; //if response == 0x00 then Fudan, else Genuine NXP +} + static int ul_print_default( uint8_t *data){ uint8_t uid[7]; @@ -389,6 +408,8 @@ int ul_print_type(uint32_t tagtype, uint8_t spaces){ PrintAndLog("%sTYPE : INFINEON my-d\x99 move NFC (SLE 66R01P)", spacer); else if ( tagtype & MY_D_MOVE_LEAN ) PrintAndLog("%sTYPE : INFINEON my-d\x99 move lean (SLE 66R01L)", spacer); + else if ( tagtype & FUDAN_UL ) + PrintAndLog("%sTYPE : FUDAN Ultralight Compatible (or other compatible) %s", spacer, (tagtype & MAGIC) ? "" : "" ); else PrintAndLog("%sTYPE : Unknown %06x", spacer, tagtype); return 0; @@ -622,6 +643,10 @@ uint32_t GetHF14AMfU_Type(void){ ul_switch_off_field(); } } + if (tagtype & UL) { + tagtype = ul_fudan_check(); + ul_switch_off_field(); + } } else { ul_switch_off_field(); // Infinition MY-D tests Exam high nibble diff --git a/client/cmdhfmfu.h b/client/cmdhfmfu.h index 132e4f908..6c9e3ea1b 100644 --- a/client/cmdhfmfu.h +++ b/client/cmdhfmfu.h @@ -45,7 +45,8 @@ typedef enum TAGTYPE_UL { MY_D_MOVE_LEAN= 0x008000, NTAG_I2C_1K = 0x010000, NTAG_I2C_2K = 0x020000, - MAGIC = 0x040000, + FUDAN_UL = 0x040000, + MAGIC = 0x080000, UL_MAGIC = UL | MAGIC, UL_C_MAGIC = UL_C | MAGIC, UL_ERROR = 0xFFFFFF, From 7c8b5e68110a08a78b6fc5d49175108d9e751345 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Sat, 20 Jun 2015 21:33:42 -0400 Subject: [PATCH 12/18] comment fudan check possibilities --- client/cmdhfmfu.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index a631b6b1f..3dfee3a6a 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -276,7 +276,20 @@ static int ulev1_readSignature( uint8_t *response, uint16_t responseLength ){ return len; } -//make sure field is off before calling this function + +// Fudan check checks for which error is given for a command with incorrect crc +// NXP UL chip responds with 01, fudan 00. +// other possible checks: +// send a0 + crc +// UL responds with 00, fudan doesn't respond +// or +// send a200 + crc +// UL doesn't respond, fudan responds with 00 +// or +// send 300000 + crc (read with extra byte(s)) +// UL responds with read of page 0, fudan doesn't respond. +// +// make sure field is off before calling this function static int ul_fudan_check( void ){ iso14a_card_select_t card; if ( !ul_select(&card) ) From 467340996e7af6563070df2c96c374e6a97b1456 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 21 Jun 2015 18:00:42 +0200 Subject: [PATCH 13/18] fixing iso14443b (issue #103): - fix hf 14b snoop - fix hf 14b sim --- armsrc/iso14443b.c | 161 ++++++++++++++++++++-------------------- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 26 +++---- 3 files changed, 90 insertions(+), 97 deletions(-) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 8d1a5cca4..1ae1692b9 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -122,8 +122,7 @@ static struct { STATE_UNSYNCD, STATE_GOT_FALLING_EDGE_OF_SOF, STATE_AWAITING_START_BIT, - STATE_RECEIVING_DATA, - STATE_ERROR_WAIT + STATE_RECEIVING_DATA } state; uint16_t shiftReg; int bitCnt; @@ -145,7 +144,7 @@ static struct { * Returns: true if we received a EOF * false if we are still waiting for some more */ -static int Handle14443bUartBit(int bit) +static RAMFUNC int Handle14443bUartBit(uint8_t bit) { switch(Uart.state) { case STATE_UNSYNCD: @@ -172,7 +171,7 @@ static int Handle14443bUartBit(int bit) } else { // didn't stay down long enough // before going high, error - Uart.state = STATE_ERROR_WAIT; + Uart.state = STATE_UNSYNCD; } } else { // do nothing, keep waiting @@ -183,7 +182,8 @@ static int Handle14443bUartBit(int bit) if(Uart.bitCnt > 12) { // Give up if we see too many zeros without // a one, too. - Uart.state = STATE_ERROR_WAIT; + LED_A_OFF(); + Uart.state = STATE_UNSYNCD; } break; @@ -193,7 +193,7 @@ static int Handle14443bUartBit(int bit) if(Uart.posCnt > 50/2) { // max 57us between characters = 49 1/fs, max 3 etus after low phase of SOF = 24 1/fs // stayed high for too long between // characters, error - Uart.state = STATE_ERROR_WAIT; + Uart.state = STATE_UNSYNCD; } } else { // falling edge, this starts the data byte @@ -227,41 +227,30 @@ static int Handle14443bUartBit(int bit) if(Uart.byteCnt >= Uart.byteCntMax) { // Buffer overflowed, give up - Uart.posCnt = 0; - Uart.state = STATE_ERROR_WAIT; + LED_A_OFF(); + Uart.state = STATE_UNSYNCD; } else { // so get the next byte now Uart.posCnt = 0; Uart.state = STATE_AWAITING_START_BIT; } - } else if(Uart.shiftReg == 0x000) { + } else if (Uart.shiftReg == 0x000) { // this is an EOF byte LED_A_OFF(); // Finished receiving + Uart.state = STATE_UNSYNCD; if (Uart.byteCnt != 0) { return TRUE; } - Uart.posCnt = 0; - Uart.state = STATE_ERROR_WAIT; } else { // this is an error - Uart.posCnt = 0; - Uart.state = STATE_ERROR_WAIT; + LED_A_OFF(); + Uart.state = STATE_UNSYNCD; } } break; - case STATE_ERROR_WAIT: - // We're all screwed up, so wait a little while - // for whatever went wrong to finish, and then - // start over. - Uart.posCnt++; - if(Uart.posCnt > 10) { - Uart.state = STATE_UNSYNCD; - LED_A_OFF(); - } - break; - default: + LED_A_OFF(); Uart.state = STATE_UNSYNCD; break; } @@ -269,6 +258,23 @@ static int Handle14443bUartBit(int bit) return FALSE; } + +static void UartReset() +{ + Uart.byteCntMax = MAX_FRAME_SIZE; + Uart.state = STATE_UNSYNCD; + Uart.byteCnt = 0; + Uart.bitCnt = 0; +} + + +static void UartInit(uint8_t *data) +{ + Uart.output = data; + UartReset(); +} + + //----------------------------------------------------------------------------- // Receive a command (from the reader to us, where we are the simulated tag), // and store it in the given buffer, up to the given maximum length. Keeps @@ -278,44 +284,34 @@ static int Handle14443bUartBit(int bit) // Assume that we're called with the SSC (to the FPGA) and ADC path set // correctly. //----------------------------------------------------------------------------- -static int GetIso14443bCommandFromReader(uint8_t *received, int *len, int maxLen) +static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { - uint8_t mask; - int i, bit; - // Set FPGA mode to "simulated ISO 14443B tag", no modulation (listen // only, since we are receiving, not transmitting). // Signal field is off with the appropriate LED LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); - // Now run a `software UART' on the stream of incoming samples. - Uart.output = received; - Uart.byteCntMax = maxLen; - Uart.state = STATE_UNSYNCD; + UartInit(received); for(;;) { WDT_HIT(); if(BUTTON_PRESS()) return FALSE; - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; - } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - mask = 0x80; - for(i = 0; i < 8; i++, mask >>= 1) { - bit = (b & mask); - if(Handle14443bUartBit(bit)) { + for(uint8_t mask = 0x80; mask != 0x00; mask >>= 1) { + if(Handle14443bUartBit(b & mask)) { *len = Uart.byteCnt; return TRUE; } } } } + + return FALSE; } //----------------------------------------------------------------------------- @@ -324,9 +320,12 @@ static int GetIso14443bCommandFromReader(uint8_t *received, int *len, int maxLen //----------------------------------------------------------------------------- void SimulateIso14443bTag(void) { - // the only command we understand is REQB, AFI=0, Select All, N=0: + // the only commands we understand is REQB, AFI=0, Select All, N=0: static const uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; - // ... and we respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, + // ... and REQB, AFI=0, Normal Request, N=0: + static const uint8_t cmd2[] = { 0x05, 0x00, 0x00, 0x71, 0xFF }; + + // ... and we always respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, // supports only 106kBit/s in both directions, max frame size = 32Bytes, // supports ISO14443-4, FWI=8 (77ms), NAD supported, CID not supported: static const uint8_t response1[] = { @@ -334,25 +333,27 @@ void SimulateIso14443bTag(void) 0x00, 0x21, 0x85, 0x5e, 0xd7 }; - uint8_t *resp; - int respLen; + clear_trace(); + set_tracing(TRUE); + + const uint8_t *resp; + uint8_t *respCode; + uint16_t respLen, respCodeLen; // allocate command receive buffer BigBuf_free(); uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - int len; - int i; - - int cmdsRecvd = 0; + uint16_t len; + uint16_t cmdsRecvd = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // prepare the (only one) tag answer: CodeIso14443bAsTag(response1, sizeof(response1)); - uint8_t *resp1 = BigBuf_malloc(ToSendMax); - memcpy(resp1, ToSend, ToSendMax); - uint16_t resp1Len = ToSendMax; + uint8_t *resp1Code = BigBuf_malloc(ToSendMax); + memcpy(resp1Code, ToSend, ToSendMax); + uint16_t resp1CodeLen = ToSendMax; // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -361,20 +362,28 @@ void SimulateIso14443bTag(void) cmdsRecvd = 0; for(;;) { - uint8_t b1, b2; - if(!GetIso14443bCommandFromReader(receivedCmd, &len, 100)) { + if(!GetIso14443bCommandFromReader(receivedCmd, &len)) { Dbprintf("button pressed, received %d commands", cmdsRecvd); break; - } + } + + if (tracing) { + uint8_t parity[MAX_PARITY_SIZE]; + LogTrace(receivedCmd, len, 0, 0, parity, TRUE); + } // Good, look at the command now. - - if(len == sizeof(cmd1) && memcmp(receivedCmd, cmd1, len) == 0) { - resp = resp1; respLen = resp1Len; + if ( (len == sizeof(cmd1) && memcmp(receivedCmd, cmd1, len) == 0) + || (len == sizeof(cmd2) && memcmp(receivedCmd, cmd2, len) == 0) ) { + resp = response1; + respLen = sizeof(response1); + respCode = resp1Code; + respCodeLen = resp1CodeLen; } else { Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsRecvd); // And print whether the CRC fails, just for good measure + uint8_t b1, b2; ComputeCrc14443(CRC_14443_B, receivedCmd, len-2, &b1, &b2); if(b1 != receivedCmd[len-2] || b2 != receivedCmd[len-1]) { // Not so good, try again. @@ -392,7 +401,7 @@ void SimulateIso14443bTag(void) break; } - if(respLen <= 0) continue; + if(respCodeLen <= 0) continue; // Modulate BPSK // Signal field is off with the appropriate LED @@ -402,15 +411,15 @@ void SimulateIso14443bTag(void) FpgaSetupSsc(); // Transmit the response. - i = 0; + uint16_t i = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - uint8_t b = resp[i]; + uint8_t b = respCode[i]; AT91C_BASE_SSC->SSC_THR = b; i++; - if(i > respLen) { + if(i > respCodeLen) { break; } } @@ -419,6 +428,13 @@ void SimulateIso14443bTag(void) (void)b; } } + + // trace the response: + if (tracing) { + uint8_t parity[MAX_PARITY_SIZE]; + LogTrace(resp, respLen, 0, 0, parity, FALSE); + } + } } @@ -436,8 +452,7 @@ static struct { DEMOD_AWAITING_FALLING_EDGE_OF_SOF, DEMOD_GOT_FALLING_EDGE_OF_SOF, DEMOD_AWAITING_START_BIT, - DEMOD_RECEIVING_DATA, - DEMOD_ERROR_WAIT + DEMOD_RECEIVING_DATA } state; int bitCount; int posCount; @@ -684,22 +699,6 @@ static void DemodInit(uint8_t *data) } -static void UartReset() -{ - Uart.byteCntMax = MAX_FRAME_SIZE; - Uart.state = STATE_UNSYNCD; - Uart.byteCnt = 0; - Uart.bitCnt = 0; -} - - -static void UartInit(uint8_t *data) -{ - Uart.output = data; - UartReset(); -} - - /* * Demodulate the samples we received from the tag, also log to tracebuffer * quiet: set to 'TRUE' to disable debug output @@ -1163,11 +1162,11 @@ void RAMFUNC SnoopIso14443b(void) /* false-triggered by the commands from the reader. */ DemodReset(); } - ReaderIsActive = (Uart.state != STATE_UNSYNCD); + ReaderIsActive = (Uart.state > STATE_GOT_FALLING_EDGE_OF_SOF); } if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if(Handle14443bSamplesDemod(ci & 0xFE, cq & 0xFE)) { + if(Handle14443bSamplesDemod(ci | 0x01, cq | 0x01)) { //Use samples as a time measurement if(tracing) diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 49bec2242cb7720524289f3eba1397bbc2a22867..a4d72e373bb59cfa56cc3869b1ccb3c18908250a 100644 GIT binary patch literal 42175 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 z Date: Mon, 22 Jun 2015 14:20:13 +0200 Subject: [PATCH 14/18] Add PACE replay functionality This function allows the user to specify APDUs which are sent to a card supporting the PACE protocol. The response times are measured and printed. The code was pulled from the old Google Code repository (branch "epa") and modified to fit into the new code base. --- armsrc/appmain.c | 3 + armsrc/apps.h | 1 + armsrc/epa.c | 145 +++++++++++++++++++++++++++++++++++----------- armsrc/epa.h | 4 +- client/cmdhfepa.c | 122 ++++++++++++++++++++++++++++++++++++-- include/usb_cmd.h | 1 + 6 files changed, 234 insertions(+), 42 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index c226c7263..bd1075c14 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -816,6 +816,9 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_EPA_PACE_COLLECT_NONCE: EPA_PACE_Collect_Nonce(c); break; + case CMD_EPA_PACE_REPLAY: + EPA_PACE_Replay(c); + break; case CMD_READER_MIFARE: ReaderMifare(c->arg[0]); diff --git a/armsrc/apps.h b/armsrc/apps.h index 6360b664b..715e70026 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -160,6 +160,7 @@ void RAMFUNC SniffMifare(uint8_t param); /// epa.h void EPA_PACE_Collect_Nonce(UsbCommand * c); +void EPA_PACE_Replay(UsbCommand *c); // mifarecmd.h void ReaderMifare(bool first_try); diff --git a/armsrc/epa.c b/armsrc/epa.c index 0006d59d0..6bd8692ec 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -5,7 +5,7 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Routines to support the German eletronic "Personalausweis" (ID card) +// Routines to support the German electronic "Personalausweis" (ID card) // Note that the functions which do not implement USB commands do NOT initialize // the card (with iso14443a_select_card etc.). If You want to use these // functions, You need to do the setup before calling them! @@ -74,6 +74,32 @@ static const uint8_t oid_pace_start[] = { 0x04 // id-PACE }; +// APDUs for replaying: +// MSE: Set AT (initiate PACE) +static uint8_t apdu_replay_mse_set_at_pace[41]; +// General Authenticate (Get Nonce) +static uint8_t apdu_replay_general_authenticate_pace_get_nonce[8]; +// General Authenticate (Map Nonce) +static uint8_t apdu_replay_general_authenticate_pace_map_nonce[75]; +// General Authenticate (Mutual Authenticate) +static uint8_t apdu_replay_general_authenticate_pace_mutual_authenticate[75]; +// General Authenticate (Perform Key Agreement) +static uint8_t apdu_replay_general_authenticate_pace_perform_key_agreement[18]; +// pointers to the APDUs (for iterations) +static struct { + uint8_t len; + uint8_t *data; +} const apdus_replay[] = { + {sizeof(apdu_replay_mse_set_at_pace), apdu_replay_mse_set_at_pace}, + {sizeof(apdu_replay_general_authenticate_pace_get_nonce), apdu_replay_general_authenticate_pace_get_nonce}, + {sizeof(apdu_replay_general_authenticate_pace_map_nonce), apdu_replay_general_authenticate_pace_map_nonce}, + {sizeof(apdu_replay_general_authenticate_pace_mutual_authenticate), apdu_replay_general_authenticate_pace_mutual_authenticate}, + {sizeof(apdu_replay_general_authenticate_pace_perform_key_agreement), apdu_replay_general_authenticate_pace_perform_key_agreement} +}; + +// lengths of the replay APDUs +static uint8_t apdu_lengths_replay[5]; + //----------------------------------------------------------------------------- // Closes the communication channel and turns off the field //----------------------------------------------------------------------------- @@ -101,7 +127,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data, pace_version_info_t *pace_info) { size_t index = 0; - + while (index <= length - 2) { // determine type of element // SET or SEQUENCE @@ -158,7 +184,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data, index += 2 + data[index + 1]; } } - + // TODO: We should check whether we reached the end in error, but for that // we need a better parser (e.g. with states like IN_SET or IN_PACE_INFO) return 0; @@ -176,7 +202,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) // we reserve 262 bytes here just to be safe (256-byte APDU + SW + ISO frame) uint8_t response_apdu[262]; int rapdu_length = 0; - + // select the file EF.CardAccess rapdu_length = iso14_apdu((uint8_t *)apdu_select_binary_cardaccess, sizeof(apdu_select_binary_cardaccess), @@ -188,7 +214,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) Dbprintf("epa - no select cardaccess"); return -1; } - + // read the file rapdu_length = iso14_apdu((uint8_t *)apdu_read_binary, sizeof(apdu_read_binary), @@ -200,7 +226,7 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) Dbprintf("epa - no read cardaccess"); return -1; } - + // copy the content into the buffer // length of data available: apdu_length - 4 (ISO frame) - 2 (SW) size_t to_copy = rapdu_length - 6; @@ -215,16 +241,11 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) //----------------------------------------------------------------------------- static void EPA_PACE_Collect_Nonce_Abort(uint8_t step, int func_return) { -// // step in which the failure occured -// ack->arg[0] = step; -// // last return code -// ack->arg[1] = func_return; - // power down the field EPA_Finish(); - + // send the USB packet - cmd_send(CMD_ACK,step,func_return,0,0,0); + cmd_send(CMD_ACK,step,func_return,0,0,0); } //----------------------------------------------------------------------------- @@ -246,10 +267,6 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) // return value of a function int func_return = 0; -// // initialize ack with 0s -// memset(ack->arg, 0, 12); -// memset(ack->d.asBytes, 0, 48); - // set up communication func_return = EPA_Setup(); if (func_return != 0) { @@ -277,11 +294,11 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) EPA_PACE_Collect_Nonce_Abort(3, func_return); return; } - + // initiate the PACE protocol // use the CAN for the password since that doesn't change func_return = EPA_PACE_MSE_Set_AT(pace_version_info, 2); - + // now get the nonce uint8_t nonce[256] = {0}; uint8_t requested_size = (uint8_t)c->arg[0]; @@ -292,14 +309,12 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) EPA_PACE_Collect_Nonce_Abort(4, func_return); return; } - - // all done, return + + // all done, return EPA_Finish(); - + // save received information -// ack->arg[1] = func_return; -// memcpy(ack->d.asBytes, nonce, func_return); - cmd_send(CMD_ACK,0,func_return,0,nonce,func_return); + cmd_send(CMD_ACK,0,func_return,0,nonce,func_return); } //----------------------------------------------------------------------------- @@ -320,7 +335,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) sizeof(apdu_general_authenticate_pace_get_nonce)); // append Le (requested length + 2 due to tag/length taking 2 bytes) in RAPDU apdu[sizeof(apdu_general_authenticate_pace_get_nonce)] = requested_length + 4; - + // send it uint8_t response_apdu[262]; int send_return = iso14_apdu(apdu, @@ -333,7 +348,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { return -1; } - + // if there is no nonce in the RAPDU, return here if (send_return < 10) { @@ -348,7 +363,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) } // copy the nonce memcpy(nonce, response_apdu + 6, nonce_length); - + return nonce_length; } @@ -407,13 +422,79 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) return 0; } +//----------------------------------------------------------------------------- +// Perform the PACE protocol by replaying given APDUs +//----------------------------------------------------------------------------- +void EPA_PACE_Replay(UsbCommand *c) +{ + uint32_t timings[sizeof(apdu_lengths_replay) / sizeof(apdu_lengths_replay[0])] = {0}; + + // if an APDU has been passed, save it + if (c->arg[0] != 0) { + // make sure it's not too big + if(c->arg[2] > apdus_replay[c->arg[0] - 1].len) + { + cmd_send(CMD_ACK, 1, 0, 0, NULL, 0); + } + memcpy(apdus_replay[c->arg[0] - 1].data + c->arg[1], + c->d.asBytes, + c->arg[2]); + // save/update APDU length + if (c->arg[1] == 0) { + apdu_lengths_replay[c->arg[0] - 1] = c->arg[2]; + } else { + apdu_lengths_replay[c->arg[0] - 1] += c->arg[2]; + } + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); + return; + } + + // return value of a function + int func_return; + + // set up communication + func_return = EPA_Setup(); + if (func_return != 0) { + EPA_Finish(); + cmd_send(CMD_ACK, 2, func_return, 0, NULL, 0); + return; + } + + // increase the timeout (at least some cards really do need this!)///////////// + // iso14a_set_timeout(0x0003FFFF); + + // response APDU + uint8_t response_apdu[300] = {0}; + + // now replay the data and measure the timings + for (int i = 0; i < sizeof(apdu_lengths_replay); i++) { + StartCountUS(); + func_return = iso14_apdu(apdus_replay[i].data, + apdu_lengths_replay[i], + response_apdu); + timings[i] = GetCountUS(); + // every step but the last one should succeed + if (i < sizeof(apdu_lengths_replay) - 1 + && (func_return < 6 + || response_apdu[func_return - 4] != 0x90 + || response_apdu[func_return - 3] != 0x00)) + { + EPA_Finish(); + cmd_send(CMD_ACK, 3 + i, func_return, 0, timings, 20); + return; + } + } + EPA_Finish(); + cmd_send(CMD_ACK,0,0,0,timings,20); + return; +} + //----------------------------------------------------------------------------- // Set up a communication channel (Card Select, PPS) // Returns 0 on success or a non-zero error code on failure //----------------------------------------------------------------------------- int EPA_Setup() { - int return_code = 0; uint8_t uid[10]; uint8_t pps_response[3]; @@ -422,20 +503,16 @@ int EPA_Setup() // power up the field iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); - // select the card return_code = iso14443a_select_card(uid, &card_select_info, NULL); if (return_code != 1) { - Dbprintf("Epa: Can't select card"); return 1; } - // send the PPS request ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL); return_code = ReaderReceive(pps_response, pps_response_par); if (return_code != 3 || pps_response[0] != 0xD0) { return return_code == 0 ? 2 : return_code; } - return 0; -} \ No newline at end of file +} diff --git a/armsrc/epa.h b/armsrc/epa.h index 730652b79..0c580205d 100644 --- a/armsrc/epa.h +++ b/armsrc/epa.h @@ -19,7 +19,7 @@ typedef struct { uint8_t parameter_id; } pace_version_info_t; -// note: EPA_PACE_GetNonce is declared in apps.h +// note: EPA_PACE_Collect_Nonce is declared in apps.h // general functions void EPA_Finish(); @@ -33,4 +33,4 @@ int EPA_Setup(); int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password); int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce); -#endif /* __EPA_H */ \ No newline at end of file +#endif /* __EPA_H */ diff --git a/client/cmdhfepa.c b/client/cmdhfepa.c index 3286ceb9c..e9c63f20b 100644 --- a/client/cmdhfepa.c +++ b/client/cmdhfepa.c @@ -9,7 +9,7 @@ //----------------------------------------------------------------------------- #include "util.h" -//#include "proxusb.h" + #include "proxmark3.h" #include "ui.h" #include "cmdparser.h" @@ -29,9 +29,9 @@ int CmdHFEPACollectPACENonces(const char *Cmd) unsigned int n = 0; // delay between requests unsigned int d = 0; - + sscanf(Cmd, "%u %u %u", &m, &n, &d); - + // values are expected to be > 0 m = m > 0 ? m : 1; n = n > 0 ? n : 1; @@ -44,7 +44,7 @@ int CmdHFEPACollectPACENonces(const char *Cmd) UsbCommand c = {CMD_EPA_PACE_COLLECT_NONCE, {(int)m, 0, 0}}; SendCommand(&c); UsbCommand resp; - + WaitForResponse(CMD_ACK,&resp); // check if command failed @@ -68,13 +68,123 @@ int CmdHFEPACollectPACENonces(const char *Cmd) return 1; } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////The commands lie below here///////////////////////////////////////////////////////////////////////////////////////// + +// perform the PACE protocol by replaying APDUs +int CmdHFEPAPACEReplay(const char *Cmd) +{ + // the 4 APDUs which are replayed + their lengths + uint8_t msesa_apdu[41], gn_apdu[8], map_apdu[75]; + uint8_t pka_apdu[75], ma_apdu[18], apdu_lengths[5] = {0}; + // pointers to the arrays to be able to iterate + uint8_t *apdus[] = {msesa_apdu, gn_apdu, map_apdu, pka_apdu, ma_apdu}; + + // usage message + static const char const *usage_msg = + "Please specify 5 APDUs separated by spaces. " + "Example:\n preplay 0022C1A4 1068000000 1086000002 1234ABCDEF 1A2B3C4D"; + + // Proxmark response + UsbCommand resp; + + int skip = 0, skip_add = 0, scan_return = 0; + // for each APDU + for (int i = 0; i < sizeof(apdu_lengths); i++) { + // scan to next space or end of string + while (Cmd[skip] != ' ' && Cmd[skip] != '\0') { + // convert + scan_return = sscanf(Cmd + skip, "%2X%n", + (unsigned int *) (apdus[i] + apdu_lengths[i]), + &skip_add); + if (scan_return < 1) { + PrintAndLog((char *)usage_msg); + PrintAndLog("Not enough APDUs! Try again!"); + return 0; + } + skip += skip_add; + apdu_lengths[i]++; + } + + // break on EOF + if (Cmd[skip] == '\0') { + if (i < sizeof(apdu_lengths) - 1) { + + PrintAndLog((char *)usage_msg); + return 0; + } + break; + } + // skip the space + skip++; + } + + // transfer the APDUs to the Proxmark + UsbCommand usb_cmd; + usb_cmd.cmd = CMD_EPA_PACE_REPLAY; + for (int i = 0; i < sizeof(apdu_lengths); i++) { + // APDU number + usb_cmd.arg[0] = i + 1; + // transfer the APDU in several parts if necessary + for (int j = 0; j * sizeof(usb_cmd.d.asBytes) < apdu_lengths[i]; j++) { + // offset into the APDU + usb_cmd.arg[1] = j * sizeof(usb_cmd.d.asBytes); + // amount of data in this packet + int packet_length = apdu_lengths[i] - (j * sizeof(usb_cmd.d.asBytes)); + if (packet_length > sizeof(usb_cmd.d.asBytes)) { + packet_length = sizeof(usb_cmd.d.asBytes); + } + usb_cmd.arg[2] = packet_length; + + memcpy(usb_cmd.d.asBytes, // + (j * sizeof(usb_cmd.d.asBytes)), + apdus[i] + (j * sizeof(usb_cmd.d.asBytes)), + packet_length); + SendCommand(&usb_cmd); + WaitForResponse(CMD_ACK, &resp); + if (resp.arg[0] != 0) { + PrintAndLog("Transfer of APDU #%d Part %d failed!", i, j); + return 0; + } + } + } + + // now perform the replay + usb_cmd.arg[0] = 0; + SendCommand(&usb_cmd); + WaitForResponse(CMD_ACK, &resp); + if (resp.arg[0] != 0) { + PrintAndLog("\nPACE replay failed in step %u!", (uint32_t)resp.arg[0]); + PrintAndLog("Measured times:"); + PrintAndLog("MSE Set AT: %u us", resp.d.asDwords[0]); + PrintAndLog("GA Get Nonce: %u us", resp.d.asDwords[1]); + PrintAndLog("GA Map Nonce: %u us", resp.d.asDwords[2]); + PrintAndLog("GA Perform Key Agreement: %u us", resp.d.asDwords[3]); + PrintAndLog("GA Mutual Authenticate: %u us", resp.d.asDwords[4]); + } else { + PrintAndLog("PACE replay successfull!"); + PrintAndLog("MSE Set AT: %u us", resp.d.asDwords[0]); + PrintAndLog("GA Get Nonce: %u us", resp.d.asDwords[1]); + PrintAndLog("GA Map Nonce: %u us", resp.d.asDwords[2]); + PrintAndLog("GA Perform Key Agreement: %u us", resp.d.asDwords[3]); + PrintAndLog("GA Mutual Authenticate: %u us", resp.d.asDwords[4]); + } + + + return 1; +} + +////////////////////////////////The new commands lie above here///////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // UI-related stuff -static const command_t CommandTable[] = +static const command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"cnonces", CmdHFEPACollectPACENonces, 0, " Acquire n>0 encrypted PACE nonces of size m>0 with d sec pauses"}, + {"preplay", CmdHFEPAPACEReplay, 0, + " Perform PACE protocol by replaying given APDUs"}, {NULL, NULL, 0, NULL} }; @@ -92,4 +202,4 @@ int CmdHFEPA(const char *Cmd) // parse CmdsParse(CommandTable, Cmd); return 0; -} \ No newline at end of file +} diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 357395d43..169f30cfc 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -128,6 +128,7 @@ typedef struct{ #define CMD_READER_LEGIC_RF 0x0388 #define CMD_WRITER_LEGIC_RF 0x0389 #define CMD_EPA_PACE_COLLECT_NONCE 0x038A +#define CMD_EPA_PACE_REPLAY 0x038B #define CMD_SNOOP_ICLASS 0x0392 #define CMD_SIMULATE_TAG_ICLASS 0x0393 From 705bfa1058837ae60f014458b0a01e88cdf5839d Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 22 Jun 2015 21:45:28 +0200 Subject: [PATCH 15/18] fixing iso14443b (issue #103): - increased DMA_BUFFER_SIZE to avoid occasional circular buffer overflows. - minor code cleanups --- armsrc/iso14443b.c | 93 ++++++++++++++-------------------------- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 4 +- 3 files changed, 33 insertions(+), 64 deletions(-) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 1ae1692b9..416c31f93 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -17,6 +17,7 @@ #include "iso14443crc.h" #define RECEIVE_SAMPLES_TIMEOUT 2000 +#define ISO14443B_DMA_BUFFER_SIZE 256 //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using @@ -717,16 +718,16 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); // Setup and start DMA. - FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); int8_t *upTo = dmaBuf; - lastRxCounter = DMA_BUFFER_SIZE; + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: LED_D_ON(); @@ -737,18 +738,18 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; if(behindBy > max) max = behindBy; - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (DMA_BUFFER_SIZE-1)) > 2) { + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1)) > 2) { ci = upTo[0]; cq = upTo[1]; upTo += 2; - if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { upTo = dmaBuf; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; } lastRxCounter -= 2; if(lastRxCounter <= 0) { - lastRxCounter += DMA_BUFFER_SIZE; + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; } samples += 2; @@ -770,7 +771,6 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; - //GetParity(Demod.output, Demod.len, parity); LogTrace(Demod.output, Demod.len, 0, 0, parity, FALSE); } } @@ -892,7 +892,6 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) TransmitFor14443b(); if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; - GetParity(cmd, len, parity); LogTrace(cmd,len, 0, 0, parity, TRUE); } } @@ -927,35 +926,29 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) // Now give it time to spin up. // Signal field is on with the appropriate LED LED_D_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); SpinDelay(200); // First command: wake up the tag using the INITIATE command uint8_t cmd1[] = {0x06, 0x00, 0x97, 0x5b}; - CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); -// LED_A_ON(); GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); -// LED_A_OFF(); if (Demod.len == 0) { - DbpString("No response from tag"); - return; + DbpString("No response from tag"); + return; } else { - Dbprintf("Randomly generated UID from tag (+ 2 byte CRC): %02x %02x %02x", - Demod.output[0], Demod.output[1], Demod.output[2]); + Dbprintf("Randomly generated Chip ID (+ 2 byte CRC): %02x %02x %02x", + Demod.output[0], Demod.output[1], Demod.output[2]); } + // There is a response, SELECT the uid DbpString("Now SELECT tag:"); cmd1[0] = 0x0E; // 0x0E is SELECT cmd1[1] = Demod.output[0]; ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); - -// LED_A_ON(); GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); -// LED_A_OFF(); if (Demod.len != 3) { Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); return; @@ -971,15 +964,13 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) Dbprintf("Bad response to SELECT from Tag, aborting: %02x %02x", cmd1[1], Demod.output[0]); return; } + // Tag is now selected, // First get the tag's UID: cmd1[0] = 0x0B; ComputeCrc14443(CRC_14443_B, cmd1, 1 , &cmd1[1], &cmd1[2]); CodeAndTransmit14443bAsReader(cmd1, 3); // Only first three bytes for this one - -// LED_A_ON(); GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); -// LED_A_OFF(); if (Demod.len != 10) { Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); return; @@ -988,12 +979,12 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) ComputeCrc14443(CRC_14443_B, Demod.output, 8, &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { Dbprintf("CRC Error reading block! Expected: %04x got: %04x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); + (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); // Do not return;, let's go on... (we should retry, maybe ?) } Dbprintf("Tag UID (64 bits): %08x %08x", - (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); + (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); // Now loop to read all 16 blocks, address from 0 to last block Dbprintf("Tag memory dump, block 0 to %d", dwLast); @@ -1008,10 +999,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) cmd1[1] = i; ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); - -// LED_A_ON(); GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); -// LED_A_OFF(); if (Demod.len != 6) { // Check if we got an answer from the tag DbpString("Expected 6 bytes from tag, got less..."); return; @@ -1020,13 +1008,13 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) ComputeCrc14443(CRC_14443_B, Demod.output, 4, &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[4] || cmd1[3] != Demod.output[5]) { Dbprintf("CRC Error reading block! Expected: %04x got: %04x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); + (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); // Do not return;, let's go on... (we should retry, maybe ?) } // Now print out the memory location: Dbprintf("Address=%02x, Contents=%08x, CRC=%04x", i, - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], - (Demod.output[4]<<8)+Demod.output[5]); + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], + (Demod.output[4]<<8)+Demod.output[5]); if (i == 0xff) { break; } @@ -1049,7 +1037,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) * Memory usage for this function, (within BigBuf) * Last Received command (reader->tag) - MAX_FRAME_SIZE * Last Received command (tag->reader) - MAX_FRAME_SIZE - * DMA Buffer - DMA_BUFFER_SIZE + * DMA Buffer - ISO14443B_DMA_BUFFER_SIZE * Demodulated samples received - all the rest */ void RAMFUNC SnoopIso14443b(void) @@ -1066,7 +1054,7 @@ void RAMFUNC SnoopIso14443b(void) set_tracing(TRUE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); int lastRxCounter; int8_t *upTo; int ci, cq; @@ -1084,7 +1072,7 @@ void RAMFUNC SnoopIso14443b(void) Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); Dbprintf(" Reader -> tag: %i bytes", MAX_FRAME_SIZE); Dbprintf(" tag -> Reader: %i bytes", MAX_FRAME_SIZE); - Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE); + Dbprintf(" DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); // Signal field is off, no reader signal, no tag signal LEDsoff(); @@ -1096,8 +1084,8 @@ void RAMFUNC SnoopIso14443b(void) // Setup for the DMA. FpgaSetupSsc(); upTo = dmaBuf; - lastRxCounter = DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; bool TagIsActive = FALSE; @@ -1106,7 +1094,7 @@ void RAMFUNC SnoopIso14443b(void) // And now we loop, receiving samples. for(;;) { int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (DMA_BUFFER_SIZE-1); + (ISO14443B_DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; } @@ -1117,13 +1105,13 @@ void RAMFUNC SnoopIso14443b(void) cq = upTo[1]; upTo += 2; lastRxCounter -= 2; - if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { upTo = dmaBuf; - lastRxCounter += DMA_BUFFER_SIZE; + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; WDT_HIT(); - if(behindBy > (9*DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? + if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? Dbprintf("blew circular buffer! behindBy=%d", behindBy); break; } @@ -1142,7 +1130,6 @@ void RAMFUNC SnoopIso14443b(void) if (!TagIsActive) { // no need to try decoding reader data if the tag is sending if(Handle14443bUartBit(ci & 0x01)) { if(triggered && tracing) { - //GetParity(Uart.output, Uart.byteCnt, parity); LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); } /* And ready to receive another command. */ @@ -1153,7 +1140,6 @@ void RAMFUNC SnoopIso14443b(void) } if(Handle14443bUartBit(cq & 0x01)) { if(triggered && tracing) { - //GetParity(Uart.output, Uart.byteCnt, parity); LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); } /* And ready to receive another command. */ @@ -1172,7 +1158,6 @@ void RAMFUNC SnoopIso14443b(void) if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; - //GetParity(Demod.output, Demod.len, parity); LogTrace(Demod.output, Demod.len, samples, samples, parity, FALSE); } triggered = TRUE; @@ -1217,22 +1202,6 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, u set_tracing(TRUE); -/* if(!powerfield) { - // Make sure that we start from off, since the tags are stateful; - // confusing things will happen if we don't reset them between reads. - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - SpinDelay(200); - } - */ - - // if(!GETBIT(GPIO_LED_D)) { // if field is off - // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); - // // Signal field is on with the appropriate LED - // LED_D_ON(); - // SpinDelay(200); - // } - CodeAndTransmit14443bAsReader(data, datalen); if(recv) { diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index a4d72e373bb59cfa56cc3869b1ccb3c18908250a..50c7eef97c8b98461b7d8be9e4e643dd85910241 100644 GIT binary patch literal 42175 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
aF>5%M+C8_(G-6 z$7*^8`U~}M`4oNHU6jE^6)p5b_$*0xLB^E9yhy@5F$uQM z7c2-C|Nm?JK2K#)%8>(;c%)jP#;q zk*bVY8d*9{*HfLU2*dc>J<`i9sxq&hVBS?B z8-wA9GTe$__SsJ?7N zW6d1lHg2KnyhusMhA$HFh1>#Dt#RyLv(o3On~mRAFJ?c|SjWP6c3EQ_!odC9QhC=? zH`m-b@VV?~8*84fxl?V(ex`BJ-B)-m&( zkN=#8yK9>6;s-8z+PqU)+23qj%-sC3c# zB?J9}Fi9=23A)AaR1;Yy*Y_`MFrQ+fkA<3L)GHU-X7HTmnoUQ3H}jbtiypX4P}O%i zgiITe!tn(PI>g}_2;AcTSnKXs(5P;1S}@GQ(9MzG9$uGap=m+Thi>XyaGQBkWP#Me zry;EAvu`s$9a$uU=iqODqjt#Ae+0ZQx5yK2%|{lJbm%}=DC$l zW(bZoKr=3TLeNJ^+CiVC=Enx=3+qM}TrT*JvD`Rm)0K&=zf6#}*UJsy_)O zeN4ugn`wc%T=4qX5h=2?i%SLB!BvhSfN?>T*uo+=z)_MFPQq}dl~pqpsH0O$ukcRK)jtg>IfQojORmYDnPsxe#J7`gj0LHQHA zDQj+tTtmO97L+d&DrW=xbqOv90A0~9iT#>`%K<<$F8g!GIrIvE7KqCPs?5uvKDQxL z!{~OYApPe6VXC;S;dnvN;&tJ3cnK~8l`1WjcSYpoY8ABj@48gKz8F?CzYo@MWn@0B zRefb&umizg!#;xM%Q#nA9l1Q^dmBih!1}%DdK@*&~gao8t=x zwNP6T(4zb`j7ZXOTrnqNj^Mp3((A!-1($0pLKT$k|f(DJtTBrdo2Q8KT3i)E; zJbpjq(y$eTWHd`iAJ`H>ZMmkY8ouU=wm7KVTT6x_?^JxI9N# zQ|ah-q;w3;(<;9*5^ob6M-83SX+LsGKpE>iIYx2EU;Z46}St<)vd5%q2WN^5@ zLW^Ap6I#H^Yhlbg(beam20Y_0ct!A^L&0P?ILNDr6{`sTc<>+p7rbBoQU-$GsbXL$ z&Q*clPav3%-||2FPiN=9(WO;Xs!w z!ezSrOKhh9uBt8vN?jGId+O_QpwuD3?-b~A^&eHG_jUP~nS%ds^}V_~ze>vBFG2A! z$xzR4{i;6ix4*vv@2|l7EAajbyuSkPufY2&@E>^vw7&$03I3A&kGu}=&;9)scz*@n zUxD{m;QbYNe+B+yt^mUM$TWQZiy2b*FCnTg=lwb>;rFk};47fq-+RuHYFf&F$o~V| Ce|JUz literal 42175 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 Date: Tue, 23 Jun 2015 12:16:23 -0400 Subject: [PATCH 16/18] fix bug in pskdemod return value if no samples... ... caused crash in data psknexwatchdemod if no samples were in the graphbuffer. also fixed hf mfu wrbl and rdbl to allow printing of help without a tag being present. --- client/cmddata.c | 4 ++-- client/cmdhfmfu.c | 56 ++++++++++++++++++++++------------------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/client/cmddata.c b/client/cmddata.c index aa1170fc7..bec1b5aa3 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -1546,12 +1546,12 @@ int PSKDemod(const char *Cmd, bool verbose) clk=0; } if (invert != 0 && invert != 1) { - if (verbose) PrintAndLog("Invalid argument: %s", Cmd); + if (g_debugMode || verbose) PrintAndLog("Invalid argument: %s", Cmd); return 0; } uint8_t BitStream[MAX_GRAPH_TRACE_LEN]={0}; size_t BitLen = getFromGraphBuf(BitStream); - if (BitLen==0) return -1; + if (BitLen==0) return 0; uint8_t carrier=countFC(BitStream, BitLen, 0); if (carrier!=2 && carrier!=4 && carrier!=8){ //invalid carrier diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 3dfee3a6a..25a073d34 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -908,10 +908,6 @@ int CmdHF14AMfUWrBl(const char *Cmd){ uint8_t authenticationkey[16] = {0x00}; uint8_t *authKeyPtr = authenticationkey; - // starting with getting tagtype - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) return -1; - while(param_getchar(Cmd, cmdp) != 0x00) { switch(param_getchar(Cmd, cmdp)) @@ -943,21 +939,10 @@ int CmdHF14AMfUWrBl(const char *Cmd){ case 'b': case 'B': blockNo = param_get8(Cmd, cmdp+1); - - uint8_t maxblockno = 0; - for (uint8_t idx = 0; idx < MAX_UL_TYPES; idx++){ - if (tagtype & UL_TYPES_ARRAY[idx]) - maxblockno = UL_MEMORY_ARRAY[idx]; - } - if (blockNo < 0) { PrintAndLog("Wrong block number"); errors = true; } - if (blockNo > maxblockno){ - PrintAndLog("block number too large. Max block is %u/0x%02X \n", maxblockno,maxblockno); - errors = true; - } cmdp += 2; break; case 'l': @@ -984,6 +969,19 @@ int CmdHF14AMfUWrBl(const char *Cmd){ } if ( blockNo == -1 ) return usage_hf_mfu_wrbl(); + // starting with getting tagtype + TagTypeUL_t tagtype = GetHF14AMfU_Type(); + if (tagtype == UL_ERROR) return -1; + + uint8_t maxblockno = 0; + for (uint8_t idx = 0; idx < MAX_UL_TYPES; idx++){ + if (tagtype & UL_TYPES_ARRAY[idx]) + maxblockno = UL_MEMORY_ARRAY[idx]; + } + if (blockNo > maxblockno){ + PrintAndLog("block number too large. Max block is %u/0x%02X \n", maxblockno,maxblockno); + return usage_hf_mfu_wrbl(); + } // Swap endianness if (swapEndian && hasAuthKey) authKeyPtr = SwapEndian64(authenticationkey, 16, 8); @@ -1035,10 +1033,6 @@ int CmdHF14AMfURdBl(const char *Cmd){ uint8_t authenticationkey[16] = {0x00}; uint8_t *authKeyPtr = authenticationkey; - // starting with getting tagtype - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) return -1; - while(param_getchar(Cmd, cmdp) != 0x00) { switch(param_getchar(Cmd, cmdp)) @@ -1070,21 +1064,10 @@ int CmdHF14AMfURdBl(const char *Cmd){ case 'b': case 'B': blockNo = param_get8(Cmd, cmdp+1); - - uint8_t maxblockno = 0; - for (uint8_t idx = 0; idx < MAX_UL_TYPES; idx++){ - if (tagtype & UL_TYPES_ARRAY[idx]) - maxblockno = UL_MEMORY_ARRAY[idx]; - } - if (blockNo < 0) { PrintAndLog("Wrong block number"); errors = true; } - if (blockNo > maxblockno){ - PrintAndLog("block number to large. Max block is %u/0x%02X \n", maxblockno,maxblockno); - errors = true; - } cmdp += 2; break; case 'l': @@ -1102,6 +1085,19 @@ int CmdHF14AMfURdBl(const char *Cmd){ } if ( blockNo == -1 ) return usage_hf_mfu_rdbl(); + // start with getting tagtype + TagTypeUL_t tagtype = GetHF14AMfU_Type(); + if (tagtype == UL_ERROR) return -1; + + uint8_t maxblockno = 0; + for (uint8_t idx = 0; idx < MAX_UL_TYPES; idx++){ + if (tagtype & UL_TYPES_ARRAY[idx]) + maxblockno = UL_MEMORY_ARRAY[idx]; + } + if (blockNo > maxblockno){ + PrintAndLog("block number to large. Max block is %u/0x%02X \n", maxblockno,maxblockno); + return usage_hf_mfu_rdbl(); + } // Swap endianness if (swapEndian && hasAuthKey) authKeyPtr = SwapEndian64(authenticationkey, 16, 8); From 475aefa5958d0eab881b5b140e0d0e59571f397c Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 23 Jun 2015 22:12:03 +0200 Subject: [PATCH 17/18] Updated changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c016c3d0..5a6ac6207 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [Unreleased][unreleased] + + + +## [2.1.0][2015-06-23] + ### Changed - Added ultralight/ntag tag type detection to `hf 14a read` (marshmellow) - Improved ultralight dump command to auto detect tag type, take authentication, and dump full memory (or subset specified) of known tag types (iceman1001 / marshmellow) @@ -14,6 +19,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Fixed - Fixed EM4x50 read/demod of the tags broadcasted memory blocks. 'lf em4x em4x50read' (not page read) (marshmellow) - Fixed issue #19, problems with LF T55xx commands (iceman1001, marshmellow) +- Fixed various problems with iso14443b, issue #103 (piwi, marshmellow) ### Added - Added `hf search` - currently tests for 14443a tags, iclass tags, and 15693 tags (marshmellow) From dd3d1b7012762887ada17fa041100d8e16f540c0 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 23 Jun 2015 22:24:55 +0200 Subject: [PATCH 18/18] Changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a6ac6207..b416e26a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [Unreleased][unreleased] +### Added +- Add PACE replay functionality (frederikmoellers) + +### Fixed +- t55xx write timing (marshmellow) ## [2.1.0][2015-06-23]