mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Improved crypto on secc
Improved crypto on secc
This commit is contained in:
+131
-40
@@ -41,8 +41,8 @@
|
||||
// Internal constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Fixed card challenge used in SCP02 INITIALIZE UPDATE responses.
|
||||
static const uint8_t hid_cc[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
|
||||
// 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)
|
||||
@@ -52,6 +52,10 @@ 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};
|
||||
|
||||
// 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];
|
||||
@@ -69,54 +73,109 @@ void hid_config_card_set_apdu_table(const hid_apdu_entry_t *table, uint8_t count
|
||||
|
||||
static void hid_config_card_set_scp02_key(const uint8_t *key) {
|
||||
memcpy(s_scp02_key, key, 16);
|
||||
s_seq_counter = 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internal crypto
|
||||
// Internal crypto helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Compute GlobalPlatform SCP02 card cryptogram.
|
||||
// card_cryptogram = Retail_MAC(S-ENC, host_challenge || card_challenge || 0x80 || 0x00*7)
|
||||
// Uses hardcoded master key 404142...4F and SN = 0x0001.
|
||||
static void compute_card_cryptogram(const uint8_t *host_challenge, uint8_t *out) {
|
||||
static const uint8_t SN[2] = {0x00, 0x01};
|
||||
// Derive a 16-byte SCP02 session key using 3DES-CBC with null IV.
|
||||
// constant0/constant1 select the key type (0x01,0x82=S-ENC; 0x01,0x01=S-MAC).
|
||||
static void derive_scp02_session_key(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, s_scp02_key, iv, 2);
|
||||
}
|
||||
|
||||
// Derive S-ENC: 3DES-CBC(K, zero_IV, {0x01, 0x82, SN0, SN1, 0x00*12})
|
||||
uint8_t s_enc[16];
|
||||
{
|
||||
uint8_t deriv[16] = {0x01, 0x82, SN[0], SN[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint8_t iv[8] = {0};
|
||||
tdes_nxp_send(deriv, s_enc, 16, s_scp02_key, iv, 2);
|
||||
}
|
||||
|
||||
// Retail MAC over HC || CC || 0x80 || 0x00*7
|
||||
// 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, s_enc);
|
||||
mbedtls_des3_set2key_enc(&des3_ctx, s_enc);
|
||||
mbedtls_des_setkey_enc(&des_ctx, key16);
|
||||
mbedtls_des3_set2key_enc(&des3_ctx, key16);
|
||||
|
||||
uint8_t x[8] = {0};
|
||||
uint8_t tmp[8];
|
||||
|
||||
// Block 1: host challenge
|
||||
for (int i = 0; i < 8; i++) tmp[i] = host_challenge[i] ^ x[i];
|
||||
mbedtls_des_crypt_ecb(&des_ctx, tmp, x);
|
||||
|
||||
// Block 2: card challenge
|
||||
for (int i = 0; i < 8; i++) tmp[i] = hid_cc[i] ^ x[i];
|
||||
mbedtls_des_crypt_ecb(&des_ctx, tmp, x);
|
||||
|
||||
// Block 3: 0x80 || 0x00*7 (ISO 9797-1 Method 2 padding)
|
||||
tmp[0] = 0x80 ^ x[0];
|
||||
for (int i = 1; i < 8; i++) tmp[i] = x[i];
|
||||
mbedtls_des3_crypt_ecb(&des3_ctx, tmp, out);
|
||||
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 s_enc[16];
|
||||
derive_scp02_session_key(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)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -201,23 +260,55 @@ bool hid_config_card_handle_iblock(const uint8_t *cmd, int len, tag_response_inf
|
||||
}
|
||||
|
||||
// ----- INITIALIZE UPDATE (INS=0x50) -----
|
||||
// CID frame: INS at cmd[3], host challenge at cmd[off+4] = cmd[6]
|
||||
// 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) {
|
||||
uint8_t cryptogram[8];
|
||||
compute_card_cryptogram(&cmd[off + 4], cryptogram);
|
||||
s_seq_counter++;
|
||||
memcpy(s_host_challenge, &cmd[off + 5], 8);
|
||||
|
||||
memset(rsp, 0x00, 10); // key diversification data
|
||||
rsp[10] = 0xFF; // key version (JCOP factory default)
|
||||
rsp[11] = 0x02; // SCP02
|
||||
memcpy(rsp + 12, hid_cc, 8); // card challenge
|
||||
memcpy(rsp + 20, cryptogram, 8); // card cryptogram
|
||||
uint8_t cryptogram[8];
|
||||
compute_card_cryptogram(s_host_challenge, cryptogram);
|
||||
|
||||
memset(rsp, 0x00, 10); // key diversification data
|
||||
rsp[10] = 0xFF; // key version (JCOP factory default)
|
||||
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 AUTH and all other APDUs: generic 90 00 -----
|
||||
// ----- 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 s_enc[16], s_mac[16];
|
||||
derive_scp02_session_key(0x01, 0x82, s_seq_counter, s_enc);
|
||||
derive_scp02_session_key(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: generic 90 00 -----
|
||||
rsp[0] = 0x90; rsp[1] = 0x00;
|
||||
ri->response_n = off + 2;
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user