Add support for Innovatron protocol to Calypso commands

This commit is contained in:
kormax
2026-06-18 21:13:47 +03:00
parent 4416d6da3c
commit 1e254bc7a1
8 changed files with 193 additions and 18 deletions
+21 -1
View File
@@ -1440,7 +1440,13 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t
if (Handle14443bSamplesFromTag(ci, cq)) {
*eof_time = GetCountSspClkDelta(dma_start_time) - DELAY_TAG_TO_ARM; // end of EOF
// Response timing is measured from DMA start, but trace rows use
// absolute SSP time like reader frames.
uint32_t eof_delta = GetCountSspClkDelta(dma_start_time);
if (eof_delta > DELAY_TAG_TO_ARM) {
eof_delta -= DELAY_TAG_TO_ARM;
}
*eof_time = dma_start_time + eof_delta; // end of EOF
if (Demod.len > Demod.max_len) {
ret = PM3_EOVFLOW;
@@ -1983,6 +1989,20 @@ static int iso14443b_select_prime_card(iso14b_prime_card_select_t *card) {
}
}
uint8_t attrib[] = {
r_repgen[0],
ISO14443B_PRIME_CMD_ATTRIB,
r_repgen[2],
r_repgen[3],
r_repgen[4],
r_repgen[5],
0x00,
0x00,
};
AddCrc14B(attrib, sizeof(attrib) - 2);
start_time = eof_time + ISO14B_TR2;
CodeAndTransmit14443bAsReader(attrib, sizeof(attrib), &start_time, &eof_time, true);
s_iso14b_pcb_blocknum = 0;
return PM3_SUCCESS;
}
+137 -15
View File
@@ -61,6 +61,8 @@
// 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 uint16_t ats_fsc[] = {16, 24, 32, 40, 48, 64, 96, 128, 256};
static bool apdu_in_framing_enable = true;
@@ -1339,18 +1341,18 @@ 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;
}
int select_card_14443b_prime(bool disconnect, iso14b_prime_card_select_t *card, bool verbose) {
iso14b_raw_cmd_t packet = {
.flags = (ISO14B_CONNECT | ISO14B_SELECT_PRIME | ISO14B_DISCONNECT),
.flags = (ISO14B_CONNECT | ISO14B_SELECT_PRIME | ISO14B_CLEARTRACE),
.timeout = 0,
.rawlen = 0,
};
if (disconnect) {
packet.flags |= ISO14B_DISCONNECT;
}
clearCommandBuffer();
PacketResponseNG resp;
SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t));
@@ -1358,26 +1360,34 @@ static bool get_prime_card(iso14b_prime_card_select_t *card, bool verbose) {
if (verbose) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
}
return false;
return PM3_ETIMEOUT;
}
switch (resp.status) {
case PM3_SUCCESS: {
if (resp.length < sizeof(*card)) {
if (resp.length < sizeof(iso14b_prime_card_select_t)) {
if (verbose) {
PrintAndLogEx(FAILED, "ISO 14443-B' card select response too short (%u bytes)", resp.length);
}
return false;
return PM3_ELENGTH;
}
memcpy(card, resp.data.asBytes, sizeof(*card));
if (card->repgen_cmd != ISO14443B_PRIME_CMD_REPGEN || card->atr_len > sizeof(card->atr)) {
iso14b_prime_card_select_t selected = {0};
memcpy(&selected, resp.data.asBytes, sizeof(selected));
if (selected.repgen_cmd != ISO14443B_PRIME_CMD_REPGEN || selected.atr_len > sizeof(selected.atr)) {
if (verbose) {
PrintAndLogEx(FAILED, "ISO 14443-B' invalid card select response");
}
return false;
return PM3_EWRONGANSWER;
}
return true;
prime_vt_addr = selected.vt_addr;
prime_com_ra_cmd = ISO14443B_PRIME_COM_RA_START;
SetISODEPState(disconnect ? ISODEP_INACTIVE : ISODEP_NFCB_PRIME);
if (card) {
*card = selected;
}
return PM3_SUCCESS;
}
case PM3_ELENGTH:
if (verbose) PrintAndLogEx(FAILED, "ISO 14443-B' REPGEN wrong length");
@@ -1393,7 +1403,7 @@ static bool get_prime_card(iso14b_prime_card_select_t *card, bool verbose) {
break;
}
return false;
return resp.status;
}
// menu command to get and print all info known about any known 14b tag
@@ -1656,7 +1666,7 @@ static bool HF14B_picopass_reader(bool verbose) {
static bool HF14B_prime_reader(bool verbose) {
iso14b_prime_card_select_t card = {0};
if (get_prime_card(&card, verbose) == false) {
if (select_card_14443b_prime(true, &card, verbose) != PM3_SUCCESS) {
return false;
}
print_prime_general_info(&card);
@@ -2597,6 +2607,118 @@ 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;
}
int exchange_14b_prime_apdu(uint8_t *datain, int datainlen, bool activate_field,
bool leave_signal_on, uint8_t *dataout, int maxdataoutlen,
int *dataoutlen, int user_timeout) {
if (dataoutlen == NULL || dataout == NULL || datainlen < 0 || datainlen > 0xFE || (datain == NULL && datainlen > 0)) {
return PM3_EINVARG;
}
*dataoutlen = 0;
if (activate_field) {
int selres = select_card_14443b_prime(false, NULL, false);
if (selres != PM3_SUCCESS) {
return selres;
}
}
uint8_t frame[PM3_CMD_DATA_SIZE] = {0};
const uint16_t frame_len = (uint16_t)datainlen + 3;
if (frame_len > sizeof(frame)) {
return PM3_EINVARG;
}
frame[0] = prime_vt_addr;
frame[1] = prime_com_ra_cmd;
frame[2] = (uint8_t)datainlen + 1;
if (datainlen > 0) {
memcpy(frame + 3, datain, datainlen);
}
uint32_t flags = ISO14B_RAW | ISO14B_APPEND_CRC;
uint32_t timeout = 0;
if (user_timeout > 0) {
flags |= ISO14B_SET_TIMEOUT;
if (user_timeout > MAX_14B_TIMEOUT_MS) {
user_timeout = MAX_14B_TIMEOUT_MS;
PrintAndLogEx(INFO, "set timeout to 4.9 seconds. The max we can wait for response");
}
timeout = (uint32_t)((13560 / 128) * user_timeout);
}
iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + frame_len);
if (packet == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
return PM3_EMALLOC;
}
packet->flags = flags;
packet->timeout = timeout;
packet->rawlen = frame_len;
memcpy(packet->raw, frame, frame_len);
clearCommandBuffer();
SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen);
free(packet);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, MAX(APDU_TIMEOUT, user_timeout)) == false) {
if (leave_signal_on == false) {
switch_off_field_14b();
}
PrintAndLogEx(ERR, "APDU: reply timeout");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
if (leave_signal_on == false) {
switch_off_field_14b();
}
PrintAndLogEx(ERR, "APDU: no Type B' APDU response");
return resp.status;
}
const uint8_t *rx = resp.data.asBytes;
if (resp.length < 5 || check_crc(CRC_14443_B, rx, resp.length) == false) {
if (leave_signal_on == false) {
switch_off_field_14b();
}
return PM3_ECRC;
}
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 (leave_signal_on == false) {
switch_off_field_14b();
}
return PM3_EWRONGANSWER;
}
const int apdu_len = rx_len - 1;
if (maxdataoutlen && apdu_len > maxdataoutlen) {
if (leave_signal_on == false) {
switch_off_field_14b();
}
PrintAndLogEx(ERR, "APDU: buffer too small ( " _RED_("%d") " ), needs " _YELLOW_("%d") " bytes", maxdataoutlen, apdu_len);
return PM3_ESOFT;
}
memcpy(dataout, rx + 3, apdu_len);
*dataoutlen = apdu_len;
prime_com_ra_cmd = next_prime_com_ra_cmd(prime_com_ra_cmd);
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) {
CLIParserContext *ctx;
+2
View File
@@ -27,7 +27,9 @@ int CmdHF14BNdefRead(const char *Cmd);
uint8_t *get_uid_from_filename(const char *filename);
int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, int user_timeout);
int exchange_14b_prime_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, int user_timeout);
int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card);
int select_card_14443b_prime(bool disconnect, iso14b_prime_card_select_t *card, bool verbose);
int infoHF14B(bool verbose, bool do_aid_search);
int readHF14B(bool loop, bool verbose, bool read_plot);
+23 -1
View File
@@ -131,6 +131,8 @@ typedef struct {
iso14a_card_select_t card_a;
bool has_14b;
iso14b_card_select_t card_b;
bool has_prime;
iso14b_prime_card_select_t card_prime;
} calypso_rf_info_t;
typedef struct {
@@ -395,6 +397,9 @@ static const char *calypso_rf_protocol_desc(isodep_state_t protocol) {
if (protocol == ISODEP_NFCA) {
return "ISO14443-A";
}
if (protocol == ISODEP_NFCB_PRIME) {
return "ISO14443-B' / Innovatron";
}
if (protocol == ISODEP_NFCB) {
return "ISO14443-B";
}
@@ -430,6 +435,18 @@ static int calypso_connect_contactless(bool verbose, calypso_rf_info_t *rf) {
return PM3_SUCCESS;
}
iso14b_prime_card_select_t card_prime = {0};
res = select_card_14443b_prime(false, &card_prime, verbose);
if (res == PM3_SUCCESS) {
if (rf != NULL) {
rf->present = true;
rf->protocol = ISODEP_NFCB_PRIME;
rf->has_prime = true;
rf->card_prime = card_prime;
}
return PM3_SUCCESS;
}
return res;
}
@@ -2169,6 +2186,10 @@ static void calypso_print_rf_info(const calypso_rf_info_t *rf) {
} else if (rf->has_14b) {
calypso_print_rf_hex_line(" PUPI : ", rf->card_b.uid, rf->card_b.uidlen);
calypso_print_rf_hex_line(" ATQB : ", rf->card_b.atqb, sizeof(rf->card_b.atqb));
} else if (rf->has_prime) {
PrintAndLogEx(SUCCESS, " V&T Ad : " _GREEN_("%02X"), rf->card_prime.vt_addr);
calypso_print_rf_hex_line(" DIV : ", rf->card_prime.div, sizeof(rf->card_prime.div));
calypso_print_rf_hex_line(" ATR : ", rf->card_prime.atr, rf->card_prime.atr_len);
}
}
@@ -4494,7 +4515,8 @@ static int calypso_probe_setup(const calypso_select_result_t *selected, bool ver
uint8_t select_response[APDU_RES_LEN] = {0};
size_t select_response_len = 0;
uint16_t select_sw = 0;
int res = Iso7816Select(CC_CONTACTLESS, false, true, aid, aid_len, select_response, sizeof(select_response), &select_response_len, &select_sw);
sAPDU_t apdu = {0x00, ISO7816_SELECT_FILE, 0x04, 0x00, (uint8_t)aid_len, aid};
int res = Iso7816ExchangeEx(CC_CONTACTLESS, false, true, apdu, true, 0, select_response, sizeof(select_response), &select_response_len, &select_sw);
if (res != PM3_SUCCESS) {
return res;
}
+5 -1
View File
@@ -44,10 +44,11 @@ static isodep_state_t isodep_state = ISODEP_INACTIVE;
void SetISODEPState(isodep_state_t state) {
isodep_state = state;
if (APDULogging) {
PrintAndLogEx(SUCCESS, "Setting ISODEP -> %s%s%s%s"
PrintAndLogEx(SUCCESS, "Setting ISODEP -> %s%s%s%s%s"
, isodep_state == ISODEP_INACTIVE ? "inactive" : ""
, isodep_state == ISODEP_NFCA ? _GREEN_("NFC-A") : ""
, isodep_state == ISODEP_NFCB ? _GREEN_("NFC-B") : ""
, isodep_state == ISODEP_NFCB_PRIME ? _GREEN_("NFC-B'") : ""
, isodep_state == ISODEP_NFCV ? _GREEN_("NFC-V") : ""
);
}
@@ -125,6 +126,9 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool l
case ISODEP_NFCB:
res = exchange_14b_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000);
break;
case ISODEP_NFCB_PRIME:
res = exchange_14b_prime_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000);
break;
case ISODEP_NFCV:
PrintAndLogEx(INFO, " To be implemented, feel free to contribute!");
break;
+1
View File
@@ -31,6 +31,7 @@ typedef enum {
ISODEP_NFCA,
ISODEP_NFCB,
ISODEP_NFCV,
ISODEP_NFCB_PRIME,
} isodep_state_t;
typedef enum {
+3
View File
@@ -1286,6 +1286,9 @@ static int l_set_iso_dep_state(lua_State *L) {
case 3:
SetISODEPState(ISODEP_NFCV);
break;
case 4:
SetISODEPState(ISODEP_NFCB_PRIME);
break;
default:
return returnToLuaWithError(L, "Wrong ISODEP STATE value");
}
+1
View File
@@ -362,6 +362,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#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
// XEROX Commands
#define ISO14443B_XEROX_PWD 0x38