Files
proxmark3/armsrc/secc.c
T
Philippe Teuwen 88d20bd4f2 make style
2026-04-13 09:35:02 +02:00

641 lines
25 KiB
C

//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// HID Config Card (JCOP / GlobalPlatform SCP02) simulation helpers.
// Handles I-block dispatch for tagType=16 in SimulateIso14443aTag and the
// A0 D4 jam logic for SniffIso14443a.
//-----------------------------------------------------------------------------
#include "secc.h"
#include <string.h>
#include "proxmark3_arm.h"
#include "dbprint.h"
#include "BigBuf.h" // DMA_BUFFER_SIZE, MAX_PARITY_SIZE
#include "crc16.h" // AddCrc14A, CheckCrc14A
#include "fpgaloader.h" // FpgaWriteConfWord, FpgaSetupSscDma
#include "desfire_crypto.h" // tdes_nxp_send
#include "mbedtls/des.h" // mbedtls_des_*, mbedtls_des3_*
#include "iso14443a.h" // ReaderTransmit, ReaderReceive, iso14a_get/set_timeout, iso14a_get/toggle_pcb_blocknum, MAX_ISO14A_TIMEOUT
#include "appmain.h" // tearoff_hook, send_wtx
#include "util.h" // data_available, BUTTON_PRESS
#include "cmd.h" // reply_ng
#include "pm3_cmd.h" // CMD_HF_HIDCONFIG_SIM, CMD_HF_HIDCONFIG_SNIFF
#include "dbprint.h" // Dbprintf, LED_*
#include "ticks.h" // WDT_HIT
#include "protocols.h" // ISO14443A_CMD_* constants
// ---------------------------------------------------------------------------
// Internal constants
// ---------------------------------------------------------------------------
// Fixed card challenge used in SCP02 INITIALIZE UPDATE responses (CC portion only).
static const uint8_t s_card_challenge[6] = {0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
// ---------------------------------------------------------------------------
// Custom APDU response table and SCP02 key (loaded from payload)
// ---------------------------------------------------------------------------
static hid_apdu_entry_t s_apdu_table[HID_APDU_MAX_ENTRIES];
static uint8_t s_apdu_count = 0;
static uint8_t s_scp02_key[16] = {0};
// Per-card key diversification data emitted in INITIALIZE UPDATE and used to
// derive card-specific static ENC/MAC/DEK keys via VISA-2. All-zero disables
// diversification (s_scp02_key is used directly as the static base key).
static uint8_t s_kdd[10] = {0};
// Key Version Number emitted in the INIT UPDATE response (GP KVN).
static uint8_t s_kvn = 0x01;
// Default response for unmatched APDUs (loaded from JSON "DefaultResponse").
// When s_default_resp_len == 0 the handler falls back to the legacy 90 00 reply.
static uint8_t s_default_resp[HID_APDU_MAX_RESP] = {0};
static uint8_t s_default_resp_len = 0;
// SCP02 session state — updated on each INITIALIZE UPDATE.
static uint16_t s_seq_counter = 0;
static uint8_t s_host_challenge[8] = {0};
// Jam config — set by SniffHIDConfigCard before entering sniff loop.
// Length 0 means "use built-in default".
static uint8_t s_jam_apdu[HID_JAM_MAX_APDU];
static uint8_t s_jam_apdu_len = 0;
static uint8_t s_jam_resp[HID_JAM_MAX_RESP];
static uint8_t s_jam_resp_len = 0;
static const uint8_t s_jam_apdu_default[5] = {0xA0, 0xD4, 0x00, 0x00, 0x00};
static const uint8_t s_jam_resp_default[4] = {0x00, 0x00, 0x90, 0x00};
void hid_config_card_set_apdu_table(const hid_apdu_entry_t *table, uint8_t count) {
s_apdu_count = (count > HID_APDU_MAX_ENTRIES) ? HID_APDU_MAX_ENTRIES : count;
memcpy(s_apdu_table, table, s_apdu_count * sizeof(hid_apdu_entry_t));
}
static void hid_config_card_set_default_resp(const uint8_t *resp, uint8_t len) {
s_default_resp_len = (len > HID_APDU_MAX_RESP) ? HID_APDU_MAX_RESP : len;
if (s_default_resp_len)
memcpy(s_default_resp, resp, s_default_resp_len);
}
static void hid_config_card_set_scp02_key(const uint8_t *key, const uint8_t *kdd, uint8_t kvn) {
memcpy(s_scp02_key, key, 16);
memcpy(s_kdd, kdd, 10);
s_kvn = kvn;
s_seq_counter = 0;
}
// ---------------------------------------------------------------------------
// Internal crypto helpers
// ---------------------------------------------------------------------------
// Derive the per-card static base key for the given SCP02 key type:
// type=0x01 -> K_ENC, 0x02 -> K_MAC, 0x03 -> K_DEK
// VISA-2 diversification block (NIST SP800-108 style, but fixed JCOP layout):
// d = KDD[0:2] || KDD[4:8] || F0 || type || KDD[0:2] || KDD[4:8] || 0F || type
// The diversified key is 3DES-ECB( master, d ) over the two 8-byte halves.
// When s_kdd is all zero we return s_scp02_key directly (legacy "no
// diversification" mode - works with test cards that share a common master).
static void scp02_get_base_key(uint8_t type, uint8_t *out16) {
bool kdd_zero = true;
for (int i = 0; i < 10; i++) {
if (s_kdd[i]) { kdd_zero = false; break; }
}
if (kdd_zero) {
memcpy(out16, s_scp02_key, 16);
return;
}
uint8_t block[16];
memcpy(block, s_kdd, 2);
memcpy(block + 2, s_kdd + 4, 4);
block[6] = 0xF0;
block[7] = type;
memcpy(block + 8, s_kdd, 2);
memcpy(block + 10, s_kdd + 4, 4);
block[14] = 0x0F;
block[15] = type;
mbedtls_des3_context ctx;
mbedtls_des3_init(&ctx);
mbedtls_des3_set2key_enc(&ctx, s_scp02_key);
mbedtls_des3_crypt_ecb(&ctx, block, out16);
mbedtls_des3_crypt_ecb(&ctx, block + 8, out16 + 8);
mbedtls_des3_free(&ctx);
}
// Derive a 16-byte SCP02 session key using 3DES-CBC with null IV.
// base_key16 is the diversified per-card static key (from scp02_get_base_key).
// constant0/constant1 select the key type (0x01,0x82=S-ENC; 0x01,0x01=S-MAC;
// 0x01,0x81=DEK). The session counter is from the current INIT UPDATE.
static void derive_scp02_session_key(const uint8_t *base_key16, uint8_t c0, uint8_t c1, uint16_t sc, uint8_t *out16) {
uint8_t deriv[16] = {c0, c1, (uint8_t)(sc >> 8), (uint8_t)(sc & 0xFF),
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
uint8_t iv[8] = {0};
tdes_nxp_send(deriv, out16, 16, base_key16, iv, 2);
}
// Retail MAC (ISO 9797-1 Algorithm 3): single-DES for all-but-last blocks,
// full 3DES for the final block. data_len must be a multiple of 8.
static void scp02_retail_mac(const uint8_t *key16, const uint8_t *data, size_t n_blocks, uint8_t *out8) {
mbedtls_des_context des_ctx;
mbedtls_des3_context des3_ctx;
mbedtls_des_init(&des_ctx);
mbedtls_des3_init(&des3_ctx);
mbedtls_des_setkey_enc(&des_ctx, key16);
mbedtls_des3_set2key_enc(&des3_ctx, key16);
uint8_t x[8] = {0};
uint8_t tmp[8];
for (size_t i = 0; i < n_blocks - 1; i++) {
for (int j = 0; j < 8; j++) tmp[j] = data[i * 8 + j] ^ x[j];
mbedtls_des_crypt_ecb(&des_ctx, tmp, x);
}
for (int j = 0; j < 8; j++) tmp[j] = data[(n_blocks - 1) * 8 + j] ^ x[j];
mbedtls_des3_crypt_ecb(&des3_ctx, tmp, out8);
mbedtls_des_free(&des_ctx);
mbedtls_des3_free(&des3_ctx);
}
// Full 3DES-CBC-MAC: every block (including intermediate) uses full 3DES.
// data_len must be a multiple of 8.
static void scp02_full_3des_cbc_mac(const uint8_t *key16, const uint8_t *data, size_t n_blocks, uint8_t *out8) {
mbedtls_des3_context ctx;
mbedtls_des3_init(&ctx);
mbedtls_des3_set2key_enc(&ctx, key16);
uint8_t x[8] = {0};
uint8_t tmp[8];
for (size_t i = 0; i < n_blocks; i++) {
for (int j = 0; j < 8; j++) tmp[j] = data[i * 8 + j] ^ x[j];
mbedtls_des3_crypt_ecb(&ctx, tmp, x);
}
memcpy(out8, x, 8);
mbedtls_des3_free(&ctx);
}
// ---------------------------------------------------------------------------
// SCP02 cryptogram computation
// ---------------------------------------------------------------------------
// Card cryptogram = full-3DES-CBC-MAC(S-ENC, HC(8) || SC(2)||CC(6) || 80 00*7)
static void compute_card_cryptogram(const uint8_t *host_challenge, uint8_t *out) {
uint8_t k_enc[16];
scp02_get_base_key(0x01, k_enc);
uint8_t s_enc[16];
derive_scp02_session_key(k_enc, 0x01, 0x82, s_seq_counter, s_enc);
uint8_t data[24];
memcpy(data, host_challenge, 8);
data[8] = (uint8_t)(s_seq_counter >> 8);
data[9] = (uint8_t)(s_seq_counter & 0xFF);
memcpy(data + 10, s_card_challenge, 6);
data[16] = 0x80;
memset(data + 17, 0x00, 7);
scp02_full_3des_cbc_mac(s_enc, data, 3, out);
}
// Host cryptogram = full-3DES-CBC-MAC(S-ENC, SC(2)||CC(6)||HC(8) || 80 00*7)
static void compute_host_cryptogram(const uint8_t *s_enc, uint8_t *out) {
uint8_t data[24];
data[0] = (uint8_t)(s_seq_counter >> 8);
data[1] = (uint8_t)(s_seq_counter & 0xFF);
memcpy(data + 2, s_card_challenge, 6);
memcpy(data + 8, s_host_challenge, 8);
data[16] = 0x80;
memset(data + 17, 0x00, 7);
scp02_full_3des_cbc_mac(s_enc, data, 3, out);
}
// C-MAC = Retail-MAC(S-MAC, {84 82 sec_level 00 10 || HostCrypto(8) || 80 00 00})
static void compute_ext_auth_cmac(const uint8_t *s_mac, uint8_t sec_level,
const uint8_t *host_crypto, uint8_t *out) {
uint8_t data[16];
data[0] = 0x84;
data[1] = 0x82;
data[2] = sec_level;
data[3] = 0x00;
data[4] = 0x10;
memcpy(data + 5, host_crypto, 8);
data[13] = 0x80;
data[14] = 0x00;
data[15] = 0x00;
scp02_retail_mac(s_mac, data, 2, out);
}
// ---------------------------------------------------------------------------
// I-block handler (called from SimulateIso14443aTag for tagType=16)
// ---------------------------------------------------------------------------
bool hid_config_card_handle_iblock(const uint8_t *cmd, int len, tag_response_info_t *ri) {
uint8_t pcb = cmd[0];
bool has_cid = (pcb & 0x08) != 0;
// Payload starts after PCB [+ CID]
int off = has_cid ? 2 : 1; // offset to APDU CLA byte
// Echo PCB and mirror CID from the reader frame.
ri->response[0] = pcb;
if (has_cid)
ri->response[1] = cmd[1]; // mirror CID byte from reader
// Base pointer for APDU payload in the response (after PCB [CID])
uint8_t *rsp = &ri->response[off];
// ----- Check custom APDU table first (overrides hardcoded responses) -----
// cmd[off] is the start of the APDU payload; len includes trailing CRC (2 bytes).
// apdu_mask bit i=1 → exact match; bit i=0 → wildcard:
// apdu[i]=0x00 → any single byte (**); apdu[i]=0x01 → length-prefix skip (##).
for (int i = 0; i < s_apdu_count; i++) {
if (s_apdu_table[i].apdu_len == 0)
continue;
bool match = true;
int recv_pos = 0;
for (int j = 0; j < (int)s_apdu_table[i].apdu_len && match; j++) {
bool is_exact = (s_apdu_table[i].apdu_mask[j / 8] & (1u << (j % 8))) != 0;
if (off + recv_pos >= len - 2) { match = false; break; }
if (is_exact) {
if (cmd[off + recv_pos] != s_apdu_table[i].apdu[j])
match = false;
recv_pos++;
} else if (s_apdu_table[i].apdu[j] == 0x01) {
// ## : read length byte, skip 1 + N bytes total
uint8_t payload_len = cmd[off + recv_pos];
recv_pos += 1 + payload_len;
} else {
// ** : skip one byte
recv_pos++;
}
}
if (match) {
memcpy(rsp, s_apdu_table[i].resp, s_apdu_table[i].resp_len);
ri->response_n = off + s_apdu_table[i].resp_len;
return true;
}
}
// SELECT AID, A0 D4, and other generic APDUs are now handled exclusively
// by the JSON APDUResponses table (above) and the DefaultResponse
// fall-through (below). Only SCP02 crypto handlers stay hardcoded.
// ----- INITIALIZE UPDATE (INS=0x50) -----
// CID frame: INS at cmd[3], host challenge at cmd[off+5] (after CLA INS P1 P2 Lc)
if (has_cid && len >= 17 && cmd[3] == 0x50) {
s_seq_counter++;
memcpy(s_host_challenge, &cmd[off + 5], 8);
uint8_t cryptogram[8];
compute_card_cryptogram(s_host_challenge, cryptogram);
memcpy(rsp, s_kdd, 10); // key diversification data
rsp[10] = s_kvn; // key version number
rsp[11] = 0x02; // SCP02
rsp[12] = (uint8_t)(s_seq_counter >> 8); // SC high
rsp[13] = (uint8_t)(s_seq_counter & 0xFF); // SC low
memcpy(rsp + 14, s_card_challenge, 6); // CC
memcpy(rsp + 20, cryptogram, 8); // card cryptogram
rsp[28] = 0x90;
rsp[29] = 0x00;
ri->response_n = off + 30;
return true;
}
// ----- EXTERNAL AUTHENTICATE (INS=0x82) -----
// CID frame: sec_level at cmd[off+2], HostCrypto(8) at cmd[off+5], C-MAC(8) at cmd[off+13]
if (has_cid && len >= 25 && cmd[3] == 0x82) {
uint8_t sec_level = cmd[off + 2];
const uint8_t *host_crypto = &cmd[off + 5];
const uint8_t *cmac_recv = &cmd[off + 13];
uint8_t k_enc[16], k_mac[16];
scp02_get_base_key(0x01, k_enc);
scp02_get_base_key(0x02, k_mac);
uint8_t s_enc[16], s_mac[16];
derive_scp02_session_key(k_enc, 0x01, 0x82, s_seq_counter, s_enc);
derive_scp02_session_key(k_mac, 0x01, 0x01, s_seq_counter, s_mac);
uint8_t host_crypto_exp[8];
compute_host_cryptogram(s_enc, host_crypto_exp);
uint8_t cmac_exp[8];
compute_ext_auth_cmac(s_mac, sec_level, host_crypto, cmac_exp);
if (memcmp(host_crypto_exp, host_crypto, 8) != 0 ||
memcmp(cmac_exp, cmac_recv, 8) != 0) {
rsp[0] = 0x63;
rsp[1] = 0x00; // Authentication failed
} else {
rsp[0] = 0x90;
rsp[1] = 0x00;
}
ri->response_n = off + 2;
return true;
}
// ----- All other APDUs: configured DefaultResponse, or 90 00 if none -----
if (s_default_resp_len > 0) {
memcpy(rsp, s_default_resp, s_default_resp_len);
ri->response_n = off + s_default_resp_len;
} else {
rsp[0] = 0x90;
rsp[1] = 0x00;
ri->response_n = off + 2;
}
return true;
}
// ---------------------------------------------------------------------------
// Sniff jam handler (called from SniffIso14443a when param bit 2 is set)
// ---------------------------------------------------------------------------
bool hid_config_card_jam(const uint8_t *cmd, int len, uint8_t *dma_buf) {
uint8_t pcb = cmd[0];
// Only act on I-blocks (bits 7:6 == 00)
if ((pcb & 0xC0) != 0x00)
return false;
int off = (pcb & 0x08) ? 2 : 1; // skip CID if present
// Select active APDU pattern and response (custom or default)
const uint8_t *match = (s_jam_apdu_len > 0) ? s_jam_apdu : s_jam_apdu_default;
int match_len = (s_jam_apdu_len > 0) ? (int)s_jam_apdu_len : (int)sizeof(s_jam_apdu_default);
const uint8_t *resp_data = (s_jam_resp_len > 0) ? s_jam_resp : s_jam_resp_default;
int resp_data_len = (s_jam_resp_len > 0) ? (int)s_jam_resp_len : (int)sizeof(s_jam_resp_default);
if (len < off + match_len + 2) // off + APDU pattern + 2-byte CRC
return false;
if (memcmp(cmd + off, match, match_len) != 0)
return false;
// Build jam response: PCB [CID] <resp_data> CRC CRC
uint8_t resp[2 + HID_JAM_MAX_RESP + 2]; // PCB + optional CID + payload + CRC
int rlen = 0;
resp[rlen++] = pcb;
if (pcb & 0x08) resp[rlen++] = cmd[1]; // mirror CID
memcpy(resp + rlen, resp_data, resp_data_len);
rlen += resp_data_len;
AddCrc14A(resp, rlen);
rlen += 2;
EmSendCmdEx(resp, rlen, false);
// Restore sniffer FPGA mode and re-arm DMA
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER);
FpgaSetupSscDma(dma_buf, DMA_BUFFER_SIZE);
return true;
}
// ---------------------------------------------------------------------------
// CID-aware ISO 14443-4 APDU exchange for HID Config Card reader interaction.
// Sends I-blocks with PCB=0x0A (CID present, CID=0) as negotiated in RATS.
// Strips PCB+CID from received responses before returning.
// API mirrors iso14_apdu().
// ---------------------------------------------------------------------------
int hid_config_card_iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint16_t data_len, uint8_t *res) {
uint8_t parity[MAX_PARITY_SIZE] = {0};
uint8_t *real_cmd = BigBuf_calloc(cmd_len + 5); // PCB(1) + CID(1) + APDU + CRC(2)
if (cmd_len) {
real_cmd[0] = 0x0A; // I-block, CID present (bit 3), block number in bit 0
if (send_chaining)
real_cmd[0] |= 0x10;
real_cmd[0] |= iso14a_get_pcb_blocknum();
real_cmd[1] = 0x00; // CID = 0 (as negotiated in RATS)
memcpy(real_cmd + 2, cmd, cmd_len);
} else {
real_cmd[0] = 0xAA; // R-block ACK + CID present
real_cmd[0] |= iso14a_get_pcb_blocknum();
real_cmd[1] = 0x00; // CID = 0
}
AddCrc14A(real_cmd, cmd_len + 2); // PCB + CID + APDU
ReaderTransmit(real_cmd, cmd_len + 4, NULL); // PCB(1) + CID(1) + APDU + CRC(2)
if (tearoff_hook() == PM3_ETEAROFF) {
BigBuf_free();
return -1;
}
size_t len = ReaderReceive(data, data_len, parity);
uint8_t *data_bytes = (uint8_t *)data;
if (len == 0) {
BigBuf_free();
return 0;
}
uint32_t save_timeout = iso14a_get_timeout();
// S-Block WTX
while (len && ((data_bytes[0] & 0xF2) == 0xF2)) {
if (BUTTON_PRESS() || data_available()) {
BigBuf_free();
return -3;
}
send_wtx(38);
data_bytes[1] &= 0x3F;
iso14a_set_timeout(MAX(data_bytes[1] * save_timeout, MAX_ISO14A_TIMEOUT));
AddCrc14A(data_bytes, len - 2);
ReaderTransmit(data_bytes, len, NULL);
len = ReaderReceive(data_bytes, data_len, parity);
}
iso14a_set_timeout(save_timeout);
// Toggle block number on valid I-block or R(ACK)
if (len >= 3
&& ((data_bytes[0] & 0xC0) == 0 || (data_bytes[0] & 0xD0) == 0x80)
&& (data_bytes[0] & 0x01) == iso14a_get_pcb_blocknum()) {
iso14a_toggle_pcb_blocknum();
}
if (res)
*res = data_bytes[0];
if (len >= 3 && !CheckCrc14A(data_bytes, len)) {
BigBuf_free();
return -1;
}
if (len) {
// Strip PCB and CID from the front of the response
int header_len = ((data_bytes[0] & 0x08) != 0) ? 2 : 1;
len -= header_len;
memmove(data_bytes, data_bytes + header_len, len);
}
BigBuf_free();
return len;
}
// ---------------------------------------------------------------------------
// Full HID Config Card simulation (own loop, no iso14443a.c hooks)
// ---------------------------------------------------------------------------
void SimulateHIDConfigCard(const hid_sim_payload_t *payload) {
hid_config_card_set_apdu_table(payload->apdu_table, payload->apdu_count);
hid_config_card_set_default_resp(payload->default_resp, payload->default_resp_len);
hid_config_card_set_scp02_key(payload->scp02_key, payload->kdd, payload->kvn ? payload->kvn : 0x01);
// Command buffers
uint8_t receivedCmd[MAX_FRAME_SIZE];
uint8_t receivedCmdPar[MAX_PARITY_SIZE];
BigBuf_free_keep_EM();
#define HID_SIM_DYNAMIC_RESPONSE_SIZE 64
#define HID_SIM_DYNAMIC_MODULATION_SIZE 512
uint8_t *dyn_resp = BigBuf_calloc(HID_SIM_DYNAMIC_RESPONSE_SIZE);
uint8_t *dyn_mod = BigBuf_calloc(HID_SIM_DYNAMIC_MODULATION_SIZE);
if (dyn_resp == NULL || dyn_mod == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_HIDCONFIG_SIM, PM3_EMALLOC, NULL, 0);
return;
}
tag_response_info_t dynamic_response_info = {
.response = dyn_resp,
.response_n = 0,
.modulation = dyn_mod,
.modulation_n = 0
};
// Build flags: use provided UID, override ATQA/SAK/ATS with HID values.
uint16_t flags = payload->flags | FLAG_ATS_IN_DATA | FLAG_ATQA_IN_DATA | FLAG_SAK_IN_DATA;
uint8_t uid[10];
memcpy(uid, payload->uid, sizeof(uid));
uint16_t atqa_val = ((uint16_t)payload->atqa[0] << 8) | payload->atqa[1];
iso14a_set_atqa_sak_override(atqa_val, payload->sak);
tag_response_info_t *responses;
uint32_t cuid;
uint8_t pages;
if (SimulateIso14443aInit(4, flags, uid,
(uint8_t *)payload->ats, payload->ats_len,
&responses, &cuid, &pages, NULL) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_HIDCONFIG_SIM, PM3_EINIT, NULL, 0);
return;
}
iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN);
iso14a_set_timeout(201400);
int len = 0;
clear_trace();
set_tracing(true);
LED_A_ON();
uint8_t cardINTERACTIONS = 0;
bool finished = false;
while (finished == false) {
WDT_HIT();
tag_response_info_t *p_response = NULL;
if (GetIso14443aCommandFromReader(receivedCmd, sizeof(receivedCmd), receivedCmdPar, &len) == false) {
finished = true;
break;
}
dynamic_response_info.response_n = 0;
dynamic_response_info.modulation_n = 0;
if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) {
p_response = &responses[RESP_INDEX_ATQA];
} else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) {
p_response = &responses[RESP_INDEX_ATQA];
} else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) {
p_response = &responses[RESP_INDEX_UIDC1];
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) {
p_response = &responses[RESP_INDEX_SAKC1];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) {
p_response = &responses[RESP_INDEX_ATS];
} else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) {
p_response = NULL;
} else {
// ISO 14443-4 I-block dispatch — HID-specific handling
switch (receivedCmd[0]) {
case 0x02:
case 0x03:
case 0x0A:
case 0x0B: {
hid_config_card_handle_iblock(receivedCmd, len, &dynamic_response_info);
if (dynamic_response_info.response_n > 0) {
// Mirror CID from reader frame when CID bit is set in PCB
if (receivedCmd[0] & 0x08)
dynamic_response_info.response[1] = receivedCmd[1];
AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n);
dynamic_response_info.response_n += 2;
if (prepare_tag_modulation(&dynamic_response_info, HID_SIM_DYNAMIC_MODULATION_SIZE))
p_response = &dynamic_response_info;
}
cardINTERACTIONS++;
if (payload->exitAfter > 0 && cardINTERACTIONS >= payload->exitAfter)
finished = true;
break;
}
default:
break;
}
}
if (p_response != NULL)
EmSendPrecompiledCmd(p_response);
}
set_tracing(false);
LED_A_OFF();
BigBuf_free_keep_EM();
reply_ng(CMD_HF_HIDCONFIG_SIM, PM3_SUCCESS, NULL, 0);
}
// ---------------------------------------------------------------------------
// HID Config Card sniff with optional A0 D4 jamming
// ---------------------------------------------------------------------------
void SniffHIDConfigCard(const hid_sniff_payload_t *payload) {
// Configure jam pattern and response before entering the sniff loop.
if (payload->apdu_len > 0 && payload->apdu_len <= HID_JAM_MAX_APDU) {
memcpy(s_jam_apdu, payload->apdu, payload->apdu_len);
s_jam_apdu_len = payload->apdu_len;
} else {
s_jam_apdu_len = 0; // use default A0 D4 00 00 00
}
if (payload->resp_len > 0 && payload->resp_len <= HID_JAM_MAX_RESP) {
memcpy(s_jam_resp, payload->resp, payload->resp_len);
s_jam_resp_len = payload->resp_len;
} else {
s_jam_resp_len = 0; // use default 00 00 90 00
}
// Delegate to SniffIso14443a.
// When param bit 0x04 is set, SniffIso14443a calls hid_config_card_jam()
// inline after each decoded reader frame (see iso14443a.c).
SniffIso14443a(payload->param);
}