mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
added: hf 14b apdu
This commit is contained in:
parent
c3c59e35cb
commit
181bb3bc74
4 changed files with 483 additions and 93 deletions
|
@ -118,7 +118,7 @@ static int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response, uint16_t re
|
|||
case 'a':
|
||||
return iso14_apdu(apdu, (uint16_t) length, false, response, NULL);
|
||||
case 'b':
|
||||
return iso14443b_apdu(apdu, length, response, respmaxlen);
|
||||
return iso14443b_apdu(apdu, length, false, response, respmaxlen);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -44,12 +44,15 @@
|
|||
# define FWT_TIMEOUT_14B 35312
|
||||
#endif
|
||||
|
||||
// 1 tick == 1/13.56 mhz
|
||||
// 1 us = 1.5 tick
|
||||
|
||||
// 330/848kHz = 1558us / 4 == 400us,
|
||||
#define ISO14443B_READER_TIMEOUT 1700 //330
|
||||
#define ISO14443B_READER_TIMEOUT 10000 //330
|
||||
|
||||
// 1024/3.39MHz = 302.1us between end of tag response and next reader cmd
|
||||
#define DELAY_ISO14443B_VICC_TO_VCD_READER 600 // 1024
|
||||
#define DELAY_ISO14443B_VCD_TO_VICC_READER 600// 1056
|
||||
#define DELAY_ISO14443B_VICC_TO_VCD_READER (28*9) // 1024 ( counting from start of PICC EOF 14 ETU's)
|
||||
#define DELAY_ISO14443B_VCD_TO_VICC_READER (28*9) // 1056
|
||||
|
||||
#ifndef RECEIVE_MASK
|
||||
# define RECEIVE_MASK (DMA_BUFFER_SIZE - 1)
|
||||
|
@ -57,7 +60,7 @@
|
|||
|
||||
// Guard Time (per 14443-2)
|
||||
#ifndef TR0
|
||||
# define TR0 64 // TR0 max is 256/fs = 256/(848kHz) = 302us or 64 samples from FPGA
|
||||
# define TR0 32 // TR0 max is 151/fs = 151/(848kHz) = 302us or 64 samples from FPGA
|
||||
#endif
|
||||
|
||||
// Synchronization time (per 14443-2)
|
||||
|
@ -76,10 +79,9 @@ static void iso14b_set_timeout(uint32_t timeout);
|
|||
static void iso14b_set_maxframesize(uint16_t size);
|
||||
|
||||
// the block number for the ISO14443-4 PCB (used with APDUs)
|
||||
static uint8_t pcb_blocknum = 0;
|
||||
static uint8_t iso14b_pcb_blocknum = 0;
|
||||
static uint32_t iso14b_timeout = FWT_TIMEOUT_14B;
|
||||
|
||||
|
||||
/* ISO 14443 B
|
||||
*
|
||||
* Reader to card | ASK - Amplitude Shift Keying Modulation (PCD to PICC for Type B) (NRZ-L encodig)
|
||||
|
@ -306,7 +308,6 @@ static void Demod14bInit(uint8_t *data, uint16_t max_len) {
|
|||
Demod14bReset();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 9.4395 us = 1 ETU and clock is about 1.5 us
|
||||
* 13560000Hz
|
||||
|
@ -740,7 +741,7 @@ void SimulateIso14443bTag(uint32_t pupi) {
|
|||
*/
|
||||
static RAMFUNC int Handle14443bSamplesFromTag(int ci, int cq) {
|
||||
|
||||
int v;
|
||||
int v = 0;
|
||||
|
||||
// The soft decision on the bit uses an estimate of just the
|
||||
// quadrant of the reference angle, not the exact angle.
|
||||
|
@ -797,7 +798,7 @@ static RAMFUNC int Handle14443bSamplesFromTag(int ci, int cq) {
|
|||
Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF;
|
||||
Demod.posCount = 0; // start of SOF sequence
|
||||
} else {
|
||||
if (Demod.posCount > 200 / 4) { // maximum length of TR1 = 200 1/fs
|
||||
if (Demod.posCount > 200/4) { // maximum length of TR1 = 200 1/fs
|
||||
Demod.state = DEMOD_UNSYNCD;
|
||||
}
|
||||
}
|
||||
|
@ -820,7 +821,7 @@ static RAMFUNC int Handle14443bSamplesFromTag(int ci, int cq) {
|
|||
Demod.state = DEMOD_AWAITING_START_BIT;
|
||||
}
|
||||
} else {
|
||||
if (Demod.posCount > 14 * 2) { // low phase of SOF too long (> 12 etu)
|
||||
if (Demod.posCount > 12 * 2) { // low phase of SOF too long (> 12 etu)
|
||||
Demod.state = DEMOD_UNSYNCD;
|
||||
LED_C_OFF();
|
||||
}
|
||||
|
@ -831,7 +832,7 @@ static RAMFUNC int Handle14443bSamplesFromTag(int ci, int cq) {
|
|||
Demod.posCount++;
|
||||
MAKE_SOFT_DECISION();
|
||||
if (v > 0) {
|
||||
if (Demod.posCount > 6 * 2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs
|
||||
if (Demod.posCount > 3 * 2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs
|
||||
LED_C_OFF();
|
||||
if (Demod.bitCount == 0 && Demod.len == 0) { // received SOF only, this is valid for iClass/Picopass
|
||||
return true;
|
||||
|
@ -873,7 +874,7 @@ static RAMFUNC int Handle14443bSamplesFromTag(int ci, int cq) {
|
|||
Demod.bitCount = 0;
|
||||
Demod.state = DEMOD_AWAITING_START_BIT;
|
||||
} else {
|
||||
Demod.state = DEMOD_UNSYNCD;
|
||||
Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF;
|
||||
LED_C_OFF();
|
||||
if (s == 0x000) {
|
||||
// This is EOF (start, stop and all data bits == '0'
|
||||
|
@ -894,7 +895,6 @@ static RAMFUNC int Handle14443bSamplesFromTag(int ci, int cq) {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Demodulate the samples we received from the tag, also log to tracebuffer
|
||||
*/
|
||||
|
@ -905,12 +905,6 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, int timeo
|
|||
// Set up the demodulator for tag -> reader responses.
|
||||
Demod14bInit(response, max_len);
|
||||
|
||||
// wait for last transfer to complete
|
||||
while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) {};
|
||||
|
||||
// And put the FPGA in the appropriate mode
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ);
|
||||
|
||||
// Setup and start DMA.
|
||||
FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER);
|
||||
|
||||
|
@ -924,6 +918,9 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, int timeo
|
|||
uint32_t dma_start_time = 0;
|
||||
uint16_t *upTo = dma->buf;
|
||||
|
||||
// Put FPGA in the appropriate mode
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ);
|
||||
|
||||
for (;;) {
|
||||
|
||||
volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1);
|
||||
|
@ -971,7 +968,7 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, int timeo
|
|||
|
||||
if (Handle14443bSamplesFromTag(ci, cq)) {
|
||||
|
||||
*eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM; // end of EOF
|
||||
*eof_time = dma_start_time + (samples ) - DELAY_TAG_TO_ARM; // end of EOF
|
||||
|
||||
if (Demod.len > Demod.max_len) {
|
||||
ret = -2; // overflow
|
||||
|
@ -986,19 +983,17 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, int timeo
|
|||
}
|
||||
|
||||
FpgaDisableSscDma();
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (Demod.len > 0) {
|
||||
uint32_t sof_time = *eof_time
|
||||
- (Demod.len * 8 * 8 * 16) // time for byte transfers
|
||||
- (32 * 16) // time for SOF transfer
|
||||
- 0; // time for EOF transfer
|
||||
- (Demod.len * (8 + 2)) // time for byte transfers
|
||||
- (12) // time for SOF transfer
|
||||
- (12); // time for EOF transfer
|
||||
LogTrace(Demod.output, Demod.len, (sof_time * 4), (*eof_time * 4), NULL, false);
|
||||
}
|
||||
|
||||
return Demod.len;
|
||||
}
|
||||
|
||||
|
@ -1011,6 +1006,7 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) {
|
|||
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD);
|
||||
|
||||
// TR2 minimum 14 ETUs
|
||||
if (*start_time < DELAY_ARM_TO_TAG) {
|
||||
*start_time = DELAY_ARM_TO_TAG;
|
||||
}
|
||||
|
@ -1018,21 +1014,20 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) {
|
|||
*start_time = (*start_time - DELAY_ARM_TO_TAG) & 0xfffffff0;
|
||||
|
||||
if (GetCountSspClk() > *start_time) { // we may miss the intended time
|
||||
*start_time = (GetCountSspClk() + 16) & 0xfffffff0; // next possible time
|
||||
*start_time = (GetCountSspClk() + 32) & 0xfffffff0; // next possible time
|
||||
}
|
||||
|
||||
// wait
|
||||
while (GetCountSspClk() < *start_time) ;
|
||||
while (GetCountSspClk() < *start_time);
|
||||
|
||||
LED_B_ON();
|
||||
for (int c = 0; c < ts->max; c++) {
|
||||
volatile uint8_t data = ts->buf[c];
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint16_t send_word = (data & 0x80) ? 0x0000 : 0xffff;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
volatile uint16_t send_word = (data & 0x80) ? 0x0000 : 0xFFFF;
|
||||
|
||||
while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ;
|
||||
|
||||
AT91C_BASE_SSC->SSC_THR = send_word;
|
||||
|
||||
while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ;
|
||||
|
@ -1045,6 +1040,9 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) {
|
|||
LED_B_OFF();
|
||||
|
||||
*start_time += DELAY_ARM_TO_TAG;
|
||||
|
||||
// wait for last transfer to complete
|
||||
while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) {};
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1061,32 +1059,38 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) {
|
|||
* - no modulation ONES
|
||||
*
|
||||
* 1 ETU == 1 BIT!
|
||||
* TR0 - 8 ETUS minimum.
|
||||
* TR0 - 8 ETU's minimum.
|
||||
* TR0 - 32 ETU's maximum for ATQB only
|
||||
* TR0 - FWT for all other commands
|
||||
*
|
||||
* QUESTION: how long is a 1 or 0 in pulses in the xcorr_848 mode?
|
||||
* 1 "stuffbit" = 1ETU (9us)
|
||||
*
|
||||
* TR2 - After the PICC response, the PCD is required to wait the Frame Delay Time (TR2)
|
||||
before transmission of the next command. The minimum frame delay time required for
|
||||
all commands is 14 ETUs
|
||||
*
|
||||
*/
|
||||
|
||||
int i;
|
||||
tosend_reset();
|
||||
|
||||
// Send SOF
|
||||
// 10-11 ETUs of ZERO
|
||||
for (int i = 0; i < 10; i++)
|
||||
for (i = 0; i < 10; i++) {
|
||||
tosend_stuffbit(0);
|
||||
|
||||
|
||||
}
|
||||
// 2-3 ETUs of ONE
|
||||
tosend_stuffbit(1);
|
||||
tosend_stuffbit(1);
|
||||
|
||||
// Sending cmd, LSB
|
||||
// from here we add BITS
|
||||
for (int i = 0; i < len; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
// Start bit
|
||||
tosend_stuffbit(0);
|
||||
// Data bits
|
||||
uint8_t b = cmd[i];
|
||||
|
||||
// Data bits
|
||||
volatile uint8_t b = cmd[i];
|
||||
tosend_stuffbit(b & 1);
|
||||
tosend_stuffbit((b >> 1) & 1);
|
||||
tosend_stuffbit((b >> 2) & 1);
|
||||
|
@ -1098,28 +1102,28 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) {
|
|||
|
||||
// Stop bit
|
||||
tosend_stuffbit(1);
|
||||
// EGT extra guard time
|
||||
// For PCD it ranges 0-57us (1etu = 9us)
|
||||
// tosend_stuffbit(1);
|
||||
// tosend_stuffbit(1);
|
||||
// tosend_stuffbit(1);
|
||||
// EGT extra guard time 1 ETU = 9us
|
||||
// For PCD it ranges 0-57us === 0 - 6 ETU
|
||||
// FOR PICC it ranges 0-19us == 0 - 2 ETU
|
||||
}
|
||||
|
||||
// Send EOF
|
||||
// 10-11 ETUs of ZERO
|
||||
for (int i = 0; i < 10; i++)
|
||||
for (i = 0; i < 10; i++) {
|
||||
tosend_stuffbit(0);
|
||||
}
|
||||
|
||||
// Transition time. TR0 - guard time
|
||||
// 8ETUS minum?
|
||||
// Per specification, Subcarrier must be stopped no later than 2 ETUs after EOF.
|
||||
// I'm guessing this is for the FPGA to be able to send all bits before we switch to listening mode
|
||||
tosend_stuffbit(1);
|
||||
/* Transition time. TR0 - guard time
|
||||
* TR0 - 8 ETU's minimum.
|
||||
* TR0 - 32 ETU's maximum for ATQB only
|
||||
* TR0 - FWT for all other commands
|
||||
* 32,64,128,256,512, ... , 262144, 524288 ETU
|
||||
*/
|
||||
|
||||
// ensure that last byte is filled up
|
||||
for (int i = 0; i < 8 ; ++i)
|
||||
for (i = 0; i < 8 ; ++i)
|
||||
tosend_stuffbit(1);
|
||||
|
||||
// TR1 - Synchronization time
|
||||
// Convert from last character reference to length
|
||||
tosend_t *ts = get_tosend();
|
||||
ts->max++;
|
||||
|
@ -1132,54 +1136,100 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t
|
|||
tosend_t *ts = get_tosend();
|
||||
CodeIso14443bAsReader(cmd, len);
|
||||
TransmitFor14443b_AsReader(start_time);
|
||||
*eof_time = *start_time + (32 * (8 * ts->max));
|
||||
*eof_time = *start_time + (10 * ts->max) + 10 + 2 + 10;
|
||||
LogTrace(cmd, len, *start_time, *eof_time, NULL, true);
|
||||
}
|
||||
|
||||
/* Sends an APDU to the tag
|
||||
* TODO: check CRC and preamble
|
||||
*/
|
||||
int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response, uint16_t respmaxlen) {
|
||||
int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, uint8_t *response, uint16_t respmaxlen) {
|
||||
|
||||
LED_A_ON();
|
||||
uint8_t message_frame[message_length + 4];
|
||||
// PCB
|
||||
message_frame[0] = 0x0A | pcb_blocknum;
|
||||
pcb_blocknum ^= 1;
|
||||
// CID
|
||||
message_frame[1] = 0;
|
||||
// INF
|
||||
memcpy(message_frame + 2, message, message_length);
|
||||
// EDC (CRC)
|
||||
AddCrc14B(message_frame, message_length + 2);
|
||||
uint8_t real_cmd[msg_len + 4];
|
||||
|
||||
if (msg_len) {
|
||||
// ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
|
||||
real_cmd[0] = 0x02; // bnr, nad, cid, chn=0; i-block(0x00)
|
||||
if (send_chaining) {
|
||||
real_cmd[0] |= 0x10;
|
||||
}
|
||||
// put block number into the PCB
|
||||
real_cmd[0] |= iso14b_pcb_blocknum;
|
||||
memcpy(real_cmd + 1, msg, msg_len);
|
||||
} else {
|
||||
// R-block. ACK
|
||||
real_cmd[0] = 0xA2; // r-block + ACK
|
||||
real_cmd[0] |= iso14b_pcb_blocknum;
|
||||
}
|
||||
|
||||
AddCrc14B(real_cmd, msg_len + 1);
|
||||
|
||||
// send
|
||||
uint32_t start_time = 0;
|
||||
uint32_t eof_time = 0;
|
||||
CodeAndTransmit14443bAsReader(message_frame, sizeof(message_frame), &start_time, &eof_time);
|
||||
|
||||
// Get response?
|
||||
if (response == NULL) {
|
||||
LED_A_OFF();
|
||||
return 0;
|
||||
}
|
||||
CodeAndTransmit14443bAsReader(real_cmd, msg_len + 3, &start_time, &eof_time);
|
||||
|
||||
eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER;
|
||||
int retlen = Get14443bAnswerFromTag(response, respmaxlen, ISO14443B_READER_TIMEOUT, &eof_time);
|
||||
int len = Get14443bAnswerFromTag(response, respmaxlen, ISO14443B_READER_TIMEOUT, &eof_time);
|
||||
FpgaDisableTracing();
|
||||
|
||||
if (retlen < 3) {
|
||||
LED_A_OFF();
|
||||
uint8_t *data_bytes = (uint8_t *) response;
|
||||
|
||||
if (len <= 0) {
|
||||
return 0; //DATA LINK ERROR
|
||||
} else {
|
||||
// S-Block WTX
|
||||
while (len && ((data_bytes[0] & 0xF2) == 0xF2)) {
|
||||
uint32_t save_iso14b_timeout = iso14b_timeout;
|
||||
// temporarily increase timeout
|
||||
iso14b_set_timeout(MAX((data_bytes[1] & 0x3f) * save_iso14b_timeout, ISO14443B_READER_TIMEOUT));
|
||||
// Transmit WTX back
|
||||
// byte1 - WTXM [1..59]. command FWT=FWT*WTXM
|
||||
data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b
|
||||
// now need to fix CRC.
|
||||
AddCrc14B(data_bytes, len - 2);
|
||||
|
||||
// transmit S-Block
|
||||
CodeAndTransmit14443bAsReader(data_bytes, len, &start_time, &eof_time);
|
||||
|
||||
// retrieve the result again (with increased timeout)
|
||||
eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER;
|
||||
len = Get14443bAnswerFromTag(response, respmaxlen, ISO14443B_READER_TIMEOUT, &eof_time);
|
||||
FpgaDisableTracing();
|
||||
|
||||
data_bytes = response;
|
||||
// restore timeout
|
||||
iso14b_set_timeout(save_iso14b_timeout);
|
||||
}
|
||||
|
||||
// if we received an I- or R(ACK)-Block with a block number equal to the
|
||||
// current block number, toggle the current block number
|
||||
if (len >= 3 // PCB+CRC = 3 bytes
|
||||
&& ((data_bytes[0] & 0xC0) == 0 // I-Block
|
||||
|| (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0
|
||||
&& (data_bytes[0] & 0x01) == iso14b_pcb_blocknum) { // equal block numbers
|
||||
iso14b_pcb_blocknum ^= 1;
|
||||
}
|
||||
|
||||
// if we received I-block with chaining we need to send ACK and receive another block of data
|
||||
if (response)
|
||||
*response = data_bytes[0];
|
||||
|
||||
// crc check
|
||||
if (len >= 3 && !check_crc(CRC_14443_B, data_bytes, len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// VALIDATE CRC
|
||||
if (!check_crc(CRC_14443_B, response, retlen)) {
|
||||
if (DBGLEVEL > DBG_DEBUG) DbpString("CRC fail");
|
||||
return -2;
|
||||
}
|
||||
|
||||
return retlen;
|
||||
if (len) {
|
||||
// cut frame byte
|
||||
len -= 1;
|
||||
// memmove(data_bytes, data_bytes + 1, len);
|
||||
for (int i = 0; i < len; i++)
|
||||
data_bytes[i] = data_bytes[i + 1];
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1272,7 +1322,10 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) {
|
|||
int iso14443b_select_card(iso14b_card_select_t *card) {
|
||||
// WUPB command (including CRC)
|
||||
// Note: WUPB wakes up all tags, REQB doesn't wake up tags in HALT state
|
||||
static const uint8_t wupb[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 };
|
||||
// WUTB or REQB is denoted in the third byte, lower nibble. 0 vs 8
|
||||
//static const uint8_t wupb[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 };
|
||||
static const uint8_t wupb[] = { ISO14443B_REQB, 0x00, 0x00, 0x71, 0xff };
|
||||
|
||||
// ATTRIB command (with space for CRC)
|
||||
uint8_t attrib[] = { ISO14443B_ATTRIB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
|
@ -1350,7 +1403,7 @@ int iso14443b_select_card(iso14b_card_select_t *card) {
|
|||
}
|
||||
}
|
||||
// reset PCB block number
|
||||
pcb_blocknum = 0;
|
||||
iso14b_pcb_blocknum = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1720,7 +1773,7 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) {
|
|||
|
||||
if ((param & ISO14B_APDU) == ISO14B_APDU) {
|
||||
uint8_t buf[100] = {0};
|
||||
status = iso14443b_apdu(cmd, len, buf, sizeof(buf));
|
||||
status = iso14443b_apdu(cmd, len, (param & ISO14A_SEND_CHAINING), buf, sizeof(buf));
|
||||
reply_mix(CMD_HF_ISO14443B_COMMAND, status, status, 0, buf, status);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#endif
|
||||
|
||||
void iso14443b_setup(void);
|
||||
int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response, uint16_t respmaxlen);
|
||||
int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, uint8_t *response, uint16_t respmaxlen);
|
||||
|
||||
int iso14443b_select_card(iso14b_card_select_t *card);
|
||||
int iso14443b_select_card_srx(iso14b_card_select_t *card);
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "cmdhf14b.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include "fileutils.h"
|
||||
#include "cmdparser.h" // command_t
|
||||
#include "commonutil.h" // ARRAYLEN
|
||||
#include "comms.h" // clearCommandBuffer
|
||||
#include "emv/emvcore.h" // TLVPrintFromBuffer
|
||||
#include "cmdtrace.h"
|
||||
#include "cliparser.h"
|
||||
#include "crc16.h"
|
||||
#include "cmdhf14a.h"
|
||||
#include "protocols.h" // definitions of ISO14B/7816 protocol
|
||||
|
@ -24,6 +26,11 @@
|
|||
|
||||
#define TIMEOUT 2000
|
||||
|
||||
// iso14b apdu input frame length
|
||||
static uint16_t apdu_frame_length = 0;
|
||||
uint16_t ats_fsc[] = {16, 24, 32, 40, 48, 64, 96, 128, 256};
|
||||
bool apdu_in_framing_enable = true;
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
static int usage_hf_14b_info(void) {
|
||||
|
@ -145,7 +152,6 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) {
|
|||
static bool waitCmd14b(bool verbose) {
|
||||
|
||||
PacketResponseNG resp;
|
||||
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) {
|
||||
|
||||
uint16_t len = (resp.oldarg[1] & 0xFFFF);
|
||||
|
@ -1270,6 +1276,336 @@ static int srix4kValid(const char *Cmd) {
|
|||
}
|
||||
*/
|
||||
|
||||
static int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card) {
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (card)
|
||||
memset(card, 0, sizeof(iso14b_card_select_t));
|
||||
|
||||
switch_off_field_14b();
|
||||
|
||||
// Anticollision + SELECT STD card
|
||||
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD, 0, 0, NULL, 0);
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) {
|
||||
PrintAndLogEx(INFO, "Trying 14B Select SR");
|
||||
|
||||
// Anticollision + SELECT SR card
|
||||
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0);
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) {
|
||||
PrintAndLogEx(ERR, "connection timeout");
|
||||
switch_off_field_14b();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
}
|
||||
|
||||
// check result
|
||||
int status = resp.oldarg[0];
|
||||
if (status < 0) {
|
||||
PrintAndLogEx(ERR, "No card in field.");
|
||||
switch_off_field_14b();
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
apdu_frame_length = 0;
|
||||
// get frame length from ATS in card data structure
|
||||
iso14b_card_select_t *vcard = (iso14b_card_select_t *) resp.data.asBytes;
|
||||
// uint8_t fsci = vcard->atqb[1] & 0x0f;
|
||||
// if (fsci < ARRAYLEN(ats_fsc)) {
|
||||
// apdu_frame_length = ats_fsc[fsci];
|
||||
// }
|
||||
|
||||
if (card) {
|
||||
memcpy(card, vcard, sizeof(iso14b_card_select_t));
|
||||
}
|
||||
|
||||
if (disconnect) {
|
||||
switch_off_field_14b();
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chainingout) {
|
||||
*chainingout = false;
|
||||
|
||||
if (activateField) {
|
||||
// select with no disconnect and set frameLength
|
||||
int selres = select_card_14443b_4(false, NULL);
|
||||
if (selres != PM3_SUCCESS)
|
||||
return selres;
|
||||
}
|
||||
|
||||
uint16_t flags = 0;
|
||||
|
||||
// Don't support 14B chaining yet
|
||||
if (chainingin)
|
||||
flags = ISO14B_SEND_CHAINING;
|
||||
|
||||
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
|
||||
// https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size
|
||||
// here length PM3_CMD_DATA_SIZE=512
|
||||
// timeout must be authomatically set by "get ATS"
|
||||
if (datain)
|
||||
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, (datainlen & 0xFFFF), 0, datain, datainlen & 0xFFFF);
|
||||
else
|
||||
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, 0, 0, NULL, 0);
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) {
|
||||
uint8_t *recv = resp.data.asBytes;
|
||||
int iLen = resp.oldarg[0];
|
||||
uint8_t res = resp.oldarg[1];
|
||||
|
||||
int dlen = iLen - 2;
|
||||
if (dlen < 0) {
|
||||
dlen = 0;
|
||||
}
|
||||
*dataoutlen += dlen;
|
||||
|
||||
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
|
||||
PrintAndLogEx(ERR, "APDU: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// I-block ACK
|
||||
if ((res & 0xf2) == 0xa2) {
|
||||
*dataoutlen = 0;
|
||||
*chainingout = true;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (!iLen) {
|
||||
PrintAndLogEx(ERR, "APDU: No APDU response.");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// check apdu length
|
||||
if (iLen < 2 && iLen >= 0) {
|
||||
PrintAndLogEx(ERR, "APDU: Small APDU response. Len=%d", iLen);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// check block TODO
|
||||
if (iLen == -2) {
|
||||
PrintAndLogEx(ERR, "APDU: Block type mismatch.");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
memcpy(dataout, recv, dlen);
|
||||
|
||||
// chaining
|
||||
if ((res & 0x10) != 0) {
|
||||
*chainingout = true;
|
||||
}
|
||||
|
||||
// CRC Check
|
||||
if (iLen == -1) {
|
||||
PrintAndLogEx(ERR, "APDU: ISO 14443A CRC error.");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
} else {
|
||||
PrintAndLogEx(ERR, "APDU: Reply timeout.");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
|
||||
*dataoutlen = 0;
|
||||
bool chaining = false;
|
||||
int res;
|
||||
|
||||
// 3 byte here - 1b framing header, 2b crc16
|
||||
if (apdu_in_framing_enable &&
|
||||
((apdu_frame_length && (datainlen > apdu_frame_length - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) {
|
||||
int clen = 0;
|
||||
|
||||
bool v_activate_field = activate_field;
|
||||
|
||||
do {
|
||||
int vlen = MIN(apdu_frame_length - 3, datainlen - clen);
|
||||
bool chainBlockNotLast = ((clen + vlen) < datainlen);
|
||||
|
||||
*dataoutlen = 0;
|
||||
res = handle_14b_apdu(chainBlockNotLast, &datain[clen], vlen, v_activate_field, dataout, maxdataoutlen, dataoutlen, &chaining);
|
||||
if (res) {
|
||||
if (leave_signal_on == false)
|
||||
switch_off_field_14b();
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
// check R-block ACK
|
||||
//TODO check this one...
|
||||
if ((*dataoutlen == 0) && (*dataoutlen != 0 || chaining != chainBlockNotLast)) { // *dataoutlen!=0. 'A && (!A || B)' is equivalent to 'A && B'
|
||||
if (leave_signal_on == false) {
|
||||
switch_off_field_14b();
|
||||
}
|
||||
return 201;
|
||||
}
|
||||
|
||||
clen += vlen;
|
||||
v_activate_field = false;
|
||||
if (*dataoutlen) {
|
||||
if (clen != datainlen)
|
||||
PrintAndLogEx(ERR, "APDU: I-block/R-block sequence error. Data len=%d, Sent=%d, Last packet len=%d", datainlen, clen, *dataoutlen);
|
||||
break;
|
||||
}
|
||||
} while (clen < datainlen);
|
||||
|
||||
} else {
|
||||
|
||||
res = handle_14b_apdu(false, datain, datainlen, activate_field, dataout, maxdataoutlen, dataoutlen, &chaining);
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (leave_signal_on == false) {
|
||||
switch_off_field_14b();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
while (chaining) {
|
||||
// I-block with chaining
|
||||
res = handle_14b_apdu(false, NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining);
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (leave_signal_on == false) {
|
||||
switch_off_field_14b();
|
||||
}
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (leave_signal_on == false) {
|
||||
switch_off_field_14b();
|
||||
}
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// ISO14443-4. 7. Half-duplex block transmission protocol
|
||||
static int CmdHF14BAPDU(const char *Cmd) {
|
||||
uint8_t data[PM3_CMD_DATA_SIZE];
|
||||
int datalen = 0;
|
||||
uint8_t header[PM3_CMD_DATA_SIZE];
|
||||
int headerlen = 0;
|
||||
bool activate_field = false;
|
||||
bool leave_signal_on = false;
|
||||
bool decode_TLV = false;
|
||||
bool decode_APDU = false;
|
||||
bool make_APDU = false;
|
||||
bool extended_APDU = false;
|
||||
int le = 0;
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf 14b apdu",
|
||||
"Sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL). works with all apdu types from ISO 7816-4:2013",
|
||||
"hf 14b apdu -s 94a40800043f000002\n"
|
||||
"hf 14b apdu -sd 00A404000E325041592E5359532E444446303100 -> decode apdu\n"
|
||||
"hf 14b apdu -sm 00A40400 325041592E5359532E4444463031 -l 256 -> encode standard apdu\n"
|
||||
"hf 14b apdu -sm 00A40400 325041592E5359532E4444463031 -el 65536 -> encode extended apdu\n");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0("s", "select", "activate field and select card"),
|
||||
arg_lit0("k", "keep", "leave the signal field ON after receive response"),
|
||||
arg_lit0("t", "tlv", "executes TLV decoder if it possible"),
|
||||
arg_lit0("d", "decode", "decode apdu request if it possible"),
|
||||
arg_str0("m", "make", "<head (CLA INS P1 P2) hex>", "make apdu with head from this field and data from data field. Must be 4 bytes length: <CLA INS P1 P2>"),
|
||||
arg_lit0("e", "extended", "make extended length apdu if `m` parameter included"),
|
||||
arg_int0("l", "le", "<Le (int)>", "Le apdu parameter if `m` parameter included"),
|
||||
arg_strx1(NULL, NULL, "<APDU (hex) | data (hex)>", "data if `m` parameter included"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
||||
activate_field = arg_get_lit(ctx, 1);
|
||||
leave_signal_on = arg_get_lit(ctx, 2);
|
||||
decode_TLV = arg_get_lit(ctx, 3);
|
||||
decode_APDU = arg_get_lit(ctx, 4);
|
||||
|
||||
CLIGetHexWithReturn(ctx, 5, header, &headerlen);
|
||||
make_APDU = headerlen > 0;
|
||||
if (make_APDU && headerlen != 4) {
|
||||
PrintAndLogEx(ERR, "header length must be 4 bytes instead of %d", headerlen);
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
extended_APDU = arg_get_lit(ctx, 6);
|
||||
le = arg_get_int_def(ctx, 7, 0);
|
||||
|
||||
if (make_APDU) {
|
||||
uint8_t apdudata[PM3_CMD_DATA_SIZE] = {0};
|
||||
int apdudatalen = 0;
|
||||
|
||||
CLIGetHexBLessWithReturn(ctx, 8, apdudata, &apdudatalen, 1 + 2);
|
||||
|
||||
APDUStruct apdu;
|
||||
apdu.cla = header[0];
|
||||
apdu.ins = header[1];
|
||||
apdu.p1 = header[2];
|
||||
apdu.p2 = header[3];
|
||||
|
||||
apdu.lc = apdudatalen;
|
||||
apdu.data = apdudata;
|
||||
|
||||
apdu.extended_apdu = extended_APDU;
|
||||
apdu.le = le;
|
||||
|
||||
if (APDUEncode(&apdu, data, &datalen)) {
|
||||
PrintAndLogEx(ERR, "can't make apdu with provided parameters.");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (extended_APDU) {
|
||||
PrintAndLogEx(ERR, "make mode not set but here `e` option.");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (le > 0) {
|
||||
PrintAndLogEx(ERR, "make mode not set but here `l` option.");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
// len = data + PCB(1b) + CRC(2b)
|
||||
CLIGetHexBLessWithReturn(ctx, 8, data, &datalen, 1 + 2);
|
||||
}
|
||||
CLIParserFree(ctx);
|
||||
|
||||
PrintAndLogEx(NORMAL, ">>>>[%s%s%s] %s",
|
||||
activate_field ? "sel " : "",
|
||||
leave_signal_on ? "keep " : "",
|
||||
decode_TLV ? "TLV" : "",
|
||||
sprint_hex(data, datalen)
|
||||
);
|
||||
|
||||
if (decode_APDU) {
|
||||
APDUStruct apdu;
|
||||
if (APDUDecode(data, datalen, &apdu) == 0)
|
||||
APDUPrint(apdu);
|
||||
else
|
||||
PrintAndLogEx(WARNING, "can't decode APDU.");
|
||||
}
|
||||
|
||||
int res = exchange_14b_apdu(data, datalen, activate_field, leave_signal_on, data, PM3_CMD_DATA_SIZE, &datalen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "<<<< %s", sprint_hex(data, datalen));
|
||||
PrintAndLogEx(SUCCESS, "APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1]));
|
||||
|
||||
// TLV decoder
|
||||
if (decode_TLV && datalen > 4) {
|
||||
TLVPrintFromBuffer(data, datalen - 2);
|
||||
}
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHF14BNdef(const char *Cmd) {
|
||||
char c = tolower(param_getchar(Cmd, 0));
|
||||
if (c == 'h' || c == 0x00) return usage_hf_14b_ndef();
|
||||
|
@ -1352,6 +1688,7 @@ static int CmdHF14BNdef(const char *Cmd) {
|
|||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"apdu", CmdHF14BAPDU, IfPm3Iso14443b, "Send ISO 14443-4 APDU to tag"},
|
||||
{"dump", CmdHF14BDump, IfPm3Iso14443b, "Read all memory pages of an ISO14443-B tag, save to file"},
|
||||
{"info", CmdHF14Binfo, IfPm3Iso14443b, "Tag information"},
|
||||
{"list", CmdHF14BList, AlwaysAvailable, "List ISO 14443B history"},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue