mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-08-27 22:34:58 +00:00
Merge pull request #3389 from kormax/hf-14b-info-innovatron
Add support for detecting Innovatron protocol in 'hf 14b info'
This commit is contained in:
@@ -3,6 +3,7 @@ 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]
|
||||
- Add support for Innovatron protocol detection to `hf 14b info` (@kormax)
|
||||
- Improved `lf cotag reader` and `lf cotag demod`: Reimplementation and enhancement of proxmark3 COTAG support
|
||||
- Added `hf felica sim` command (@kormax)
|
||||
- Added `mad read`, `mad write`, `mad verify`, `mad decode`, `mad encode` commands with typed struct MAD API (@AlxCzl)
|
||||
|
||||
@@ -1919,6 +1919,74 @@ 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,
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
uint8_t r_repgen[PM3_CMD_DATA_SIZE] = { 0x00 };
|
||||
|
||||
AddCrc14B(apgen, sizeof(apgen) - 2);
|
||||
|
||||
uint32_t start_time = 0;
|
||||
uint32_t eof_time = 0;
|
||||
CodeAndTransmit14443bAsReader(apgen, sizeof(apgen), &start_time, &eof_time, true);
|
||||
|
||||
eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER;
|
||||
uint16_t retlen = 0;
|
||||
if (Get14443bAnswerFromTag(r_repgen, sizeof(r_repgen), s_iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
// REPGEN carries V&T address, REPGEN command, DIV, VerLog, then CRC.
|
||||
if (retlen < 9) {
|
||||
return PM3_ELENGTH;
|
||||
}
|
||||
|
||||
if (check_crc(CRC_14443_B, r_repgen, retlen) == false) {
|
||||
return PM3_ECRC;
|
||||
}
|
||||
|
||||
const uint16_t repgen_len = retlen - 2;
|
||||
if (r_repgen[0] != apgen[0] || r_repgen[1] != ISO14443B_PRIME_CMD_REPGEN) {
|
||||
return PM3_EWRONGANSWER;
|
||||
}
|
||||
|
||||
if (card) {
|
||||
card->vt_addr = r_repgen[0];
|
||||
card->repgen_cmd = r_repgen[1];
|
||||
memcpy(card->div, r_repgen + 2, sizeof(card->div));
|
||||
card->verlog = r_repgen[6];
|
||||
|
||||
uint16_t offset = 7;
|
||||
if ((card->verlog & 0x80) && repgen_len > offset) {
|
||||
card->config = r_repgen[offset++];
|
||||
if ((card->config & 0x40) && 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;
|
||||
}
|
||||
card->atr_len = (uint8_t)MIN(atr_len, ISO14B_PRIME_ATR_MAX_LEN);
|
||||
memcpy(card->atr, r_repgen + offset, card->atr_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_iso14b_pcb_blocknum = 0;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Xerox tag connect function: wup, anticoll, attrib, password
|
||||
// the original chips require all commands in this sequence
|
||||
|
||||
@@ -3148,6 +3216,15 @@ void SendRawCommand14443B(iso14b_raw_cmd_t *p) {
|
||||
if (status != PM3_SUCCESS) goto out;
|
||||
}
|
||||
|
||||
if ((p->flags & ISO14B_SELECT_PRIME) == ISO14B_SELECT_PRIME) {
|
||||
iso14b_prime_card_select_t *prime = (iso14b_prime_card_select_t *)buf;
|
||||
memset(prime, 0, sizeof(iso14b_prime_card_select_t));
|
||||
sendlen = sizeof(iso14b_prime_card_select_t);
|
||||
status = iso14443b_select_prime_card(prime);
|
||||
reply_ng(CMD_HF_ISO14443B_COMMAND, status, (uint8_t *)prime, sendlen);
|
||||
if (status != PM3_SUCCESS) goto out;
|
||||
}
|
||||
|
||||
// if field is off...
|
||||
if (
|
||||
((p->flags & ISO14B_APDU) == ISO14B_APDU) ||
|
||||
|
||||
@@ -767,6 +767,22 @@ static void print_ct_general_info(void *vcard) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
|
||||
static void print_prime_general_info(const iso14b_prime_card_select_t *card) {
|
||||
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, " 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);
|
||||
}
|
||||
if (card->atr_len) {
|
||||
PrintAndLogEx(SUCCESS, " ATR : %s", sprint_hex(card->atr, card->atr_len));
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
|
||||
static void print_hdr(void) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, " block# | data |lck| ascii");
|
||||
@@ -1323,6 +1339,63 @@ static bool HF14B_ST_Info(bool verbose, bool do_aid_search) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_prime_card(iso14b_prime_card_select_t *card, bool verbose) {
|
||||
|
||||
if (card == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
iso14b_raw_cmd_t packet = {
|
||||
.flags = (ISO14B_CONNECT | ISO14B_SELECT_PRIME | ISO14B_DISCONNECT),
|
||||
.timeout = 0,
|
||||
.rawlen = 0,
|
||||
};
|
||||
|
||||
clearCommandBuffer();
|
||||
PacketResponseNG resp;
|
||||
SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t));
|
||||
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) {
|
||||
if (verbose) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (resp.status) {
|
||||
case PM3_SUCCESS: {
|
||||
if (resp.length < sizeof(*card)) {
|
||||
if (verbose) {
|
||||
PrintAndLogEx(FAILED, "ISO 14443-B' card select response too short (%u bytes)", resp.length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(card, resp.data.asBytes, sizeof(*card));
|
||||
if (card->repgen_cmd != ISO14443B_PRIME_CMD_REPGEN || card->atr_len > sizeof(card->atr)) {
|
||||
if (verbose) {
|
||||
PrintAndLogEx(FAILED, "ISO 14443-B' invalid card select response");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case PM3_ELENGTH:
|
||||
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-B' REPGEN wrong length");
|
||||
break;
|
||||
case PM3_ECRC:
|
||||
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-B' REPGEN CRC fail");
|
||||
break;
|
||||
case PM3_EWRONGANSWER:
|
||||
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-B' REPGEN wrong answer");
|
||||
break;
|
||||
default:
|
||||
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-B' APGEN failed");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// menu command to get and print all info known about any known 14b tag
|
||||
static int CmdHF14Binfo(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
@@ -1580,6 +1653,16 @@ static bool HF14B_picopass_reader(bool verbose) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HF14B_prime_reader(bool verbose) {
|
||||
|
||||
iso14b_prime_card_select_t card = {0};
|
||||
if (get_prime_card(&card, verbose) == false) {
|
||||
return false;
|
||||
}
|
||||
print_prime_general_info(&card);
|
||||
return true;
|
||||
}
|
||||
|
||||
// test for other 14b type tags (mimic another reader - don't have tags to identify)
|
||||
static bool HF14B_other_reader(bool verbose) {
|
||||
|
||||
@@ -3169,6 +3252,10 @@ int infoHF14B(bool verbose, bool do_aid_search) {
|
||||
if (HF14B_ST_Info(verbose, do_aid_search))
|
||||
return PM3_SUCCESS;
|
||||
|
||||
// try Type B' / Innovatron APGEN
|
||||
if (HF14B_prime_reader(verbose))
|
||||
return PM3_SUCCESS;
|
||||
|
||||
// try unknown 14b read commands (to be identified later)
|
||||
// could be read of calypso, CEPAS, moneo, or pico pass.
|
||||
if (verbose) {
|
||||
@@ -3204,6 +3291,11 @@ int readHF14B(bool loop, bool verbose, bool read_plot) {
|
||||
if (found)
|
||||
goto plot;
|
||||
|
||||
// try Type B' / Innovatron APGEN
|
||||
found |= HF14B_prime_reader(verbose);
|
||||
if (found)
|
||||
goto plot;
|
||||
|
||||
// try unknown 14b read commands (to be identified later)
|
||||
// could be read of calypso, CEPAS, moneo, or pico pass.
|
||||
found |= HF14B_other_reader(verbose);
|
||||
|
||||
+13
-1
@@ -34,6 +34,18 @@ typedef struct {
|
||||
uint8_t fc;
|
||||
} PACKED iso14b_cts_card_select_t;
|
||||
|
||||
#define ISO14B_PRIME_DIV_LEN 4
|
||||
#define ISO14B_PRIME_ATR_MAX_LEN 33
|
||||
typedef struct {
|
||||
uint8_t vt_addr;
|
||||
uint8_t repgen_cmd;
|
||||
uint8_t div[ISO14B_PRIME_DIV_LEN];
|
||||
uint8_t verlog;
|
||||
uint8_t config;
|
||||
uint8_t atr_len;
|
||||
uint8_t atr[ISO14B_PRIME_ATR_MAX_LEN];
|
||||
} PACKED iso14b_prime_card_select_t;
|
||||
|
||||
typedef enum ISO14B_COMMAND {
|
||||
ISO14B_CONNECT = (1 << 0),
|
||||
ISO14B_DISCONNECT = (1 << 1),
|
||||
@@ -49,6 +61,7 @@ typedef enum ISO14B_COMMAND {
|
||||
ISO14B_CLEARTRACE = (1 << 11),
|
||||
ISO14B_SELECT_XRX = (1 << 12),
|
||||
ISO14B_SELECT_PICOPASS = (1 << 13),
|
||||
ISO14B_SELECT_PRIME = (1 << 14),
|
||||
} iso14b_command_t;
|
||||
|
||||
typedef enum ISO14B_TYPE {
|
||||
@@ -85,4 +98,3 @@ typedef struct {
|
||||
#define US_TO_ETU(x) ( (float)((x) / 9.4396) )
|
||||
|
||||
#endif // _ISO14B_H_
|
||||
|
||||
|
||||
@@ -328,6 +328,41 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
|
||||
#define ISO14443B_PING 0xBA
|
||||
#define ISO14443B_PONG 0xAB
|
||||
|
||||
/*
|
||||
* 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 1: payload type / command.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* 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
|
||||
|
||||
// XEROX Commands
|
||||
#define ISO14443B_XEROX_PWD 0x38
|
||||
#define ISO14443B_XEROX_WUP1 0x0D
|
||||
|
||||
Reference in New Issue
Block a user