Refine Innovatron protocol handling

This commit is contained in:
kormax
2026-06-26 23:20:05 +03:00
parent 2655037a9f
commit 9d7447ddcd
3 changed files with 94 additions and 50 deletions
+6 -11
View File
@@ -1925,19 +1925,14 @@ int iso14443b_select_srx_card(iso14b_card_select_t *card) {
return PM3_SUCCESS;
}
/**
* Type B' / Innovatron APGEN.
*/
static int iso14443b_select_prime_card(iso14b_prime_card_select_t *card) {
uint8_t apgen[] = {
ISO14443B_PRIME_VT_ADDR_DEFAULT,
ISO14443B_PRIME_CMD_APGEN,
// Seems to affect time slots / response chance:
// 0x3f card responds every time
// 0x3e-0x00 reduced success rate
// 0x40-0xff card does not respond
0x3f,
ISO14443B_PRIME_REQUEST_EXTENDED_REPGEN,
// OccuPar 0x3F is the default value used by most readers in real installations.
ISO14443B_PRIME_OCCUPAR_DEFAULT,
// APGEN Config bit 7 requests the long REPGEN form with CONFIG/ATR.
ISO14443B_PRIME_APGEN_CONFIG_REQUEST_ATR,
0x00,
0x00
};
@@ -1976,9 +1971,9 @@ static int iso14443b_select_prime_card(iso14b_prime_card_select_t *card) {
card->verlog = r_repgen[6];
uint16_t offset = 7;
if ((card->verlog & 0x80) && repgen_len > offset) {
if ((card->verlog & ISO14443B_PRIME_VERLOG_LONG_REPGEN) && repgen_len > offset) {
card->config = r_repgen[offset++];
if ((card->config & 0x40) && repgen_len > offset) {
if ((card->config & ISO14443B_PRIME_CONFIG_ATR_PRESENT) && repgen_len > offset) {
uint16_t atr_len = repgen_len - offset;
if (atr_len >= 2 && r_repgen[repgen_len - 2] == 0x90 && r_repgen[repgen_len - 1] == 0x00) {
atr_len -= 2;
+22 -12
View File
@@ -62,7 +62,7 @@
// iso14b apdu input frame length
static uint16_t apdu_frame_length = 0;
static uint8_t prime_vt_addr = ISO14443B_PRIME_VT_ADDR_DEFAULT;
static uint8_t prime_com_ra_cmd = ISO14443B_PRIME_COM_RA_START;
static uint8_t prime_frame_seq = ISO14443B_PRIME_SEQUENCE_START;
//static uint16_t ats_fsc[] = {16, 24, 32, 40, 48, 64, 96, 128, 256};
static bool apdu_in_framing_enable = true;
@@ -70,6 +70,8 @@ static int CmdHelp(const char *Cmd);
static int switch_off_field_14b(void) {
SetISODEPState(ISODEP_INACTIVE);
prime_vt_addr = ISO14443B_PRIME_VT_ADDR_DEFAULT;
prime_frame_seq = ISO14443B_PRIME_SEQUENCE_START;
iso14b_raw_cmd_t packet = {
.flags = ISO14B_DISCONNECT,
.timeout = 0,
@@ -770,14 +772,22 @@ static void print_ct_general_info(void *vcard) {
}
static void print_prime_general_info(const iso14b_prime_card_select_t *card) {
const uint8_t vt_card_addr = (card->vt_addr & ISO14443B_PRIME_VT_CARD_ADDR_MASK) >> ISO14443B_PRIME_VT_CARD_ADDR_SHIFT;
const uint8_t vt_coupler_addr = card->vt_addr & ISO14443B_PRIME_VT_COUPLER_ADDR_MASK;
const bool long_repgen = (card->verlog & ISO14443B_PRIME_VERLOG_LONG_REPGEN) == ISO14443B_PRIME_VERLOG_LONG_REPGEN;
const uint8_t software_version = (card->verlog & ISO14443B_PRIME_VERLOG_VERSION_MASK) >> ISO14443B_PRIME_VERLOG_VERSION_SHIFT;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Type B' / Innovatron") " ---------------------");
PrintAndLogEx(SUCCESS, " V&T Ad : %02X", card->vt_addr);
PrintAndLogEx(SUCCESS, " Cmd : %02X (REPGEN)", card->repgen_cmd);
PrintAndLogEx(SUCCESS, " V&T Ad : %02X (card %u, coupler %u)", card->vt_addr, vt_card_addr, vt_coupler_addr);
PrintAndLogEx(SUCCESS, " DIV : " _GREEN_("%s"), sprint_hex(card->div, sizeof(card->div)));
PrintAndLogEx(SUCCESS, " VerLog : %02X", card->verlog);
if (card->verlog & 0x80) {
PrintAndLogEx(SUCCESS, " Config : %02X", card->config);
PrintAndLogEx(SUCCESS, " VerLog : %02X (%s REPGEN, software version %u)",
card->verlog, long_repgen ? "long" : "short", software_version);
if (long_repgen) {
PrintAndLogEx(SUCCESS, " Config : %02X (WAIT %s, ATR %s)",
card->config,
(card->config & ISO14443B_PRIME_CONFIG_WAIT_SUPPORTED) ? "supported" : "not indicated",
(card->config & ISO14443B_PRIME_CONFIG_ATR_PRESENT) ? "present" : "absent");
}
if (card->atr_len) {
PrintAndLogEx(SUCCESS, " ATR : %s", sprint_hex(card->atr, card->atr_len));
@@ -1382,7 +1392,7 @@ int select_card_14443b_prime(bool disconnect, iso14b_prime_card_select_t *card,
}
prime_vt_addr = selected.vt_addr;
prime_com_ra_cmd = ISO14443B_PRIME_COM_RA_START;
prime_frame_seq = ISO14443B_PRIME_SEQUENCE_START;
SetISODEPState(disconnect ? ISODEP_INACTIVE : ISODEP_NFCB_PRIME);
if (card) {
*card = selected;
@@ -2607,8 +2617,8 @@ int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field,
return PM3_SUCCESS;
}
static uint8_t next_prime_com_ra_cmd(uint8_t cmd) {
return (cmd + 0x02) & 0x0F;
static uint8_t next_prime_frame_seq(uint8_t seq) {
return seq >= ISO14443B_PRIME_SEQUENCE_END ? ISO14443B_PRIME_SEQUENCE_START : seq + 1;
}
int exchange_14b_prime_apdu(uint8_t *datain, int datainlen, bool activate_field,
@@ -2634,7 +2644,7 @@ int exchange_14b_prime_apdu(uint8_t *datain, int datainlen, bool activate_field,
}
frame[0] = prime_vt_addr;
frame[1] = prime_com_ra_cmd;
frame[1] = ISO14443B_PRIME_CMD_COM_R(prime_frame_seq);
frame[2] = (uint8_t)datainlen + 1;
if (datainlen > 0) {
memcpy(frame + 3, datain, datainlen);
@@ -2692,7 +2702,7 @@ int exchange_14b_prime_apdu(uint8_t *datain, int datainlen, bool activate_field,
}
const uint8_t rx_len = rx[2];
if (rx[0] != prime_vt_addr || (rx[1] & 0x01) || rx_len == 0 || resp.length < (uint16_t)rx_len + 4) {
if (rx[0] != prime_vt_addr || rx[1] != ISO14443B_PRIME_CMD_REC(prime_frame_seq) || rx_len == 0 || resp.length < (uint16_t)rx_len + 4) {
if (leave_signal_on == false) {
switch_off_field_14b();
}
@@ -2710,7 +2720,7 @@ int exchange_14b_prime_apdu(uint8_t *datain, int datainlen, bool activate_field,
memcpy(dataout, rx + 3, apdu_len);
*dataoutlen = apdu_len;
prime_com_ra_cmd = next_prime_com_ra_cmd(prime_com_ra_cmd);
prime_frame_seq = next_prime_frame_seq(prime_frame_seq);
if (leave_signal_on == false) {
switch_off_field_14b();
+66 -27
View File
@@ -331,38 +331,77 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
/*
* Type B' / Innovatron frame format.
*
* Byte 0: V&T address. Public docs and traces label this "V&T Ad".
* 0x01 is the default address observed in APGEN, REPGEN,
* ATTRIB, COM_RA, and DISC frames.
* Observed cards respond to any value from 0x00 to 0xFF, and
* echo that same address in the response.
* Byte 0 is V&T Ad. The high nibble is the card/tag address and the low
* nibble is the coupler/validator address. APGEN uses card address 0;
*
* Byte 1: payload type / command.
* APGEN: [V&T Ad, 0B, OccuPar] or [V&T Ad, 0B, OccuPar, Config]
* REPGEN: [V&T Ad, 07, DIV(4), VERLOG] or
* [V&T Ad, 07, DIV(4), VERLOG, CONFIG, ATR/application bytes...]
* ATTRIB: [V&T Ad, 0F, DIV(4)] -> RR
* DISC: [V&T Ad, 03] -> RR
* RR: [V&T Ad, 01]
*
* Primary commands:
* 0x0B APGEN "Appel General"; acts as the wake-up command.
* 0x07 REPGEN response to APGEN.
* 0x0F ATTRIB attribute / activation command.
* 0x03 DISC disconnect.
* Data exchange commands:
* family | (seq << 1), with bit 0 clear and seq carried in bits 1..3.
* COM_RA C2..CE allows assigning card address and performing APDU exchange by DIV.
* COM_R 02..0E is the normal APDU exchange command, relying on card address only.
* REC 42..4E is the card response to COM_RA/COM_R.
* Public PICS rows list seq 1..7.
*
* COM_RA frames:
* COM_RA uses the even-valued payload type bytes. Bit 0 is clear; bits 1..3
* are the rolling frame counter. The resulting byte advances by 0x02 for
* each exchange and wraps in the low nibble:
* Byte 2 in data frames is LNG. LNG includes byte 2 itself, so the APDU/R-APDU
* byte count is LNG - 1. In COM_RA the trailing DIV is outside LNG.
*
* 04 -> 06 -> 08 -> 0A -> 0C -> 0E -> 00 -> 02 -> 04 ...
*
* Byte 2 is the COM_RA length byte. The length includes byte 2 itself, so
* the number of bytes after byte 2 is length - 1.
*/
#define ISO14443B_PRIME_VT_ADDR_DEFAULT 0x01
#define ISO14443B_PRIME_CMD_DISC 0x03
#define ISO14443B_PRIME_CMD_REPGEN 0x07
#define ISO14443B_PRIME_CMD_APGEN 0x0B
#define ISO14443B_PRIME_CMD_ATTRIB 0x0F
// APGEN parameter requesting the extended REPGEN response; also called 'APGEN!'
#define ISO14443B_PRIME_REQUEST_EXTENDED_REPGEN 0x80
#define ISO14443B_PRIME_COM_RA_START 0x02
#define ISO14443B_PRIME_VT_ADDR_DEFAULT 0x01
#define ISO14443B_PRIME_VT_CARD_ADDR_MASK 0xF0
#define ISO14443B_PRIME_VT_CARD_ADDR_SHIFT 4
#define ISO14443B_PRIME_VT_COUPLER_ADDR_MASK 0x0F
#define ISO14443B_PRIME_VT_CARD_ADDR_FIRST 1
#define ISO14443B_PRIME_VT_ADDR(card, coupler) ((((card) << ISO14443B_PRIME_VT_CARD_ADDR_SHIFT) & ISO14443B_PRIME_VT_CARD_ADDR_MASK) | ((coupler) & ISO14443B_PRIME_VT_COUPLER_ADDR_MASK))
#define ISO14443B_PRIME_CMD_RR 0x01
#define ISO14443B_PRIME_CMD_DISC 0x03
#define ISO14443B_PRIME_CMD_REPGEN 0x07
#define ISO14443B_PRIME_CMD_APGEN 0x0B
#define ISO14443B_PRIME_CMD_ATTRIB 0x0F
#define ISO14443B_PRIME_OCCUPAR_VALUE_MASK 0x3F
// Public documents and sources identify bit 6 as short APGEN;
// it's not proven to work yet
#define ISO14443B_PRIME_OCCUPAR_SHORT_APGEN 0x40
#define ISO14443B_PRIME_OCCUPAR_HIGH_BITS_MASK 0xC0
#define ISO14443B_PRIME_OCCUPAR_BPSK_FLAG 0x80
// OccuPar controls APGEN response chance:
// 0x3F card answers every poll attempt
// 0x3E-0x00 lower values reduce the response chance
#define ISO14443B_PRIME_OCCUPAR_DEFAULT 0x3F
#define ISO14443B_PRIME_APGEN_CONFIG_REQUEST_ATR 0x80
#define ISO14443B_PRIME_APGEN_CONFIG_RFU_MASK 0x7F
#define ISO14443B_PRIME_VERLOG_LONG_REPGEN 0x80
#define ISO14443B_PRIME_VERLOG_FIXED_BITS_MASK 0x61
#define ISO14443B_PRIME_VERLOG_FIXED_BITS_VALUE 0x61
#define ISO14443B_PRIME_VERLOG_VERSION_MASK 0x1E
#define ISO14443B_PRIME_VERLOG_VERSION_SHIFT 1
#define ISO14443B_PRIME_CONFIG_WAIT_SUPPORTED 0x80
#define ISO14443B_PRIME_CONFIG_ATR_PRESENT 0x40
#define ISO14443B_PRIME_CONFIG_RFU_MASK 0x3F
#define ISO14443B_PRIME_CMD_COM_DATA_FAMILY_MASK 0xC0
#define ISO14443B_PRIME_CMD_COM_DATA_SEQ_MASK 0x0E
#define ISO14443B_PRIME_CMD_COM_DATA_SEQ_SHIFT 1
#define ISO14443B_PRIME_CMD_COM_DATA_FAMILY_COM_R 0x00
#define ISO14443B_PRIME_CMD_COM_DATA_FAMILY_REC 0x40
#define ISO14443B_PRIME_CMD_COM_DATA_FAMILY_COM_RA 0xC0
#define ISO14443B_PRIME_SEQUENCE_START 1
#define ISO14443B_PRIME_SEQUENCE_END 7
#define ISO14443B_PRIME_CMD_COM_DATA_SEQ(cmd) (((cmd) & ISO14443B_PRIME_CMD_COM_DATA_SEQ_MASK) >> ISO14443B_PRIME_CMD_COM_DATA_SEQ_SHIFT)
#define ISO14443B_PRIME_CMD_COM_R(seq) (ISO14443B_PRIME_CMD_COM_DATA_FAMILY_COM_R | (((seq) << ISO14443B_PRIME_CMD_COM_DATA_SEQ_SHIFT) & ISO14443B_PRIME_CMD_COM_DATA_SEQ_MASK))
#define ISO14443B_PRIME_CMD_REC(seq) (ISO14443B_PRIME_CMD_COM_DATA_FAMILY_REC | (((seq) << ISO14443B_PRIME_CMD_COM_DATA_SEQ_SHIFT) & ISO14443B_PRIME_CMD_COM_DATA_SEQ_MASK))
#define ISO14443B_PRIME_CMD_COM_RA(seq) (ISO14443B_PRIME_CMD_COM_DATA_FAMILY_COM_RA | (((seq) << ISO14443B_PRIME_CMD_COM_DATA_SEQ_SHIFT) & ISO14443B_PRIME_CMD_COM_DATA_SEQ_MASK))
#define ISO14443B_PRIME_COM_RA_START ISO14443B_PRIME_CMD_COM_RA(ISO14443B_PRIME_SEQUENCE_START)
// XEROX Commands
#define ISO14443B_XEROX_PWD 0x38