mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2024-11-11 01:55:38 +08:00
1017 lines
31 KiB
C
1017 lines
31 KiB
C
//-----------------------------------------------------------------------------
|
|
// Jonathan Westhues, split Nov 2006
|
|
// Modified by Greg Jones, Jan 2009
|
|
// Modified by Adrian Dabrowski "atrox", Mar-Sept 2010,Oct 2011
|
|
// Modified by Christian Herrmann "iceman", 2017
|
|
//
|
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
|
// the license.
|
|
//-----------------------------------------------------------------------------
|
|
// Routines to support ISO 15693. This includes both the reader software and
|
|
// the `fake tag' modes, but at the moment I've implemented only the reader
|
|
// stuff, and that barely.
|
|
// Modified to perform modulation onboard in arm rather than on PC
|
|
// Also added additional reader commands (SELECT, READ etc.)
|
|
//-----------------------------------------------------------------------------
|
|
// The ISO 15693 describes two transmission modes from reader to tag, and 4
|
|
// transmission modes from tag to reader. As of Mar 2010 this code only
|
|
// supports one of each: "1of4" mode from reader to tag, and the highspeed
|
|
// variant with one subcarrier from card to reader.
|
|
// As long, as the card fully support ISO 15693 this is no problem, since the
|
|
// reader chooses both data rates, but some non-standard tags do not. Further for
|
|
// the simulation to work, we will need to support all data rates.
|
|
//
|
|
// VCD (reader) -> VICC (tag)
|
|
// 1 out of 256:
|
|
// data rate: 1,66 kbit/s (fc/8192)
|
|
// used for long range
|
|
// 1 out of 4:
|
|
// data rate: 26,48 kbit/s (fc/512)
|
|
// used for short range, high speed
|
|
//
|
|
// VICC (tag) -> VCD (reader)
|
|
// Modulation:
|
|
// ASK / one subcarrier (423,75 kHz)
|
|
// FSK / two subcarriers (423,75 kHz && 484,28 kHz)
|
|
// Data Rates / Modes:
|
|
// low ASK: 6,62 kbit/s
|
|
// low FSK: 6.67 kbit/s
|
|
// high ASK: 26,48 kbit/s
|
|
// high FSK: 26,69 kbit/s
|
|
//-----------------------------------------------------------------------------
|
|
// added "1 out of 256" mode (for VCD->PICC) - atrox 20100911
|
|
|
|
|
|
// Random Remarks:
|
|
// *) UID is always used "transmission order" (LSB), which is reverse to display order
|
|
|
|
// TODO / BUGS / ISSUES:
|
|
// *) writing to tags takes longer: we miss the answer from the tag in most cases
|
|
// -> tweak the read-timeout times
|
|
// *) signal decoding from the card is still a bit shaky.
|
|
// *) signal decoding is unable to detect collissions.
|
|
// *) add anti-collission support for inventory-commands
|
|
// *) read security status of a block
|
|
// *) sniffing and simulation do only support one transmission mode. need to support
|
|
// all 8 transmission combinations
|
|
// *) remove or refactor code under "depricated"
|
|
// *) document all the functions
|
|
|
|
#include "iso15693.h"
|
|
|
|
#include "proxmark3_arm.h"
|
|
#include "util.h"
|
|
#include "string.h"
|
|
#include "iso15693tools.h"
|
|
#include "cmd.h"
|
|
#include "appmain.h"
|
|
#include "dbprint.h"
|
|
#include "fpgaloader.h"
|
|
#include "commonutil.h"
|
|
#include "ticks.h"
|
|
#include "BigBuf.h"
|
|
#include "crc16.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// ISO 15693 Part 2 - Air Interface
|
|
// This section basicly contains transmission and receiving of bits
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// 32 + 2 crc + 1
|
|
#define ISO15_MAX_FRAME 35
|
|
#define CMD_ID_RESP 5
|
|
#define CMD_READ_RESP 13
|
|
#define CMD_INV_RESP 12
|
|
|
|
#define FrameSOF Iso15693FrameSOF
|
|
#define Logic0 Iso15693Logic0
|
|
#define Logic1 Iso15693Logic1
|
|
#define FrameEOF Iso15693FrameEOF
|
|
|
|
//#define Crc(data, len) Crc(CRC_15693, (data), (len))
|
|
#define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len))
|
|
#define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1)
|
|
|
|
static void BuildIdentifyRequest(uint8_t *cmdout);
|
|
//static void BuildReadBlockRequest(uint8_t *cmdout, uint8_t *uid, uint8_t blockNumber );
|
|
static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid);
|
|
|
|
// ---------------------------
|
|
// Signal Processing
|
|
// ---------------------------
|
|
|
|
// prepare data using "1 out of 4" code for later transmission
|
|
// resulting data rate is 26,48 kbit/s (fc/512)
|
|
// cmd ... data
|
|
// n ... length of data
|
|
static void CodeIso15693AsReader(uint8_t *cmd, int n) {
|
|
int i, j;
|
|
|
|
ToSendReset();
|
|
|
|
// Give it a bit of slack at the beginning
|
|
for (i = 0; i < 24; i++)
|
|
ToSendStuffBit(1);
|
|
|
|
// SOF for 1of4
|
|
ToSendStuffBit(0);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
for (i = 0; i < n; i++) {
|
|
for (j = 0; j < 8; j += 2) {
|
|
int these = (cmd[i] >> j) & 3;
|
|
switch (these) {
|
|
case 0:
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
break;
|
|
case 1:
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
break;
|
|
case 2:
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
break;
|
|
case 3:
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// EOF
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
ToSendStuffBit(1);
|
|
|
|
// And slack at the end, too.
|
|
for (i = 0; i < 24; i++)
|
|
ToSendStuffBit(1);
|
|
}
|
|
|
|
// encode data using "1 out of 256" sheme
|
|
// data rate is 1,66 kbit/s (fc/8192)
|
|
// is designed for more robust communication over longer distances
|
|
static void CodeIso15693AsReader256(uint8_t *cmd, int n) {
|
|
int i, j;
|
|
|
|
ToSendReset();
|
|
|
|
// Give it a bit of slack at the beginning
|
|
for (i = 0; i < 24; i++)
|
|
ToSendStuffBit(1);
|
|
|
|
// SOF for 1of256
|
|
ToSendStuffBit(0);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
for (j = 0; j <= 255; j++) {
|
|
if (cmd[i] == j) {
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
} else {
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
}
|
|
}
|
|
}
|
|
// EOF
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(1);
|
|
ToSendStuffBit(0);
|
|
ToSendStuffBit(1);
|
|
|
|
// And slack at the end, too.
|
|
for (i = 0; i < 24; i++)
|
|
ToSendStuffBit(1);
|
|
}
|
|
|
|
// Transmit the command (to the tag) that was placed in ToSend[].
|
|
static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *wait) {
|
|
|
|
int c;
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
|
|
|
|
if (wait) {
|
|
for (c = 0; c < *wait;) {
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing!
|
|
++c;
|
|
}
|
|
WDT_HIT();
|
|
}
|
|
}
|
|
|
|
c = 0;
|
|
for (;;) {
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
AT91C_BASE_SSC->SSC_THR = cmd[c];
|
|
if (++c >= len) break;
|
|
}
|
|
WDT_HIT();
|
|
}
|
|
|
|
if (samples) {
|
|
if (wait)
|
|
*samples = (c + *wait) << 3;
|
|
else
|
|
*samples = c << 3;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Transmit the command (to the reader) that was placed in ToSend[].
|
|
//-----------------------------------------------------------------------------
|
|
static void TransmitTo15693Reader(const uint8_t *cmd, int len, int *samples, int *wait) {
|
|
int c = 0;
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K);
|
|
|
|
if (wait) {
|
|
for (c = 0; c < *wait;) {
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing!
|
|
++c;
|
|
}
|
|
WDT_HIT();
|
|
}
|
|
}
|
|
|
|
c = 0;
|
|
for (;;) {
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
AT91C_BASE_SSC->SSC_THR = cmd[c];
|
|
if (++c >= len) break;
|
|
}
|
|
WDT_HIT();
|
|
}
|
|
if (samples) {
|
|
if (wait)
|
|
*samples = (c + *wait) << 3;
|
|
else
|
|
*samples = c << 3;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// DEMODULATE tag answer
|
|
//-----------------------------------------------------------------------------
|
|
static int DemodAnswer(uint8_t *received, uint8_t *dest, uint16_t samplecount) {
|
|
|
|
int i, j;
|
|
int max = 0, maxPos = 0, skip = 4;
|
|
int k = 0; // this will be our return value
|
|
|
|
// First, correlate for SOF
|
|
for (i = 0; i < samplecount; i++) {
|
|
int corr = 0;
|
|
for (j = 0; j < ARRAYLEN(FrameSOF); j += skip) {
|
|
corr += FrameSOF[j] * dest[i + (j / skip)];
|
|
}
|
|
if (corr > max) {
|
|
max = corr;
|
|
maxPos = i;
|
|
}
|
|
}
|
|
// DbpString("SOF at %d, correlation %d", maxPos,max/(ARRAYLEN(FrameSOF)/skip));
|
|
|
|
// greg - If correlation is less than 1 then there's little point in continuing
|
|
if ((max / (ARRAYLEN(FrameSOF) / skip)) < 1)
|
|
return k;
|
|
|
|
i = maxPos + ARRAYLEN(FrameSOF) / skip;
|
|
|
|
uint8_t outBuf[ISO15_MAX_FRAME];
|
|
memset(outBuf, 0, sizeof(outBuf));
|
|
uint8_t mask = 0x01;
|
|
for (;;) {
|
|
int corr0 = 0, corr1 = 0, corrEOF = 0;
|
|
for (j = 0; j < ARRAYLEN(Logic0); j += skip) {
|
|
corr0 += Logic0[j] * dest[i + (j / skip)];
|
|
}
|
|
for (j = 0; j < ARRAYLEN(Logic1); j += skip) {
|
|
corr1 += Logic1[j] * dest[i + (j / skip)];
|
|
}
|
|
for (j = 0; j < ARRAYLEN(FrameEOF); j += skip) {
|
|
corrEOF += FrameEOF[j] * dest[i + (j / skip)];
|
|
}
|
|
// Even things out by the length of the target waveform.
|
|
corr0 *= 4;
|
|
corr1 *= 4;
|
|
// if (DBGLEVEL >= DBG_EXTENDED)
|
|
// Dbprintf("Corr1 %d, Corr0 %d, CorrEOF %d", corr1, corr0, corrEOF);
|
|
|
|
if (corrEOF > corr1 && corrEOF > corr0)
|
|
break;
|
|
|
|
if (corr1 > corr0) {
|
|
i += ARRAYLEN(Logic1) / skip;
|
|
outBuf[k] |= mask;
|
|
} else {
|
|
i += ARRAYLEN(Logic0) / skip;
|
|
}
|
|
|
|
mask <<= 1;
|
|
|
|
if (mask == 0) {
|
|
k++;
|
|
mask = 0x01;
|
|
}
|
|
|
|
if ((i + (int)ARRAYLEN(FrameEOF)) >= samplecount - 1) {
|
|
//Dbprintf("[!] ran off end! %d | %d",( i + (int)ARRAYLEN(FrameEOF)), samplecount-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("ice: demod bytes %u", k);
|
|
|
|
if (mask != 0x01) { // this happens, when we miss the EOF
|
|
|
|
// TODO: for some reason this happens quite often
|
|
if (DBGLEVEL >= DBG_ERROR && k != 0) Dbprintf("[!] error, uneven octet! (extra bits!) mask %02x", mask);
|
|
//if (mask < 0x08) k--; // discard the last uneven octet;
|
|
// 0x08 is an assumption - but works quite often
|
|
}
|
|
|
|
for (i = 0; i < k; i++)
|
|
received[i] = outBuf[i];
|
|
|
|
// return the number of bytes demodulated
|
|
return k;
|
|
}
|
|
|
|
// Read from Tag
|
|
// Parameters:
|
|
// received
|
|
// samples
|
|
// elapsed
|
|
// returns:
|
|
// number of decoded bytes
|
|
// logging enabled
|
|
static int GetIso15693AnswerFromTag(uint8_t *received, int *elapsed) {
|
|
|
|
#define SIGNAL_BUFF_SIZE 15000
|
|
// get current clock
|
|
uint32_t time_0 = GetCountSspClk();
|
|
uint32_t time_stop = 0;
|
|
bool getNext = false;
|
|
int counter = 0, ci, cq = 0;
|
|
uint8_t *buf = BigBuf_malloc(SIGNAL_BUFF_SIZE);
|
|
|
|
if (elapsed) *elapsed = 0;
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
|
|
|
|
for (;;) {
|
|
WDT_HIT();
|
|
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
AT91C_BASE_SSC->SSC_THR = 0x00; //0x43;
|
|
// To make use of exact timing of next command from reader!!
|
|
if (elapsed)(*elapsed)++;
|
|
}
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
|
|
|
ci = (int8_t)AT91C_BASE_SSC->SSC_RHR;
|
|
ci = ABS(ci);
|
|
|
|
// The samples are correlations against I and Q versions of the
|
|
// tone that the tag AM-modulates, so every other sample is I,
|
|
// every other is Q. We just want power, so abs(I) + abs(Q) is
|
|
// close to what we want.
|
|
// iceman 2016, amplitude sqrt(abs(i) + abs(q))
|
|
if (getNext) {
|
|
|
|
buf[counter++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1));
|
|
|
|
if (counter >= SIGNAL_BUFF_SIZE)
|
|
break;
|
|
} else {
|
|
cq = ci;
|
|
}
|
|
getNext = !getNext;
|
|
}
|
|
}
|
|
time_stop = GetCountSspClk();
|
|
int len = DemodAnswer(received, buf, counter);
|
|
LogTrace(received, len, time_0 << 4, time_stop << 4, NULL, false);
|
|
BigBuf_free();
|
|
return len;
|
|
}
|
|
|
|
|
|
// Now the GetISO15693 message from sniffing command
|
|
// logging enable,
|
|
static int GetIso15693AnswerFromSniff(uint8_t *received, int *samples, int *elapsed) {
|
|
|
|
bool getNext = false;
|
|
int counter = 0, ci, cq = 0;
|
|
uint32_t time_0 = 0, time_stop = 0;
|
|
uint8_t *buf = BigBuf_get_addr();
|
|
|
|
// get current clock
|
|
time_0 = GetCountSspClk();
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
|
|
|
|
for (;;) {
|
|
WDT_HIT();
|
|
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
|
|
|
ci = (int8_t)AT91C_BASE_SSC->SSC_RHR;
|
|
ci = ABS(ci);
|
|
|
|
// The samples are correlations against I and Q versions of the
|
|
// tone that the tag AM-modulates, so every other sample is I,
|
|
// every other is Q. We just want power, so abs(I) + abs(Q) is
|
|
// close to what we want.
|
|
if (getNext) {
|
|
|
|
buf[counter++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1));
|
|
|
|
if (counter >= 20000)
|
|
break;
|
|
} else {
|
|
cq = ci;
|
|
}
|
|
getNext = !getNext;
|
|
}
|
|
}
|
|
|
|
time_stop = GetCountSspClk();
|
|
int k = DemodAnswer(received, buf, counter);
|
|
LogTrace(received, k, time_0 << 4, time_stop << 4, NULL, false);
|
|
return k;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Start to read an ISO 15693 tag. We send an identify request, then wait
|
|
// for the response. The response is not demodulated, just left in the buffer
|
|
// so that it can be downloaded to a PC and processed there.
|
|
//-----------------------------------------------------------------------------
|
|
void AcquireRawAdcSamplesIso15693(void) {
|
|
int c = 0, getNext = false;
|
|
int ci, cq = 0;
|
|
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
|
|
FpgaSetupSsc();
|
|
|
|
// Now send the command
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
|
|
SpinDelay(200);
|
|
|
|
uint8_t *buf = BigBuf_get_addr();
|
|
|
|
uint32_t time_start = GetCountSspClk();
|
|
uint8_t cmd[CMD_ID_RESP] = {0};
|
|
BuildIdentifyRequest(cmd);
|
|
|
|
// sending command
|
|
c = 0;
|
|
for (;;) {
|
|
WDT_HIT();
|
|
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
|
|
AT91C_BASE_SSC->SSC_THR = ToSend[c];
|
|
c++;
|
|
if (c == ToSendMax + 3) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LogTrace(cmd, CMD_ID_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true);
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
|
|
|
|
c = 0;
|
|
for (;;) {
|
|
WDT_HIT();
|
|
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
|
|
|
ci = (int8_t)AT91C_BASE_SSC->SSC_RHR;
|
|
ci = ABS(ci);
|
|
|
|
// The samples are correlations against I and Q versions of the
|
|
// tone that the tag AM-modulates, so every other sample is I,
|
|
// every other is Q. We just want power, so abs(I) + abs(Q) is
|
|
// close to what we want.
|
|
// iceman 2016, amplitude sqrt(abs(i) + abs(q))
|
|
if (getNext) {
|
|
|
|
buf[c++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1));
|
|
|
|
if (c >= 7000) break;
|
|
|
|
} else {
|
|
cq = ci;
|
|
}
|
|
getNext = !getNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
// switch_off, initreader, no logging
|
|
void RecordRawAdcSamplesIso15693(void) {
|
|
|
|
int c = 0, getNext = false;
|
|
int ci, cq = 0;
|
|
|
|
Iso15693InitReader();
|
|
|
|
uint8_t *buf = BigBuf_get_addr();
|
|
|
|
for (;;) {
|
|
WDT_HIT();
|
|
|
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
|
|
|
ci = (int8_t)AT91C_BASE_SSC->SSC_RHR;
|
|
ci = ABS(ci);
|
|
// The samples are correlations against I and Q versions of the
|
|
// tone that the tag AM-modulates, so every other sample is I,
|
|
// every other is Q. We just want power, so abs(I) + abs(Q) is
|
|
// close to what we want.
|
|
if (getNext) {
|
|
|
|
buf[c++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1));
|
|
|
|
if (c >= 7000)
|
|
break;
|
|
} else {
|
|
cq = ci;
|
|
}
|
|
|
|
getNext = !getNext;
|
|
}
|
|
}
|
|
|
|
Dbprintf("done");
|
|
switch_off();
|
|
}
|
|
|
|
// Initialize the proxmark as iso15k reader
|
|
// (this might produces glitches that confuse some tags
|
|
void Iso15693InitReader(void) {
|
|
LEDsoff();
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
|
|
// Start from off (no field generated)
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
SpinDelay(10);
|
|
|
|
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
|
|
|
|
FpgaSetupSsc();
|
|
|
|
// Give the tags time to energize
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR);
|
|
SpinDelay(200);
|
|
|
|
// Start the timer
|
|
StartCountSspClk();
|
|
|
|
LED_A_ON();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// ISO 15693 Part 3 - Air Interface
|
|
// This section basicly contains transmission and receiving of bits
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// Encode (into the ToSend buffers) an identify request, which is the first
|
|
// thing that you must send to a tag to get a response.
|
|
// It expects "cmdout" to be at least CMD_ID_RESP large
|
|
static void BuildIdentifyRequest(uint8_t *cmdout) {
|
|
uint8_t cmd[CMD_ID_RESP] = {0, ISO15_CMD_INVENTORY, 0, 0, 0};
|
|
// flags
|
|
cmd[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1;
|
|
// no mask
|
|
cmd[2] = 0x00;
|
|
// CRC
|
|
AddCrc15(cmd, 3);
|
|
// coding as high speed (1 out of 4)
|
|
CodeIso15693AsReader(cmd, CMD_ID_RESP);
|
|
memcpy(cmdout, cmd, CMD_ID_RESP);
|
|
}
|
|
|
|
// uid is in transmission order (which is reverse of display order)
|
|
/*
|
|
static void BuildReadBlockRequest(uint8_t **out, uint8_t *uid, uint8_t blockNumber ) {
|
|
uint8_t cmd[CMD_READ_RESP] = {0,0,0,0,0,0,0,0,0,0,0,0,0};
|
|
// If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block
|
|
// followed by teh block data
|
|
// one sub-carrier, inventory, 1 slot, fast rate
|
|
cmd[0] = (1 << 6)| (1 << 5) | (1 << 1); // no SELECT bit, ADDR bit, OPTION bit
|
|
// READ BLOCK command code
|
|
cmd[1] = 0x20;
|
|
// UID may be optionally specified here
|
|
// 64-bit UID
|
|
cmd[2] = uid[0];
|
|
cmd[3] = uid[1];
|
|
cmd[4] = uid[2];
|
|
cmd[5] = uid[3];
|
|
cmd[6] = uid[4];
|
|
cmd[7] = uid[5];
|
|
cmd[8] = uid[6];
|
|
cmd[9] = uid[7]; // 0xe0; // always e0 (not exactly unique)
|
|
// Block number to read
|
|
cmd[10] = blockNumber;//0x00;
|
|
// CRC
|
|
AddCrc15(cmd, 11);
|
|
CodeIso15693AsReader(cmd, CMD_READ_RESP);
|
|
memcpy(out, cmd, CMD_ID_RESP);
|
|
}
|
|
*/
|
|
|
|
// Now the VICC>VCD responses when we are simulating a tag
|
|
// It expects "out" to be at least CMD_INV_RESP large
|
|
static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid) {
|
|
|
|
uint8_t cmd[CMD_INV_RESP] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
// one sub-carrier, inventory, 1 slot, fast rate
|
|
// AFI is at bit 5 (1<<4) when doing an INVENTORY
|
|
//(1 << 2) | (1 << 5) | (1 << 1);
|
|
cmd[0] = 0; //
|
|
cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported
|
|
// 64-bit UID
|
|
cmd[2] = uid[7]; //0x32;
|
|
cmd[3] = uid[6]; //0x4b;
|
|
cmd[4] = uid[5]; //0x03;
|
|
cmd[5] = uid[4]; //0x01;
|
|
cmd[6] = uid[3]; //0x00;
|
|
cmd[7] = uid[2]; //0x10;
|
|
cmd[8] = uid[1]; //0x05;
|
|
cmd[9] = uid[0]; //0xe0;
|
|
// CRC
|
|
AddCrc15(cmd, 10);
|
|
CodeIso15693AsReader(cmd, CMD_INV_RESP);
|
|
memcpy(cmdout, cmd, CMD_INV_RESP);
|
|
}
|
|
|
|
// Universal Method for sending to and recv bytes from a tag
|
|
// init ... should we initialize the reader?
|
|
// speed ... 0 low speed, 1 hi speed
|
|
// **recv will return you a pointer to the received data
|
|
// If you do not need the answer use NULL for *recv[]
|
|
// return: length of received data
|
|
// logging enabled
|
|
static int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t *outdata) {
|
|
|
|
int t_samples = 0, wait = 0, elapsed = 0, answer_len = 0;
|
|
|
|
LEDsoff();
|
|
|
|
if (init) Iso15693InitReader();
|
|
|
|
LED_A_ON();
|
|
|
|
if (!speed)
|
|
CodeIso15693AsReader256(send, sendlen); // low speed (1 out of 256)
|
|
else
|
|
CodeIso15693AsReader(send, sendlen); // high speed (1 out of 4)
|
|
|
|
LED_A_INV();
|
|
|
|
uint32_t time_start = GetCountSspClk();
|
|
|
|
TransmitTo15693Tag(ToSend, ToSendMax, &t_samples, &wait);
|
|
LogTrace(send, sendlen, time_start << 4, GetCountSspClk() << 4, NULL, true);
|
|
|
|
// Now wait for a response
|
|
if (outdata != NULL) {
|
|
LED_B_INV();
|
|
answer_len = GetIso15693AnswerFromTag(outdata, &elapsed);
|
|
}
|
|
|
|
LEDsoff();
|
|
return answer_len;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// Debug Functions
|
|
// --------------------------------------------------------------------
|
|
|
|
// Decodes a message from a tag and displays its metadata and content
|
|
#define DBD15STATLEN 48
|
|
static void DbdecodeIso15693Answer(int len, uint8_t *d) {
|
|
|
|
if (len > 3) {
|
|
char status[DBD15STATLEN + 1] = {0};
|
|
if (d[0] & (1 << 3))
|
|
strncat(status, "ProtExt ", DBD15STATLEN - strlen(status));
|
|
if (d[0] & 1) {
|
|
// error
|
|
strncat(status, "Error ", DBD15STATLEN - strlen(status));
|
|
switch (d[1]) {
|
|
case 0x01:
|
|
strncat(status, "01: not supported", DBD15STATLEN - strlen(status));
|
|
break;
|
|
case 0x02:
|
|
strncat(status, "02: not recognized", DBD15STATLEN - strlen(status));
|
|
break;
|
|
case 0x03:
|
|
strncat(status, "03: opt not supported", DBD15STATLEN - strlen(status));
|
|
break;
|
|
case 0x0f:
|
|
strncat(status, "0F: no info", DBD15STATLEN - strlen(status));
|
|
break;
|
|
case 0x10:
|
|
strncat(status, "10: don't exist", DBD15STATLEN - strlen(status));
|
|
break;
|
|
case 0x11:
|
|
strncat(status, "11: lock again", DBD15STATLEN - strlen(status));
|
|
break;
|
|
case 0x12:
|
|
strncat(status, "12: locked", DBD15STATLEN - strlen(status));
|
|
break;
|
|
case 0x13:
|
|
strncat(status, "13: program error", DBD15STATLEN - strlen(status));
|
|
break;
|
|
case 0x14:
|
|
strncat(status, "14: lock error", DBD15STATLEN - strlen(status));
|
|
break;
|
|
default:
|
|
strncat(status, "unknown error", DBD15STATLEN - strlen(status));
|
|
}
|
|
strncat(status, " ", DBD15STATLEN - strlen(status));
|
|
} else {
|
|
strncat(status, "No error ", DBD15STATLEN - strlen(status));
|
|
}
|
|
|
|
if (CheckCrc15(d, len))
|
|
strncat(status, "[+] crc OK", DBD15STATLEN - strlen(status));
|
|
else
|
|
strncat(status, "[!] crc fail", DBD15STATLEN - strlen(status));
|
|
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("%s", status);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Functions called via USB/Client
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Act as ISO15693 reader, perform anti-collision and then attempt to read a sector
|
|
// all demodulation performed in arm rather than host. - greg
|
|
//-----------------------------------------------------------------------------
|
|
// ok
|
|
// parameter is unused !?!
|
|
void ReaderIso15693(uint32_t parameter) {
|
|
int answerLen1 = 0;
|
|
int tsamples = 0, wait = 0, elapsed = 0;
|
|
// set up device/fpga
|
|
Iso15693InitReader();
|
|
|
|
uint8_t *answer1 = BigBuf_malloc(50);
|
|
uint8_t *answer2 = BigBuf_malloc(50);
|
|
|
|
// Blank arrays
|
|
memset(answer1, 0x00, 50);
|
|
memset(answer2, 0x00, 50);
|
|
|
|
// Now send the IDENTIFY command
|
|
// FIRST WE RUN AN INVENTORY TO GET THE TAG UID
|
|
// THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME
|
|
uint32_t time_start = GetCountSspClk();
|
|
uint8_t cmd[CMD_ID_RESP] = {0};
|
|
BuildIdentifyRequest(cmd);
|
|
TransmitTo15693Tag(ToSend, ToSendMax, &tsamples, &wait);
|
|
LogTrace(cmd, CMD_ID_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true);
|
|
|
|
// Now wait for a response
|
|
answerLen1 = GetIso15693AnswerFromTag(answer1, &elapsed) ;
|
|
|
|
// we should do a better check than this
|
|
if (answerLen1 >= 12) {
|
|
uint8_t uid[8];
|
|
uid[0] = answer1[9]; // always E0
|
|
uid[1] = answer1[8]; // IC Manufacturer code
|
|
uid[2] = answer1[7];
|
|
uid[3] = answer1[6];
|
|
uid[4] = answer1[5];
|
|
uid[5] = answer1[4];
|
|
uid[6] = answer1[3];
|
|
uid[7] = answer1[2];
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) {
|
|
Dbprintf("[+] UID = %02X%02X%02X%02X%02X%02X%02X%02X",
|
|
uid[0], uid[1], uid[2], uid[3],
|
|
uid[4], uid[5], uid[5], uid[6]
|
|
);
|
|
}
|
|
// send UID back to client.
|
|
// arg0 = 1 = OK
|
|
// arg1 = len of response (12 bytes)
|
|
// arg2 = rtf
|
|
// asbytes = uid.
|
|
reply_old(CMD_ACK, 1, sizeof(uid), 0, uid, sizeof(uid));
|
|
}
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) {
|
|
Dbprintf("[+] %d octets read from IDENTIFY request:", answerLen1);
|
|
DbdecodeIso15693Answer(answerLen1, answer1);
|
|
Dbhexdump(answerLen1, answer1, true);
|
|
}
|
|
|
|
switch_off();
|
|
}
|
|
|
|
// Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands
|
|
// all demodulation performed in arm rather than host. - greg
|
|
void SimTagIso15693(uint32_t parameter, uint8_t *uid) {
|
|
|
|
LEDsoff();
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
|
|
FpgaSetupSsc();
|
|
// Start from off (no field generated)
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
SpinDelay(200);
|
|
|
|
LED_A_ON();
|
|
|
|
uint32_t time_start;
|
|
int samples = 0, tsamples = 0;
|
|
int wait = 0, elapsed = 0;
|
|
|
|
Dbprintf("ISO-15963 Simulating uid: %02X%02X%02X%02X%02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]);
|
|
|
|
uint8_t buf[ISO15_MAX_FRAME];
|
|
memset(buf, 0x00, sizeof(buf));
|
|
|
|
LED_C_ON();
|
|
|
|
// Build a suitable reponse to the reader INVENTORY cocmmand
|
|
// not so obsvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below.
|
|
uint8_t cmd[CMD_INV_RESP] = {0};
|
|
BuildInventoryResponse(cmd, uid);
|
|
|
|
while (!BUTTON_PRESS() && !data_available()) {
|
|
WDT_HIT();
|
|
|
|
// Listen to reader
|
|
int ans = GetIso15693AnswerFromSniff(buf, &samples, &elapsed) ;
|
|
|
|
// we should do a better check than this
|
|
if (ans >= 1) {
|
|
|
|
time_start = GetCountSspClk();
|
|
TransmitTo15693Reader(ToSend, ToSendMax, &tsamples, &wait);
|
|
LogTrace(cmd, CMD_INV_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true);
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) {
|
|
Dbprintf("[+] %d octets read from reader command: %x %x %x %x %x %x %x %x", ans,
|
|
buf[0], buf[1], buf[2], buf[3],
|
|
buf[4], buf[5], buf[6], buf[7]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
switch_off();
|
|
}
|
|
|
|
// Since there is no standardized way of reading the AFI out of a tag, we will brute force it
|
|
// (some manufactures offer a way to read the AFI, though)
|
|
void BruteforceIso15693Afi(uint32_t speed) {
|
|
|
|
uint8_t data[7] = {0, 0, 0, 0, 0, 0, 0};
|
|
uint8_t buf[ISO15_MAX_FRAME];
|
|
memset(buf, 0x00, sizeof(buf));
|
|
int datalen = 0, recvlen = 0;
|
|
bool aborted = false;
|
|
|
|
Iso15693InitReader();
|
|
|
|
// first without AFI
|
|
// Tags should respond wihtout AFI and with AFI=0 even when AFI is active
|
|
|
|
data[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1;
|
|
data[1] = ISO15_CMD_INVENTORY;
|
|
data[2] = 0; // AFI
|
|
AddCrc15(data, 3);
|
|
datalen = 5;
|
|
recvlen = SendDataTag(data, datalen, false, speed, buf);
|
|
|
|
WDT_HIT();
|
|
|
|
if (recvlen >= 12) {
|
|
Dbprintf("NoAFI UID = %s", iso15693_sprintUID(NULL, buf + 2));
|
|
}
|
|
|
|
// now with AFI
|
|
data[0] |= ISO15_REQINV_AFI;
|
|
//data[1] = ISO15_CMD_INVENTORY;
|
|
data[2] = 0; // AFI
|
|
data[3] = 0; // mask length
|
|
|
|
// 4 + 2crc
|
|
datalen = 6;
|
|
|
|
for (uint16_t i = 0; i < 256; i++) {
|
|
data[2] = i & 0xFF;
|
|
AddCrc15(data, 4);
|
|
recvlen = SendDataTag(data, datalen, false, speed, buf);
|
|
WDT_HIT();
|
|
if (recvlen >= 12) {
|
|
Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, buf + 2));
|
|
}
|
|
|
|
aborted = BUTTON_PRESS();
|
|
|
|
if (aborted) {
|
|
DbpString("button pressed, aborting..");
|
|
break;
|
|
}
|
|
}
|
|
|
|
DbpString("AFI Bruteforcing done.");
|
|
switch_off();
|
|
|
|
if (aborted) {
|
|
reply_ng(CMD_ACK, PM3_EOPABORTED, NULL, 0);
|
|
} else {
|
|
reply_ng(CMD_ACK, PM3_SUCCESS, NULL, 0);
|
|
}
|
|
}
|
|
|
|
// Allows to directly send commands to the tag via the client
|
|
// Has to increase dialog between device and client.
|
|
void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data) {
|
|
|
|
bool init = true;
|
|
int buflen = 0;
|
|
uint8_t buf[ISO15_MAX_FRAME];
|
|
memset(buf, 0x00, sizeof(buf));
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) {
|
|
DbpString("[+] SEND");
|
|
Dbhexdump(datalen, data, true);
|
|
}
|
|
|
|
buflen = SendDataTag(data, datalen, init, speed, (recv ? buf : NULL));
|
|
|
|
if (recv) {
|
|
buflen = (buflen > ISO15_MAX_FRAME) ? ISO15_MAX_FRAME : buflen;
|
|
|
|
LED_B_ON();
|
|
reply_old(CMD_ACK, buflen, 0, 0, buf, buflen);
|
|
LED_B_OFF();
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) {
|
|
DbpString("[+] RECV");
|
|
DbdecodeIso15693Answer(buflen, buf);
|
|
Dbhexdump(buflen, buf, true);
|
|
}
|
|
} else {
|
|
reply_old(CMD_ACK, 1, 0, 0, 0, 0);
|
|
}
|
|
}
|