From 3595f42f6d3dcff7ccb6c683351e3177d5e0746e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 17 Oct 2025 13:31:19 +0200 Subject: [PATCH] add secure messaging for UL_AES. Calculating CMAC on device side --- armsrc/Makefile | 3 +- armsrc/Standalone/hf_aveful.c | 2 +- armsrc/appmain.c | 17 +- armsrc/cmac_calc.c | 173 ++++++++++++++++++++ armsrc/cmac_calc.h | 44 +++++ armsrc/mifarecmd.c | 223 ++++++++++++------------- armsrc/mifarecmd.h | 11 +- armsrc/mifareutil.c | 295 ++++++++++++++++++++++++++-------- armsrc/mifareutil.h | 5 +- 9 files changed, 567 insertions(+), 206 deletions(-) create mode 100644 armsrc/cmac_calc.c create mode 100644 armsrc/cmac_calc.h diff --git a/armsrc/Makefile b/armsrc/Makefile index 9929b3ae3..b7d18cc82 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -152,7 +152,8 @@ THUMBSRC = start.c \ ticks.c \ clocks.c \ hfsnoop.c \ - generator.c + generator.c \ + cmac_calc.c # These are to be compiled in ARM mode diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index d634f819c..51d998736 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -188,7 +188,7 @@ void RunMod(void) { for (int i = 0; i < block_count; i++) { uint8_t dataout[16] = {0x00}; - if (mifare_ultra_readblock(i, dataout)) { + if (mifare_ultra_readblock(i, dataout) != PM3_SUCCESS) { // If there's an error reading, go back to search state read_successful = false; break; diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 49fcafa66..5a2d08d2a 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -67,6 +67,7 @@ #include "sam_picopass.h" #include "sam_seos.h" #include "sam_mfc.h" +#include "cmac_calc.h" #ifdef WITH_LCD #include "LCD_disabled.h" @@ -1832,7 +1833,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFAREU_READBL: { - MifareUReadBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); + MifareUReadBlock((mful_readblock_t *)packet->data.asBytes); break; } case CMD_HF_MIFAREUC_AUTH: { @@ -1840,17 +1841,11 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFAREULAES_AUTH: { - struct p { - bool turn_off_field; - uint8_t keyno; - uint8_t key[16]; - } PACKED; - struct p *payload = (struct p *) packet->data.asBytes; - MifareUL_AES_Auth(payload->turn_off_field, payload->keyno, payload->key); + MifareUL_AES_Auth((mfulaes_keys_t *)packet->data.asBytes); break; } case CMD_HF_MIFAREU_READCARD: { - MifareUReadCard(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); + MifareUReadCard((mful_readblock_t *)packet->data.asBytes); break; } case CMD_HF_MIFAREU_SETKEY: { @@ -1888,11 +1883,11 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFAREU_WRITEBL: { - MifareUWriteBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); + MifareUWriteBlock((mful_writeblock_t *)packet->data.asBytes); break; } case CMD_HF_MIFAREU_WRITEBL_COMPAT: { - MifareUWriteBlockCompat(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); + MifareUWriteBlockCompat((mful_writeblock_t *)packet->data.asBytes); break; } case CMD_HF_MIFARE_ACQ_ENCRYPTED_NONCES: { diff --git a/armsrc/cmac_calc.c b/armsrc/cmac_calc.c new file mode 100644 index 000000000..146bed81e --- /dev/null +++ b/armsrc/cmac_calc.c @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Christian Herrmman, Iceman - October 2025 +// 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. +//----------------------------------------------------------------------------- +// Calculate CMAC AES +//----------------------------------------------------------------------------- +#include "cmac_calc.h" + +#include +#include "commonutil.h" +#include "BigBuf.h" +#include "dbprint.h" +#include "mbedtls/aes.h" + +static ulaes_key_t g_secure_session = { + .counter = 0, + .use_schann = false, +}; + +// init clears all +void init_secure_session(void) { + g_secure_session.counter = 0; + g_secure_session.use_schann = false; + memset(g_secure_session.cmac_sk1, 0, sizeof(g_secure_session.cmac_sk1)); + memset(g_secure_session.cmac_sk2, 0, sizeof(g_secure_session.cmac_sk2)); + memset(g_secure_session.sessionkey, 0, sizeof(g_secure_session.sessionkey)); +} + +void increase_session_counter(void) { + g_secure_session.counter++; +} + +void set_session_channel(bool use_schann) { + g_secure_session.use_schann = use_schann; +} + +ulaes_key_t *get_secure_session_obj(void) { + return &g_secure_session; +} + +// XOR two 128-bit blocks +static void xor_128(const uint8_t *a, const uint8_t *b, uint8_t *out) { + for (int i = 0; i < 16; i++) { + out[i] = a[i] ^ b[i]; + } +} + +// Left shift one bit in a 128-bit block +static void left_shift_128(const uint8_t *input, uint8_t *output) { + uint8_t overflow = 0; + for (int i = 15; i >= 0; i--) { + output[i] = (input[i] << 1) | overflow; + overflow = (input[i] & 0x80) ? 1 : 0; + } +} + +// Generate Subkeys K1 and K2 +static void generate_subkeys(mbedtls_aes_context *ctx, uint8_t *K1, uint8_t *K2) { + uint8_t L[16] = {0}; + + // Step 1: L = AES-ENC(0^128) + mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, L, L); + + // Step 2: K1 = L << 1 (with conditional XOR with 0x87) + left_shift_128(L, K1); + if (L[0] & 0x80) { + // If MSB is 1 + K1[15] ^= 0x87; + } + + // Step 3: K2 = K1 << 1 (with conditional XOR with 0x87) + left_shift_128(K1, K2); + if (K1[0] & 0x80) { + K2[15] ^= 0x87; + } +} + +// Pad the last block (adds 0x80 followed by zeros) +static void padding(const uint8_t *lastb, uint8_t *pad, size_t len) { + memcpy(pad, lastb, len); + pad[len] = 0x80; + for (size_t i = len + 1; i < 16; i++) { + pad[i] = 0x00; + } +} + +// CMAC implementation +void ulaes_cmac(const uint8_t *key, size_t key_len, const uint8_t *input, size_t ilen, uint8_t output[16]) { + + uint8_t last[16] = {0}; + uint8_t X[16] = {0}; + uint8_t Y[16] = {0}; + uint8_t buffer[16] = {0}; + + int last_block_complete = (ilen % 16 == 0 && ilen != 0); + + memset(g_secure_session.cmac_sk1, 0, sizeof(g_secure_session.cmac_sk1)); + memset(g_secure_session.cmac_sk2, 0, sizeof(g_secure_session.cmac_sk2)); + + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + mbedtls_aes_setkey_enc(&ctx, key, 128); + + generate_subkeys(&ctx, g_secure_session.cmac_sk1, g_secure_session.cmac_sk2); + + size_t n_blocks = (ilen + 15) / 16; + + // prepare last block + if (n_blocks == 0) { + // if message is empty, CMAC is just MAC of padded block XOR K2 + // since buffer is all zeros here. + n_blocks = 1; + buffer[0] = 0x80; + xor_128(buffer, g_secure_session.cmac_sk2, last); + } else { + + const uint8_t *last_block = input + 16 * (n_blocks - 1); + + if (last_block_complete) { + xor_128(last_block, g_secure_session.cmac_sk1, last); + } else { + padding(last_block, buffer, ilen % 16); + xor_128(buffer, g_secure_session.cmac_sk2, last); + } + } + + // main loop + for (size_t i = 0; i < n_blocks - 1; i++) { + xor_128(X, input + 16 * i, Y); + mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, Y, X); + } + + // last block + xor_128(X, last, Y); + mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, Y, output); + mbedtls_aes_free(&ctx); +} + +// convert from 16 bytes to 8 bytes +void ulaes_cmac8(uint8_t *cmac, uint8_t *mac) { + uint8_t j = 0; + for (int i = 1; i < 16; i += 2) { + mac[j++] = cmac[i]; + } +} + +void append_cmac(uint8_t *d, size_t n) { + uint8_t mac[16] = {0}; + uint8_t cmd_mac[2 + n]; + cmd_mac[0] = g_secure_session.counter & 0xFF; + cmd_mac[1] = (g_secure_session.counter >> 8) & 0xFF; + memcpy(cmd_mac + 2, d, n); + + print_result("cmd mac", cmd_mac, (2 + n)); + + ulaes_cmac(g_secure_session.sessionkey, sizeof(g_secure_session.sessionkey), cmd_mac, (2 + n), mac); + // append CMAC to end of the command we are trying to send + ulaes_cmac8(mac, d + n); + + increase_session_counter(); +} diff --git a/armsrc/cmac_calc.h b/armsrc/cmac_calc.h new file mode 100644 index 000000000..f26cc6f4b --- /dev/null +++ b/armsrc/cmac_calc.h @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Christian Herrmman, Iceman - October 2025 +// 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. +//----------------------------------------------------------------------------- +// Calculate CMAC AES +//----------------------------------------------------------------------------- + +#ifndef __CMAC_CALC_H +#define __CMAC_CALC_H + + +#include "common.h" + +typedef struct { + bool use_schann; + uint8_t cmac_sk1[16]; + uint8_t cmac_sk2[16]; + uint8_t sessionkey[16]; + uint16_t counter; +} ulaes_key_t; + +ulaes_key_t *get_secure_session_obj(void); + +void init_secure_session(void); +void increase_session_counter(void); +void set_session_channel(bool use_schann); + +void ulaes_cmac(const uint8_t *key, size_t key_len, const uint8_t *input, size_t ilen, uint8_t output[16]); +void ulaes_cmac8(uint8_t *cmac, uint8_t *mac); +void append_cmac(uint8_t *d, size_t n); + +#endif diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 176bd0702..3eac557f1 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -37,6 +37,7 @@ #include "usb_cdc.h" // usb_poll_validate_length #include "spiffs.h" // spiffs #include "appmain.h" // print_stack_usage +#include "cmac_calc.h" #ifndef HARDNESTED_AUTHENTICATION_TIMEOUT # define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) @@ -287,7 +288,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes) { reply_mix(CMD_ACK, 1, 0, 0, 0, 0); } -void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) { +void MifareUL_AES_Auth(mfulaes_keys_t *packet) { LED_A_ON(); LED_B_OFF(); @@ -304,13 +305,13 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) { return; }; - if (mifare_ultra_aes_auth(keyno, keybytes) == 0) { + if (mifare_ultra_aes_auth(packet->keyno, packet->key, packet->use_schann) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed"); OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT); return; } - if (turn_off_field) { + if (packet->turn_off_field) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } @@ -320,12 +321,13 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) { // Arg0 = BlockNo, // Arg1 = UsePwd bool // datain = PWD bytes, -void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { - uint8_t blockNo = arg0; - uint8_t dataout[16] = {0x00}; - bool useCKey = (arg1 == 1); // UL_C - bool usePwd = (arg1 == 2); // UL_EV1/NTAG - bool useAESKey = (arg1 == 3); // UL_AES +void MifareUReadBlock(mful_readblock_t *packet) { + uint8_t blockNo = packet->block_no; + bool useCKey = (packet->keytype == 1); // UL_C + bool usePwd = (packet->keytype == 2); // UL_EV1/NTAG + bool useAESKey = (packet->keytype == 3); // UL_AES + + init_secure_session(); LEDsoff(); LED_A_ON(); @@ -336,57 +338,50 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - OnError(1); + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; } // UL-C authentication if (useCKey) { - uint8_t key[16] = {0x00}; - memcpy(key, datain, sizeof(key)); - - if (mifare_ultra_auth(key) == 0) { - OnError(1); + if (mifare_ultra_auth(packet->key) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; } } - // UL-AES authentication + // UL-AES authentication, hardcode to use keyno 0 if (useAESKey) { - uint8_t key[16] = {0x00}; - memcpy(key, datain, sizeof(key)); - - if (mifare_ultra_aes_auth(0, key) == 0) { - OnError(1); + if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; } } // UL-EV1 / NTAG authentication if (usePwd) { - uint8_t pwd[4] = {0x00}; - memcpy(pwd, datain, 4); uint8_t pack[4] = {0, 0, 0, 0}; - if (!mifare_ul_ev1_auth(pwd, pack)) { - OnError(1); + if (mifare_ul_ev1_auth(packet->key, pack) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; } } - if (mifare_ultra_readblock(blockNo, dataout)) { + uint8_t dataout[16] = {0x00}; + if (mifare_ultra_readblock(blockNo, dataout) != PM3_SUCCESS) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Read block error"); - OnError(2); + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ECARDEXCHANGE); return; } if (mifare_ultra_halt()) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - OnError(3); + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; } - reply_mix(CMD_ACK, 1, 0, 0, dataout, 16); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + reply_ng(CMD_HF_MIFAREU_READBL, PM3_SUCCESS, dataout, 16); LEDsoff(); } @@ -394,7 +389,10 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { // arg1 = Pages (number of blocks) // arg2 = useKey // datain = KEY bytes -void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) { +void MifareUReadCard(mful_readblock_t *packet) { + + init_secure_session(); + LEDsoff(); LED_A_ON(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -405,56 +403,49 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) set_tracing(true); // params - uint8_t blockNo = arg0; - uint16_t blocks = arg1; - bool useCKey = (arg2 == 1); // UL_C - bool usePwd = (arg2 == 2); // UL_EV1/NTAG - bool useAESKey = (arg2 == 3); // UL_AES + uint8_t blockNo = packet->block_no; + uint16_t blocks = packet->num_of_blocks; + bool useCKey = (packet->keytype == 1); // UL_C + bool usePwd = (packet->keytype == 2); // UL_EV1/NTAG + bool useAESKey = (packet->keytype == 3); // UL_AES + bool schann = packet->use_schann; + uint32_t countblocks = 0; + uint8_t *dataout = BigBuf_calloc(CARD_MEMORY_SIZE); if (dataout == NULL) { Dbprintf("Failed to allocate memory"); - OnError(1); + OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_EMALLOC); return; } - int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); - if (len == 0) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - OnError(1); + OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT); return; } // UL-C authentication if (useCKey) { - uint8_t key[16] = {0x00}; - memcpy(key, datain, sizeof(key)); - - if (mifare_ultra_auth(key) == 0) { - OnError(1); + if (mifare_ultra_auth(packet->key) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT); return; } } // UL-AES authentication if (useAESKey) { - uint8_t key[16] = {0x00}; - memcpy(key, datain, sizeof(key)); - - if (mifare_ultra_aes_auth(0, key) == 0) { - OnError(1); + if (mifare_ultra_aes_auth(0, packet->key, schann) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT); return; } } // UL-EV1 / NTAG authentication if (usePwd) { - uint8_t pwd[4] = {0x00}; - memcpy(pwd, datain, sizeof(pwd)); uint8_t pack[4] = {0, 0, 0, 0}; - - if (mifare_ul_ev1_auth(pwd, pack) == 0) { - OnError(1); + if (mifare_ul_ev1_auth(packet->key, pack) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT); return; } } @@ -465,13 +456,12 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) break; } - len = mifare_ultra_readblock(blockNo + i, dataout + (4 * i)); + if (mifare_ultra_readblock(blockNo + i, dataout + (4 * i)) != PM3_SUCCESS) { - if (len) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Read block %d error", i); // if no blocks read - error out if (i == 0) { - OnError(2); + OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ECARDEXCHANGE); return; } else { //stop at last successful read block and return what we got @@ -482,10 +472,9 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) } } - len = mifare_ultra_halt(); - if (len) { + if (mifare_ultra_halt()) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - OnError(3); + OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT); return; } @@ -493,8 +482,15 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) countblocks *= 4; - reply_mix(CMD_ACK, 1, countblocks, dataout - BigBuf_get_addr(), 0, 0); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + mful_readblock_resp_t payload = { + .bytelen = countblocks, + .startidx = dataout - BigBuf_get_addr() + }; + + reply_ng(CMD_HF_MIFAREU_READCARD, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); + LEDsoff(); BigBuf_free(); set_tracing(false); @@ -593,101 +589,100 @@ void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { // 2 = use 0x1B authentication. // datain : 4 first bytes is data to be written. // : 4/16 next bytes is authentication key. -static void MifareUWriteBlockEx(uint8_t arg0, uint8_t arg1, uint8_t *datain, bool reply) { - uint8_t blockNo = arg0; - bool useCKey = (arg1 == 1); // UL_C - bool usePwd = (arg1 == 2); // UL_EV1/NTAG - bool useAESKey = (arg1 == 3); // UL_AES +static void MifareUWriteBlockEx(mful_writeblock_t *packet, bool reply) { + + uint8_t blockNo = packet->block_no; + bool useCKey = (packet->keytype == 1); // UL_C + bool usePwd = (packet->keytype == 2); // UL_EV1/NTAG + bool useAESKey = (packet->keytype == 3); // UL_AES uint8_t blockdata[4] = {0x00}; - memcpy(blockdata, datain, 4); + memcpy(blockdata, packet->data, sizeof(blockdata)); + + init_secure_session(); LEDsoff(); LED_A_ON(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - clear_trace(); set_tracing(true); if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - OnError(0); + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; }; // UL-C authentication if (useCKey) { - uint8_t key[16] = {0x00}; - memcpy(key, datain + 4, sizeof(key)); - - if (mifare_ultra_auth(key) == 0) { - OnError(1); + if (mifare_ultra_auth(packet->key) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; } } // UL-AES authentication if (useAESKey) { - uint8_t key[16] = {0x00}; - memcpy(key, datain + 4, sizeof(key)); - - if (mifare_ultra_aes_auth(0, key) == 0) { - OnError(1); + if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; } } // UL-EV1 / NTAG authentication if (usePwd) { - uint8_t pwd[4] = {0x00}; - memcpy(pwd, datain + 4, 4); uint8_t pack[4] = {0, 0, 0, 0}; - if (mifare_ul_ev1_auth(pwd, pack) == 0) { - OnError(1); + if (mifare_ul_ev1_auth(packet->key, pack) == 0) { + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; } } if (mifare_ultra_writeblock(blockNo, blockdata) != PM3_SUCCESS) { if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); - OnError(0); + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ECARDEXCHANGE); return; }; if (mifare_ultra_halt()) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - OnError(0); + OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT); return; }; if (g_dbglevel >= DBG_INFO) DbpString("WRITE BLOCK FINISHED"); if (reply) { - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + reply_ng(CMD_HF_MIFAREU_WRITEBL, PM3_SUCCESS, NULL, 0); } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); } -void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { - MifareUWriteBlockEx(arg0, arg1, datain, true); +void MifareUWriteBlock(mful_writeblock_t *packet) { + MifareUWriteBlockEx(packet, true); } -// Arg0 : Block to write to. -// Arg1 : 0 = use no authentication. +// keytype: 0 = use no authentication. // 1 = use 0x1A authentication. // 2 = use 0x1B authentication. -// datain : 16 first bytes is data to be written. +// data : 16 first bytes is data to be written. // : 4/16 next bytes is authentication key. -void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain) { - uint8_t blockNo = arg0; - bool useCKey = (arg1 == 1); // UL_C - bool usePwd = (arg1 == 2); // UL_EV1/NTAG - bool useAESKey = (arg1 == 3); // UL_AES - uint8_t blockdata[16] = {0x00}; +void MifareUWriteBlockCompat(mful_writeblock_t *packet) { + uint8_t blockNo = packet->block_no; + bool useCKey = (packet->keytype == 1); // UL_C + bool usePwd = (packet->keytype == 2); // UL_EV1/NTAG + bool useAESKey = (packet->keytype == 3); // UL_AES - memcpy(blockdata, datain, 16); + uint8_t blockdata[16] = {0x00}; + memcpy(blockdata, packet->data, 16); + + // UL-AES doesn't not support copmpability writes + if (useAESKey) { + reply_ng(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_EINVARG, NULL, 0); + return ; + } LEDsoff(); LED_A_ON(); @@ -698,58 +693,42 @@ void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain) { if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - OnError(0); + OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT); return; }; // UL-C authentication if (useCKey) { - uint8_t key[16] = {0x00}; - memcpy(key, datain + 16, sizeof(key)); - - if (mifare_ultra_auth(key) == 0) { - OnError(1); - return; - } - } - - // UL-AES authentication - if (useAESKey) { - uint8_t key[16] = {0x00}; - memcpy(key, datain + 16, sizeof(key)); - - if (mifare_ultra_aes_auth(0, key) == 0) { - OnError(1); + if (mifare_ultra_auth(packet->key) == 0) { + OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT); return; } } // UL-EV1 / NTAG authentication if (usePwd) { - uint8_t pwd[4] = {0x00}; - memcpy(pwd, datain + 16, 4); uint8_t pack[4] = {0, 0, 0, 0}; - if (!mifare_ul_ev1_auth(pwd, pack)) { - OnError(1); + if (mifare_ul_ev1_auth(packet->key, pack) == 0) { + OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT); return; } } if (mifare_ultra_writeblock_compat(blockNo, blockdata) != PM3_SUCCESS) { if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); - OnError(0); + OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ECARDEXCHANGE); return; }; if (mifare_ultra_halt()) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - OnError(0); + OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT); return; }; if (g_dbglevel >= DBG_INFO) DbpString("WRITE BLOCK FINISHED"); - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + reply_ng(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_SUCCESS, NULL, 0); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index d1a59f095..d15ab183f 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -25,13 +25,14 @@ int16_t mifare_cmd_writeblocks(MifareWakeupType wakeup, uint8_t key_auth_cmd, ui void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key); void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); -void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); +void MifareUReadBlock(mful_readblock_t *packet); void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes); -void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes); -void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); -void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); +void MifareUL_AES_Auth(mfulaes_keys_t *packet); +void MifareUReadCard(mful_readblock_t *packet); + +void MifareUWriteBlockCompat(mful_writeblock_t *packet); +void MifareUWriteBlock(mful_writeblock_t *packet); void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key); void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index e0342b769..8b3d40cf2 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -18,6 +18,8 @@ //----------------------------------------------------------------------------- #include "mifareutil.h" +#include +#include #include "appmain.h" // tearoff hook #include "string.h" #include "BigBuf.h" @@ -29,6 +31,11 @@ #include "crc16.h" #include "protocols.h" #include "desfire_crypto.h" +#include "mbedtls/aes.h" +#include "cmac_calc.h" + +#define AES_BLOCK_SIZE 16 + // crypto1 helpers void mf_crypto1_decryptEx(struct Crypto1State *pcs, const uint8_t *data_in, int len, uint8_t *data_out) { @@ -92,7 +99,30 @@ uint16_t mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t * } uint16_t len = ReaderReceive(answer, answer_len, answer_parity); if (len == 0) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("%02X Cmd failed. Card timeout.", cmd); + if (g_dbglevel >= DBG_ERROR) + Dbprintf("%02X Cmd failed. Card timeout.", cmd); + len = ReaderReceive(answer, answer_len, answer_parity); + } + return len; +} + +// send X byte basic commands secure channel (UL AES) +uint16_t mifare_sendcmd_schann(uint8_t *data, uint8_t data_size, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing) { + + uint8_t dcmd[data_size + 2]; + if (data_size > 0) { + memcpy(dcmd, data, data_size); + } + + AddCrc14A(dcmd, data_size); + ReaderTransmit(dcmd, sizeof(dcmd), timing); + + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred + return 0; + } + + uint16_t len = ReaderReceive(answer, answer_len, answer_parity); + if (len == 0) { len = ReaderReceive(answer, answer_len, answer_parity); } return len; @@ -435,20 +465,20 @@ int mifare_ultra_auth(uint8_t *keybytes) { return 1; } -int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) { +int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann) { /// aes-128 uint8_t random_a[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - uint8_t random_b[16] = { 0 }; - uint8_t rnd_ab[32] = { 0 }; - uint8_t enc_rnd_ab[32] = { 0 }; - uint8_t IV[16] = { 0 }; - uint8_t key[16] = { 0 }; + uint8_t random_b[16] = { 0x00 }; + uint8_t rnd_ab[32] = { 0x00 }; + uint8_t enc_rnd_ab[32] = { 0x00 }; + uint8_t IV[16] = { 0x00 }; + uint8_t key[16] = { 0x00 }; memcpy(key, keybytes, sizeof(key)); // 1 cmd + 16 bytes + 2 crc - uint8_t resp[19] = {0x00}; - uint8_t respPar[5] = {0}; + uint8_t resp[19] = { 0x00 }; + uint8_t respPar[5] = { 0x00 }; // setup AES mbedtls_aes_context actx; @@ -458,7 +488,9 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) { // Send REQUEST AUTHENTICATION / receive tag nonce uint16_t len = mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_ULAES_AUTH_1, keyno, resp, sizeof(resp), respPar, NULL); if (len != 19) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); + } return 0; } @@ -488,71 +520,148 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) { // send & receive len = mifare_sendcmd(MIFARE_ULAES_AUTH_2, enc_rnd_ab, sizeof(enc_rnd_ab), resp, sizeof(resp), respPar, NULL); if (len != 19) { - if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); + if (g_dbglevel >= DBG_INFO) { + Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); + } return 0; } memset(IV, 0, 16); mbedtls_aes_setkey_dec(&actx, key, 128); - mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(random_b), IV, resp + 1, random_b); + uint8_t rec_rnd_a[16] = {0}; + mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(rec_rnd_a), IV, resp + 1, rec_rnd_a); mbedtls_aes_free(&actx); - if (memcmp(random_b, random_a, 16) != 0) { - if (g_dbglevel >= DBG_INFO) Dbprintf("failed authentication"); + if (memcmp(rec_rnd_a, random_a, 16) != 0) { + if (g_dbglevel >= DBG_INFO) { + Dbprintf("failed authentication"); + } return 0; } - if (g_dbglevel >= DBG_EXTENDED) { - - Dbprintf("e_AB:"); - Dbhexdump(32, enc_rnd_ab, false); - - Dbprintf("A:"); - Dbhexdump(16, random_a, false); - - Dbprintf("B:"); - Dbhexdump(16, random_b, false); + // If no secure messaging, return early. + if (schann == false) { + return 1; } + + // Session key calculation + if (g_dbglevel >= DBG_DEBUG) { + DbpString(""); + } + + // clear global session variable + init_secure_session(); + set_session_channel(schann); + ulaes_key_t *sobj = get_secure_session_obj(); + + // SV = 5Ah||A5h||00h||01h||00h||80h||RndA[15..14]||RndA[13..8] XOR RndB[15..10])||RndB[9..0]||RndA[7..0] + uint8_t *ra = random_a; + uint8_t *rb = random_b; + + // do we need to unroll it? + ror(rb, 16); + + uint8_t session_vec[] = { + 0x5a, 0xa5, // to be used for MAC + 0x00, 0x01, // counter + 0x00, 0x80, // 128 bit length 128 + ra[0], ra[1], + (ra[2] ^ rb[0]), + (ra[3] ^ rb[1]), + (ra[4] ^ rb[2]), + (ra[5] ^ rb[3]), + (ra[6] ^ rb[4]), + (ra[7] ^ rb[5]), + rb[6], rb[7], rb[8], rb[9], rb[10], rb[11], rb[12], rb[13], rb[14], rb[15], + ra[8], ra[9], ra[10], ra[11], ra[12], ra[13], ra[14], ra[15] + }; + + // session key is the CMAC of session_vec using the KEY + ulaes_cmac(key, sizeof(key), session_vec, sizeof(session_vec), sobj->sessionkey); return 1; } static int mifare_ultra_readblockEx(uint8_t blockNo, uint8_t *blockData) { - uint16_t len = 0; - uint8_t bt[2] = {0x00, 0x00}; + uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; + uint16_t len = 0; + uint8_t offset = 0; + + ulaes_key_t *sobj = get_secure_session_obj(); + + if (sobj->use_schann) { + offset = 8; + uint8_t mac[16] = {0}; + // counter 2b, cmd, arg, 8byte cmac, 2b crc + uint8_t cmd_mac[2 + 2 + 8] = { + sobj->counter & 0xFF, + (sobj->counter >> 8) & 0xFF, + ISO14443A_CMD_READBLOCK, blockNo + }; + ulaes_cmac(sobj->sessionkey, sizeof(sobj->sessionkey), cmd_mac, 4, mac); + ulaes_cmac8(mac, cmd_mac + 4); + + if (g_dbglevel >= DBG_DEBUG) { + print_result("cntr || cmd...", cmd_mac, 4); + print_result("session key...", sobj->sessionkey, 16); + print_result("session mac...", mac, 16); + print_result("calc cmac.....", cmd_mac, sizeof(cmd_mac)); + print_result("session cmd data", cmd_mac + 4, 8); + } + + increase_session_counter(); + + len = mifare_sendcmd_schann(cmd_mac + 2, sizeof(cmd_mac) - 2, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); + } else { + len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); + } - len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); if (len == 1) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; - } - if (len != 18) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len); - return 2; + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + } + return PM3_ECARDEXCHANGE; } - memcpy(bt, receivedAnswer + 16, 2); - AddCrc14A(receivedAnswer, 16); - if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd CRC response error."); - return 3; + // Ev1 / Ul-C == 18 bytes response + // UL-Aes in secure messaging == 26 + if (len != 18 + offset) { + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Cmd Error: card timeout. len: %x", len); + } + return PM3_ETIMEOUT; } + uint8_t bt[2] = {0x00, 0x00}; + memcpy(bt, receivedAnswer + 16 + offset, 2); + AddCrc14A(receivedAnswer, 16 + offset); + if (bt[0] != receivedAnswer[16 + offset] || bt[1] != receivedAnswer[17 + offset]) { + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Cmd CRC response error."); + } + return PM3_ECRC; + } + + // we are skipping verifying the cmac, since we don't care. + // increase counter for the read response + increase_session_counter(); + memcpy(blockData, receivedAnswer, 16); - return 0; + return PM3_SUCCESS; } + int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { #define MFU_MAX_RETRIES 5 - uint8_t res; + int res; for (uint8_t retries = 0; retries < MFU_MAX_RETRIES; ++retries) { res = mifare_ultra_readblockEx(blockNo, blockData); - // break if OK, or NACK. + // break if OK, or NACK. switch (res) { - case 0: - case 1: + case PM3_SUCCESS: + case PM3_ECARDEXCHANGE: return res; default: continue; @@ -574,8 +683,10 @@ int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint8_t blockNo, uint // MIFARE_MAGIC_GDM_WRITEBLOCK or MIFARE_MAGIC_GDM_WRITE_CFG for certain magic tags uint16_t len = mifare_sendcmd_short(pcs, 1, cmd, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { + // 0x0a - ACK + if (g_dbglevel >= DBG_INFO) + Dbprintf("Cmd Error: %02x", receivedAnswer[0]); return PM3_EFAILED; } @@ -642,8 +753,10 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo // Send increment or decrement command len = mifare_sendcmd_short(pcs, 1, command, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { + // 0x0a - ACK + if (g_dbglevel >= DBG_INFO) + Dbprintf("Cmd Error: %02x", receivedAnswer[0]); return PM3_EFAILED; } @@ -661,7 +774,8 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo // Receive the response NO Response means OK ... i.e. NOT NACK len = ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar); - if (len != 0) { // Something not right, len == 0 (no response is ok as its waiting for transfer + if (len != 0) { + // Something not right, len == 0 (no response is ok as its waiting for transfer uint8_t res = 0; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; @@ -679,21 +793,19 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { // variables - uint16_t len = 0; - - uint8_t d_block[18]; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + uint16_t len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); - len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); - - if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK + if (receivedAnswer[0] != CARD_ACK) { + // 0x0a - ACK if (g_dbglevel >= DBG_INFO) { Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len); } return PM3_EFAILED; } + uint8_t d_block[18]; memcpy(d_block, blockData, 16); AddCrc14A(d_block, 16); @@ -702,32 +814,86 @@ int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { // Receive the response len = ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar); - if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK + if (receivedAnswer[0] != CARD_ACK) { + // 0x0a - ACK if (g_dbglevel >= DBG_INFO) { Dbprintf("Cmd Send Data Error: %02x %d", receivedAnswer[0], len); } return PM3_EFAILED; } + return PM3_SUCCESS; } int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData) { - uint16_t len = 0; - uint8_t block[5] = {blockNo, 0x00, 0x00, 0x00, 0x00 }; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + uint16_t len = 0; - // command MIFARE_CLASSIC_WRITEBLOCK - memcpy(block + 1, blockData, 4); + ulaes_key_t *sobj = get_secure_session_obj(); - len = mifare_sendcmd(MIFARE_ULC_WRITE, block, sizeof(block), receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); + if (sobj->use_schann) { - if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK - if (g_dbglevel >= DBG_INFO) { - Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len); + uint8_t mac[16] = {0}; + /* + 2b counter + 1b cmd + 1b blockno + 4b data + 8b cmac + 2b crc --> not included + */ + uint8_t cmd_mac[2 + 1 + 1 + 4 + 8] = { + sobj->counter & 0xFF, + (sobj->counter >> 8) & 0xFF, + MIFARE_ULC_WRITE, + blockNo + }; + memcpy(cmd_mac + 4, blockData, 4); + + ulaes_cmac(sobj->sessionkey, sizeof(sobj->sessionkey), cmd_mac, 8, mac); + ulaes_cmac8(mac, cmd_mac + 8); + + if (g_dbglevel >= DBG_DEBUG) { + print_result("cntr || cmd...", cmd_mac, 8); + print_result("session key...", sobj->sessionkey, 16); + print_result("session mac...", mac, 16); + print_result("calc cmac.....", cmd_mac, sizeof(cmd_mac)); + print_result("session cmd data", cmd_mac + 8, 8); + } + + increase_session_counter(); + + len = mifare_sendcmd_schann(cmd_mac + 2, sizeof(cmd_mac) - 2, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); + + // we are skipping verifying the cmac, since we don't care. + // increase counter for the write response + increase_session_counter(); + + if (len != 10) { + if (g_dbglevel >= DBG_INFO) { + Dbprintf("Cmd Send Error: " _RED_("%d"), len); + } + return PM3_EFAILED; + } + + } else { + + // command MIFARE_CLASSIC_WRITEBLOCK + uint8_t block[5] = {blockNo, 0x00, 0x00, 0x00, 0x00 }; + memcpy(block + 1, blockData, 4); + len = mifare_sendcmd(MIFARE_ULC_WRITE, block, sizeof(block), receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL); + + if (receivedAnswer[0] != CARD_ACK) { + + if (g_dbglevel >= DBG_INFO) { + Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len); + } + return PM3_EFAILED; } - return PM3_EFAILED; } + return PM3_SUCCESS; } @@ -745,7 +911,6 @@ int mifare_ultra_halt(void) { return mifare_classic_halt(NULL); } - // Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards), // plus evtl. 8 sectors with 16 blocks each (4k cards) uint8_t NumBlocksPerSector(uint8_t sectorNo) { diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 28987c363..1e319ff6c 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -23,6 +23,7 @@ #include "common.h" #include "crapto1/crapto1.h" + // mifare authentication #define CRYPT_NONE 0 #define CRYPT_ALL 1 @@ -82,6 +83,8 @@ #endif //functions +uint16_t mifare_sendcmd_schann(uint8_t *data, uint8_t data_size, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing); + uint16_t mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing); uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing); @@ -102,7 +105,7 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo // Ultralight/NTAG... int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack); int mifare_ultra_auth(uint8_t *keybytes); -int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes); +int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann); int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData); int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData); int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData);