mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-03-06 21:19:18 +08:00
- make tearoff_delay_us and tearoff_enabled globals - use tearoff_hook and remove Dbprintf in critical tearoff timing - move initial write from MifareU_Otp_Tearoff to CmdHF14AMfuOtpTearoff and make it optional (old behavior was writing initial 00000000 when -d was not provided) - tearoff: compare with initial write, not with previous tearoff outcome - rephrase some messages - track all begin and end of erase and write phases, with quite complex logic to cover multiple cases (starting in middle of erased phase, starting with write 0, ...) and report them - check against initial write error - repeat same timing (up to 10x) in case of write/read errors then quit - typos
2795 lines
82 KiB
C
2795 lines
82 KiB
C
//-----------------------------------------------------------------------------
|
|
// Merlok - June 2011, 2012
|
|
// Gerhard de Koning Gans - May 2008
|
|
// Hagen Fritsch - June 2010
|
|
// Midnitesnake - Dec 2013
|
|
// Andy Davies - Apr 2014
|
|
// Iceman - May 2014,2015,2016
|
|
//
|
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
|
// the license.
|
|
//-----------------------------------------------------------------------------
|
|
// Routines to support ISO 14443 type A.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "mifarecmd.h"
|
|
|
|
#include "pmflash.h"
|
|
#include "proxmark3_arm.h"
|
|
#include "string.h"
|
|
#include "mifareutil.h"
|
|
#include "protocols.h"
|
|
#include "parity.h"
|
|
#include "BigBuf.h"
|
|
#include "cmd.h"
|
|
#include "flashmem.h"
|
|
#include "fpgaloader.h"
|
|
#include "iso14443a.h"
|
|
#include "mifaredesfire.h"
|
|
#include "util.h"
|
|
#include "commonutil.h"
|
|
#include "crc16.h"
|
|
#include "dbprint.h"
|
|
#include "ticks.h"
|
|
#include "usb_cdc.h" // usb_poll_validate_length
|
|
#include "spiffs.h" // spiffs
|
|
#include "appmain.h" // print_stack_usage
|
|
|
|
#ifndef HARDNESTED_AUTHENTICATION_TIMEOUT
|
|
# define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation)
|
|
#endif
|
|
#ifndef HARDNESTED_PRE_AUTHENTICATION_LEADTIME
|
|
# define HARDNESTED_PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication
|
|
#endif
|
|
|
|
// send an incomplete dummy response in order to trigger the card's authentication failure timeout
|
|
#ifndef CHK_TIMEOUT
|
|
# define CHK_TIMEOUT(void) { \
|
|
ReaderTransmit(&dummy_answer, 1, NULL); \
|
|
uint32_t timeout = GetCountSspClk() + HARDNESTED_AUTHENTICATION_TIMEOUT; \
|
|
while (GetCountSspClk() < timeout) {}; \
|
|
}
|
|
#endif
|
|
|
|
static uint8_t dummy_answer = 0;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Select, Authenticate, Read a MIFARE tag.
|
|
// read block
|
|
//-----------------------------------------------------------------------------
|
|
void MifareReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *datain) {
|
|
// params
|
|
uint64_t ui64Key = 0;
|
|
ui64Key = bytes_to_num(datain, 6);
|
|
|
|
// variables
|
|
uint8_t dataoutbuf[16] = {0x00};
|
|
uint8_t uid[10] = {0x00};
|
|
uint32_t cuid = 0, status = PM3_EOPABORTED;
|
|
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
LED_A_ON();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
|
|
while (true) {
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
break;
|
|
};
|
|
|
|
if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error");
|
|
break;
|
|
};
|
|
|
|
if (mifare_classic_readblock(pcs, cuid, blockNo, dataoutbuf)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Read block error");
|
|
break;
|
|
};
|
|
|
|
if (mifare_classic_halt(pcs, cuid)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
break;
|
|
};
|
|
|
|
status = PM3_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
crypto1_deinit(pcs);
|
|
|
|
if (DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED");
|
|
|
|
LED_B_ON();
|
|
reply_ng(CMD_HF_MIFARE_READBL, status, dataoutbuf, 16);
|
|
LED_B_OFF();
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
}
|
|
|
|
void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes) {
|
|
|
|
bool turnOffField = (arg0 == 1);
|
|
|
|
LED_A_ON();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
if (!mifare_ultra_auth(keybytes)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Authentication failed");
|
|
OnError(1);
|
|
return;
|
|
}
|
|
|
|
if (turnOffField) {
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
}
|
|
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
|
}
|
|
|
|
// 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 useKey = (arg1 == 1); //UL_C
|
|
bool usePwd = (arg1 == 2); //UL_EV1/NTAG
|
|
|
|
LEDsoff();
|
|
LED_A_ON();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true);
|
|
if (!len) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card (RC:%02X)", len);
|
|
OnError(1);
|
|
return;
|
|
}
|
|
|
|
// UL-C authentication
|
|
if (useKey) {
|
|
uint8_t key[16] = {0x00};
|
|
memcpy(key, datain, sizeof(key));
|
|
|
|
if (!mifare_ultra_auth(key)) {
|
|
OnError(1);
|
|
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);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mifare_ultra_readblock(blockNo, dataout)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Read block error");
|
|
OnError(2);
|
|
return;
|
|
}
|
|
|
|
if (mifare_ultra_halt()) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
OnError(3);
|
|
return;
|
|
}
|
|
|
|
reply_mix(CMD_ACK, 1, 0, 0, dataout, 16);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Select, Authenticate, Read a MIFARE tag.
|
|
// read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes)
|
|
//-----------------------------------------------------------------------------
|
|
void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
|
|
// params
|
|
uint8_t sectorNo = arg0;
|
|
uint8_t keyType = arg1;
|
|
uint64_t ui64Key = 0;
|
|
ui64Key = bytes_to_num(datain, 6);
|
|
|
|
// variables
|
|
uint8_t isOK = 0;
|
|
uint8_t dataoutbuf[16 * 16];
|
|
uint8_t uid[10] = {0x00};
|
|
uint32_t cuid = 0;
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
LED_A_ON();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
|
|
isOK = 1;
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
isOK = 0;
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
}
|
|
|
|
|
|
if (isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) {
|
|
isOK = 0;
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error");
|
|
}
|
|
|
|
for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) {
|
|
if (mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf + 16 * blockNo)) {
|
|
isOK = 0;
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Read sector %2d block %2d error", sectorNo, blockNo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mifare_classic_halt(pcs, cuid)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
}
|
|
|
|
if (DBGLEVEL >= 2) DbpString("READ SECTOR FINISHED");
|
|
|
|
crypto1_deinit(pcs);
|
|
|
|
LED_B_ON();
|
|
reply_old(CMD_ACK, isOK, 0, 0, dataoutbuf, 16 * NumBlocksPerSector(sectorNo));
|
|
LED_B_OFF();
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
|
|
// arg0 = blockNo (start)
|
|
// arg1 = Pages (number of blocks)
|
|
// arg2 = useKey
|
|
// datain = KEY bytes
|
|
void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) {
|
|
LEDsoff();
|
|
LED_A_ON();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
// free eventually allocated BigBuf memory
|
|
BigBuf_free();
|
|
BigBuf_Clear_ext(false);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
// params
|
|
uint8_t blockNo = arg0;
|
|
uint16_t blocks = arg1;
|
|
bool useKey = (arg2 == 1); //UL_C
|
|
bool usePwd = (arg2 == 2); //UL_EV1/NTAG
|
|
uint32_t countblocks = 0;
|
|
uint8_t *dataout = BigBuf_malloc(CARD_MEMORY_SIZE);
|
|
if (dataout == NULL) {
|
|
Dbprintf("out of memory");
|
|
OnError(1);
|
|
return;
|
|
}
|
|
|
|
int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true);
|
|
if (!len) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card (RC:%d)", len);
|
|
OnError(1);
|
|
return;
|
|
}
|
|
|
|
// UL-C authentication
|
|
if (useKey) {
|
|
uint8_t key[16] = {0x00};
|
|
memcpy(key, datain, sizeof(key));
|
|
|
|
if (!mifare_ultra_auth(key)) {
|
|
OnError(1);
|
|
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)) {
|
|
OnError(1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < blocks; i++) {
|
|
if ((i * 4) + 4 >= CARD_MEMORY_SIZE) {
|
|
Dbprintf("Data exceeds buffer!!");
|
|
break;
|
|
}
|
|
|
|
len = mifare_ultra_readblock(blockNo + i, dataout + 4 * i);
|
|
|
|
if (len) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Read block %d error", i);
|
|
// if no blocks read - error out
|
|
if (i == 0) {
|
|
OnError(2);
|
|
return;
|
|
} else {
|
|
//stop at last successful read block and return what we got
|
|
break;
|
|
}
|
|
} else {
|
|
countblocks++;
|
|
}
|
|
}
|
|
|
|
len = mifare_ultra_halt();
|
|
if (len) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
OnError(3);
|
|
return;
|
|
}
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Blocks read %d", countblocks);
|
|
|
|
countblocks *= 4;
|
|
|
|
reply_mix(CMD_ACK, 1, countblocks, dataout - BigBuf_get_addr(), 0, 0);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
BigBuf_free();
|
|
set_tracing(false);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Select, Authenticate, Write a MIFARE tag.
|
|
// read block
|
|
//-----------------------------------------------------------------------------
|
|
void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
|
|
// params
|
|
uint8_t blockNo = arg0;
|
|
uint8_t keyType = arg1;
|
|
uint64_t ui64Key = 0;
|
|
uint8_t blockdata[16] = {0x00};
|
|
|
|
ui64Key = bytes_to_num(datain, 6);
|
|
memcpy(blockdata, datain + 10, 16);
|
|
|
|
// variables
|
|
uint8_t isOK = 0;
|
|
uint8_t uid[10] = {0x00};
|
|
uint32_t cuid = 0;
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
LED_A_ON();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
|
|
while (true) {
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
break;
|
|
};
|
|
|
|
if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error");
|
|
break;
|
|
};
|
|
|
|
if (mifare_classic_writeblock(pcs, cuid, blockNo, blockdata)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error");
|
|
break;
|
|
};
|
|
|
|
if (mifare_classic_halt(pcs, cuid)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
break;
|
|
};
|
|
|
|
isOK = 1;
|
|
break;
|
|
}
|
|
|
|
crypto1_deinit(pcs);
|
|
|
|
if (DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED");
|
|
|
|
reply_mix(CMD_ACK, isOK, 0, 0, 0, 0);
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
|
|
// Arg0 : Block to write to.
|
|
// Arg1 : 0 = use no authentication.
|
|
// 1 = use 0x1A authentication.
|
|
// 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 useKey = (arg1 == 1); //UL_C
|
|
bool usePwd = (arg1 == 2); //UL_EV1/NTAG
|
|
uint8_t blockdata[4] = {0x00};
|
|
|
|
memcpy(blockdata, datain, 4);
|
|
|
|
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)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
// UL-C authentication
|
|
if (useKey) {
|
|
uint8_t key[16] = {0x00};
|
|
memcpy(key, datain + 4, sizeof(key));
|
|
|
|
if (!mifare_ultra_auth(key)) {
|
|
OnError(1);
|
|
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)) {
|
|
OnError(1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mifare_ultra_writeblock(blockNo, blockdata)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
if (mifare_ultra_halt()) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
if (DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED");
|
|
|
|
if (reply)
|
|
reply_mix(CMD_ACK, 1, 0, 0, 0, 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);
|
|
}
|
|
|
|
// Arg0 : Block to write to.
|
|
// Arg1 : 0 = use no authentication.
|
|
// 1 = use 0x1A authentication.
|
|
// 2 = use 0x1B authentication.
|
|
// datain : 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 useKey = (arg1 == 1); //UL_C
|
|
bool usePwd = (arg1 == 2); //UL_EV1/NTAG
|
|
uint8_t blockdata[16] = {0x00};
|
|
|
|
memcpy(blockdata, datain, 16);
|
|
|
|
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)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
// UL-C authentication
|
|
if (useKey) {
|
|
uint8_t key[16] = {0x00};
|
|
memcpy(key, datain + 16, sizeof(key));
|
|
|
|
if (!mifare_ultra_auth(key)) {
|
|
OnError(1);
|
|
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);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mifare_ultra_writeblock_compat(blockNo, blockdata)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
if (mifare_ultra_halt()) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
if (DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED");
|
|
|
|
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
|
|
void MifareUSetPwd(uint8_t arg0, uint8_t *datain) {
|
|
|
|
uint8_t pwd[16] = {0x00};
|
|
uint8_t blockdata[4] = {0x00};
|
|
|
|
memcpy(pwd, datain, 16);
|
|
|
|
LED_A_ON();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
blockdata[0] = pwd[7];
|
|
blockdata[1] = pwd[6];
|
|
blockdata[2] = pwd[5];
|
|
blockdata[3] = pwd[4];
|
|
if (mifare_ultra_writeblock(44, blockdata)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error");
|
|
OnError(44);
|
|
return;
|
|
};
|
|
|
|
blockdata[0] = pwd[3];
|
|
blockdata[1] = pwd[2];
|
|
blockdata[2] = pwd[1];
|
|
blockdata[3] = pwd[0];
|
|
if (mifare_ultra_writeblock(45, blockdata)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error");
|
|
OnError(45);
|
|
return;
|
|
};
|
|
|
|
blockdata[0] = pwd[15];
|
|
blockdata[1] = pwd[14];
|
|
blockdata[2] = pwd[13];
|
|
blockdata[3] = pwd[12];
|
|
if (mifare_ultra_writeblock(46, blockdata)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error");
|
|
OnError(46);
|
|
return;
|
|
};
|
|
|
|
blockdata[0] = pwd[11];
|
|
blockdata[1] = pwd[10];
|
|
blockdata[2] = pwd[9];
|
|
blockdata[3] = pwd[8];
|
|
if (mifare_ultra_writeblock(47, blockdata)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error");
|
|
OnError(47);
|
|
return;
|
|
};
|
|
|
|
if (mifare_ultra_halt()) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
OnError(0);
|
|
return;
|
|
};
|
|
|
|
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
|
|
// Return 1 if the nonce is invalid else return 0
|
|
static int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) {
|
|
return (
|
|
(oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) && \
|
|
(oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) && \
|
|
(oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0)))
|
|
) ? 1 : 0;
|
|
}
|
|
|
|
void MifareAcquireNonces(uint32_t arg0, uint32_t flags) {
|
|
|
|
uint8_t uid[10] = {0x00};
|
|
uint8_t answer[MAX_MIFARE_FRAME_SIZE] = {0x00};
|
|
uint8_t par[1] = {0x00};
|
|
uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00};
|
|
uint32_t cuid = 0;
|
|
int16_t isOK = 0;
|
|
uint16_t num_nonces = 0;
|
|
uint8_t cascade_levels = 0;
|
|
uint8_t blockNo = arg0 & 0xff;
|
|
uint8_t keyType = (arg0 >> 8) & 0xff;
|
|
bool initialize = flags & 0x0001;
|
|
bool field_off = flags & 0x0004;
|
|
bool have_uid = false;
|
|
|
|
LED_A_ON();
|
|
LED_C_OFF();
|
|
|
|
BigBuf_free();
|
|
BigBuf_Clear_ext(false);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
if (initialize)
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
LED_C_ON();
|
|
|
|
while (num_nonces < PM3_CMD_DATA_SIZE / 4) {
|
|
|
|
// Test if the action was cancelled
|
|
if (BUTTON_PRESS()) {
|
|
isOK = 2;
|
|
field_off = true;
|
|
break;
|
|
}
|
|
|
|
if (!have_uid) { // need a full select cycle to get the uid first
|
|
iso14a_card_select_t card_info;
|
|
if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (ALL)");
|
|
continue;
|
|
}
|
|
switch (card_info.uidlen) {
|
|
case 4 :
|
|
cascade_levels = 1;
|
|
break;
|
|
case 7 :
|
|
cascade_levels = 2;
|
|
break;
|
|
case 10:
|
|
cascade_levels = 3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
have_uid = true;
|
|
} else { // no need for anticollision. We can directly select the card
|
|
if (!iso14443a_fast_select_card(uid, cascade_levels)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (UID)");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Transmit MIFARE_CLASSIC_AUTH
|
|
uint8_t dcmd[4] = {0x60 + (keyType & 0x01), blockNo, 0x00, 0x00};
|
|
AddCrc14A(dcmd, 2);
|
|
ReaderTransmit(dcmd, sizeof(dcmd), NULL);
|
|
int len = ReaderReceive(answer, par);
|
|
|
|
// wait for the card to become ready again
|
|
CHK_TIMEOUT();
|
|
|
|
if (len != 4) {
|
|
if (DBGLEVEL >= 2) Dbprintf("AcquireNonces: Auth1 error");
|
|
continue;
|
|
}
|
|
|
|
// Save the tag nonce (nt)
|
|
memcpy(buf + num_nonces * 4, answer, 4);
|
|
num_nonces++;
|
|
}
|
|
|
|
LED_C_OFF();
|
|
LED_B_ON();
|
|
reply_old(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf));
|
|
LED_B_OFF();
|
|
|
|
if (DBGLEVEL >= 3) DbpString("AcquireNonces finished");
|
|
|
|
if (field_off) {
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// acquire encrypted nonces in order to perform the attack described in
|
|
// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened
|
|
// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on
|
|
// Computer and Communications Security, 2015
|
|
//-----------------------------------------------------------------------------
|
|
void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain) {
|
|
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
uint8_t uid[10] = {0x00};
|
|
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
|
|
uint8_t par_enc[1] = {0x00};
|
|
uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00};
|
|
|
|
uint64_t ui64Key = bytes_to_num(datain, 6);
|
|
uint32_t cuid = 0;
|
|
int16_t isOK = 0;
|
|
uint16_t num_nonces = 0;
|
|
uint8_t nt_par_enc = 0;
|
|
uint8_t cascade_levels = 0;
|
|
uint8_t blockNo = arg0 & 0xff;
|
|
uint8_t keyType = (arg0 >> 8) & 0xff;
|
|
uint8_t targetBlockNo = arg1 & 0xff;
|
|
uint8_t targetKeyType = (arg1 >> 8) & 0xff;
|
|
bool initialize = flags & 0x0001;
|
|
bool slow = flags & 0x0002;
|
|
bool field_off = flags & 0x0004;
|
|
bool have_uid = false;
|
|
|
|
LED_A_ON();
|
|
LED_C_OFF();
|
|
|
|
BigBuf_free();
|
|
BigBuf_Clear_ext(false);
|
|
clear_trace();
|
|
set_tracing(false);
|
|
|
|
if (initialize)
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
LED_C_ON();
|
|
|
|
for (uint16_t i = 0; i <= PM3_CMD_DATA_SIZE - 9;) {
|
|
|
|
// Test if the action was cancelled
|
|
if (BUTTON_PRESS()) {
|
|
isOK = 2;
|
|
field_off = true;
|
|
break;
|
|
}
|
|
|
|
if (!have_uid) { // need a full select cycle to get the uid first
|
|
iso14a_card_select_t card_info;
|
|
if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Can't select card (ALL)");
|
|
continue;
|
|
}
|
|
switch (card_info.uidlen) {
|
|
case 4 :
|
|
cascade_levels = 1;
|
|
break;
|
|
case 7 :
|
|
cascade_levels = 2;
|
|
break;
|
|
case 10:
|
|
cascade_levels = 3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
have_uid = true;
|
|
} else { // no need for anticollision. We can directly select the card
|
|
if (!iso14443a_fast_select_card(uid, cascade_levels)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Can't select card (UID)");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (slow)
|
|
SpinDelayUs(HARDNESTED_PRE_AUTHENTICATION_LEADTIME);
|
|
|
|
uint32_t nt1;
|
|
if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Auth1 error");
|
|
continue;
|
|
}
|
|
|
|
// nested authentication
|
|
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par_enc, NULL);
|
|
|
|
// wait for the card to become ready again
|
|
CHK_TIMEOUT();
|
|
|
|
if (len != 4) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Auth2 error len=%d", len);
|
|
continue;
|
|
}
|
|
|
|
num_nonces++;
|
|
if (num_nonces % 2) {
|
|
memcpy(buf + i, receivedAnswer, 4);
|
|
nt_par_enc = par_enc[0] & 0xf0;
|
|
} else {
|
|
nt_par_enc |= par_enc[0] >> 4;
|
|
memcpy(buf + i + 4, receivedAnswer, 4);
|
|
memcpy(buf + i + 8, &nt_par_enc, 1);
|
|
i += 9;
|
|
}
|
|
}
|
|
|
|
LED_C_OFF();
|
|
crypto1_deinit(pcs);
|
|
LED_B_ON();
|
|
reply_old(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf));
|
|
LED_B_OFF();
|
|
|
|
if (DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished");
|
|
|
|
if (field_off) {
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// MIFARE nested authentication.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key) {
|
|
uint64_t ui64Key = 0;
|
|
ui64Key = bytes_to_num(key, 6);
|
|
|
|
// variables
|
|
uint16_t i, j, len;
|
|
static uint16_t dmin, dmax;
|
|
|
|
uint8_t par[1] = {0x00};
|
|
uint8_t par_array[4] = {0x00};
|
|
uint8_t uid[10] = {0x00};
|
|
uint32_t cuid = 0, nt1, nt2, nttest, ks1;
|
|
uint32_t target_nt[2] = {0x00}, target_ks[2] = {0x00};
|
|
|
|
uint16_t ncount = 0;
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
|
|
|
|
uint32_t auth1_time, auth2_time;
|
|
static uint16_t delta_time = 0;
|
|
|
|
LED_A_ON();
|
|
LED_C_OFF();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
// free eventually allocated BigBuf memory
|
|
BigBuf_free();
|
|
BigBuf_Clear_ext(false);
|
|
|
|
if (calibrate)
|
|
clear_trace();
|
|
|
|
set_tracing(true);
|
|
|
|
// statistics on nonce distance
|
|
int16_t isOK = PM3_SUCCESS;
|
|
#define NESTED_MAX_TRIES 12
|
|
if (calibrate) { // calibrate: for first call only. Otherwise reuse previous calibration
|
|
LED_B_ON();
|
|
WDT_HIT();
|
|
|
|
uint16_t unsuccessful_tries = 0;
|
|
uint16_t davg = 0;
|
|
dmax = 0;
|
|
dmin = 2000;
|
|
delta_time = 0;
|
|
uint16_t rtr;
|
|
for (rtr = 0; rtr < 17; rtr++) {
|
|
|
|
// Test if the action was cancelled
|
|
if (BUTTON_PRESS() || data_available()) {
|
|
isOK = PM3_EOPABORTED;
|
|
break;
|
|
}
|
|
|
|
// prepare next select. No need to power down the card.
|
|
if (mifare_classic_halt(pcs, cuid)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Halt error");
|
|
rtr--;
|
|
continue;
|
|
}
|
|
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Can't select card");
|
|
rtr--;
|
|
continue;
|
|
};
|
|
|
|
auth1_time = 0;
|
|
if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth1 error");
|
|
rtr--;
|
|
continue;
|
|
};
|
|
auth2_time = (delta_time) ? auth1_time + delta_time : 0;
|
|
|
|
if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, &auth2_time)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth2 error");
|
|
rtr--;
|
|
continue;
|
|
};
|
|
|
|
// cards with fixed nonce
|
|
if (nt1 == nt2) {
|
|
Dbprintf("Nested: %08x vs %08x", nt1, nt2);
|
|
break;
|
|
}
|
|
|
|
uint32_t nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160
|
|
for (i = 101; i < 1200; i++) {
|
|
nttmp = prng_successor(nttmp, 1);
|
|
if (nttmp == nt2) break;
|
|
}
|
|
|
|
if (i != 1200) {
|
|
if (rtr != 0) {
|
|
davg += i;
|
|
dmin = MIN(dmin, i);
|
|
dmax = MAX(dmax, i);
|
|
} else {
|
|
delta_time = auth2_time - auth1_time + 32; // allow some slack for proper timing
|
|
}
|
|
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nested: calibrating... ntdist=%d", i);
|
|
} else {
|
|
unsuccessful_tries++;
|
|
if (unsuccessful_tries > NESTED_MAX_TRIES) { // card isn't vulnerable to nested attack (random numbers are not predictable)
|
|
isOK = PM3_EFAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rtr > 1)
|
|
davg = (davg + (rtr - 1) / 2) / (rtr - 1);
|
|
|
|
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("rtr=%d isOK=%d min=%d max=%d avg=%d, delta_time=%d", rtr, isOK, dmin, dmax, davg, delta_time);
|
|
|
|
dmin = davg - 2;
|
|
dmax = davg + 2;
|
|
|
|
LED_B_OFF();
|
|
}
|
|
// -------------------------------------------------------------------------------------------------
|
|
|
|
LED_C_ON();
|
|
|
|
// get crypted nonces for target sector
|
|
for (i = 0; i < 2 && !isOK; i++) { // look for exactly two different nonces
|
|
|
|
target_nt[i] = 0;
|
|
while (target_nt[i] == 0) { // continue until we have an unambiguous nonce
|
|
|
|
// Test if the action was cancelled
|
|
if (BUTTON_PRESS() || data_available()) {
|
|
isOK = PM3_EOPABORTED;
|
|
break;
|
|
}
|
|
|
|
// prepare next select. No need to power down the card.
|
|
if (mifare_classic_halt(pcs, cuid)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Halt error");
|
|
continue;
|
|
}
|
|
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Can't select card");
|
|
continue;
|
|
};
|
|
|
|
auth1_time = 0;
|
|
if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth1 error");
|
|
continue;
|
|
};
|
|
|
|
// nested authentication
|
|
auth2_time = auth1_time + delta_time;
|
|
|
|
len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, &auth2_time);
|
|
if (len != 4) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth2 error len=%d", len);
|
|
continue;
|
|
};
|
|
|
|
nt2 = bytes_to_num(receivedAnswer, 4);
|
|
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nonce#%d: Testing nt1=%08x nt2enc=%08x nt2par=%02x", i + 1, nt1, nt2, par[0]);
|
|
|
|
// Parity validity check
|
|
for (j = 0; j < 4; j++) {
|
|
par_array[j] = (oddparity8(receivedAnswer[j]) != ((par[0] >> (7 - j)) & 0x01));
|
|
}
|
|
|
|
ncount = 0;
|
|
nttest = prng_successor(nt1, dmin - 1);
|
|
for (j = dmin; j < dmax + 1; j++) {
|
|
nttest = prng_successor(nttest, 1);
|
|
ks1 = nt2 ^ nttest;
|
|
|
|
if (valid_nonce(nttest, nt2, ks1, par_array)) {
|
|
if (ncount > 0) { // we are only interested in disambiguous nonces, try again
|
|
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nonce#%d: dismissed (ambiguous), ntdist=%d", i + 1, j);
|
|
target_nt[i] = 0;
|
|
break;
|
|
}
|
|
target_nt[i] = nttest;
|
|
target_ks[i] = ks1;
|
|
ncount++;
|
|
if (i == 1 && target_nt[1] == target_nt[0]) { // we need two different nonces
|
|
target_nt[i] = 0;
|
|
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nonce#2: dismissed (= nonce#1), ntdist=%d", j);
|
|
break;
|
|
}
|
|
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nonce#%d: valid, ntdist=%d", i + 1, j);
|
|
}
|
|
}
|
|
if (target_nt[i] == 0 && j == dmax + 1 && DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (all invalid)", i + 1);
|
|
}
|
|
}
|
|
|
|
LED_C_OFF();
|
|
|
|
crypto1_deinit(pcs);
|
|
|
|
struct p {
|
|
int16_t isOK;
|
|
uint8_t block;
|
|
uint8_t keytype;
|
|
uint8_t cuid[4];
|
|
uint8_t nt_a[4];
|
|
uint8_t ks_a[4];
|
|
uint8_t nt_b[4];
|
|
uint8_t ks_b[4];
|
|
} PACKED payload;
|
|
payload.isOK = isOK;
|
|
payload.block = targetBlockNo;
|
|
payload.keytype = targetKeyType;
|
|
|
|
memcpy(payload.cuid, &cuid, 4);
|
|
memcpy(payload.nt_a, &target_nt[0], 4);
|
|
memcpy(payload.ks_a, &target_ks[0], 4);
|
|
memcpy(payload.nt_b, &target_nt[1], 4);
|
|
memcpy(payload.ks_b, &target_ks[1], 4);
|
|
|
|
reply_ng(CMD_HF_MIFARE_NESTED, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload));
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
|
|
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key) {
|
|
|
|
LEDsoff();
|
|
|
|
uint64_t ui64Key = 0;
|
|
ui64Key = bytes_to_num(key, 6);
|
|
uint16_t len;
|
|
uint8_t uid[10] = {0x00};
|
|
uint32_t cuid = 0, nt1, nt2;
|
|
uint32_t target_nt = 0, target_ks = 0;
|
|
uint8_t par[1] = {0x00};
|
|
uint8_t receivedAnswer[10] = {0x00};
|
|
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
LED_A_ON();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
// free eventually allocated BigBuf memory
|
|
BigBuf_free();
|
|
BigBuf_Clear_ext(false);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
int16_t isOK = 0;
|
|
LED_C_ON();
|
|
|
|
for (uint8_t retry = 0; retry < 3 && (isOK == 0); retry++) {
|
|
|
|
WDT_HIT();
|
|
|
|
// prepare next select. No need to power down the card.
|
|
if (mifare_classic_halt(pcs, cuid)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Halt error");
|
|
retry--;
|
|
continue;
|
|
}
|
|
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Can't select card");
|
|
retry--;
|
|
continue;
|
|
};
|
|
|
|
// First authentication. Normal auth.
|
|
if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth1 error");
|
|
retry--;
|
|
continue;
|
|
};
|
|
|
|
// second authentication. Nested auth
|
|
len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, NULL);
|
|
if (len != 4) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth2 error len=%d", len);
|
|
continue;
|
|
};
|
|
|
|
nt2 = bytes_to_num(receivedAnswer, 4);
|
|
target_nt = prng_successor(nt1, 160);
|
|
target_ks = nt2 ^ target_nt;
|
|
isOK = 1;
|
|
|
|
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Testing nt1=%08x nt2enc=%08x nt2par=%02x ks=%08x", nt1, nt2, par[0], target_ks);
|
|
}
|
|
|
|
LED_C_OFF();
|
|
|
|
crypto1_deinit(pcs);
|
|
|
|
struct p {
|
|
int16_t isOK;
|
|
uint8_t block;
|
|
uint8_t keytype;
|
|
uint8_t cuid[4];
|
|
uint8_t nt[4];
|
|
uint8_t ks[4];
|
|
} PACKED payload;
|
|
payload.isOK = isOK;
|
|
payload.block = targetBlockNo;
|
|
payload.keytype = targetKeyType;
|
|
|
|
memcpy(payload.cuid, &cuid, 4);
|
|
memcpy(payload.nt, &target_nt, 4);
|
|
memcpy(payload.ks, &target_ks, 4);
|
|
|
|
LED_B_ON();
|
|
reply_ng(CMD_HF_MIFARE_STATIC_NESTED, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload));
|
|
LED_B_OFF();
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// MIFARE check keys. key count up to 85.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
typedef struct sector_t {
|
|
uint8_t keyA[6];
|
|
uint8_t keyB[6];
|
|
} sector_t;
|
|
|
|
typedef struct chk_t {
|
|
uint64_t key;
|
|
uint32_t cuid;
|
|
uint8_t cl;
|
|
uint8_t block;
|
|
uint8_t keyType;
|
|
uint8_t *uid;
|
|
struct Crypto1State *pcs;
|
|
} chk_t;
|
|
|
|
// checks one key.
|
|
// fast select, tries 5 times to select
|
|
//
|
|
// return:
|
|
// 2 = failed to select.
|
|
// 1 = wrong key
|
|
// 0 = correct key
|
|
static uint8_t chkKey(struct chk_t *c) {
|
|
uint8_t i = 0, res = 2;
|
|
while (i < 5) {
|
|
// this part is from Piwi's faster nonce collecting part in Hardnested.
|
|
// assume: fast select
|
|
if (!iso14443a_fast_select_card(c->uid, c->cl)) {
|
|
++i;
|
|
continue;
|
|
}
|
|
res = mifare_classic_authex(c->pcs, c->cuid, c->block, c->keyType, c->key, AUTH_FIRST, NULL, NULL);
|
|
|
|
// CHK_TIMEOUT();
|
|
|
|
// if successful auth, send HALT
|
|
// if ( !res )
|
|
// mifare_classic_halt_ex(c->pcs);
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) {
|
|
|
|
if (!iso14443a_fast_select_card(c->uid, c->cl))
|
|
return 2;
|
|
|
|
if (mifare_classic_authex(c->pcs, c->cuid, c->block, 0, c->key, AUTH_FIRST, NULL, NULL))
|
|
return 1;
|
|
|
|
uint8_t data[16] = {0x00};
|
|
uint8_t res = mifare_classic_readblock(c->pcs, c->cuid, c->block, data);
|
|
|
|
// successful read
|
|
if (!res) {
|
|
// data was something else than zeros.
|
|
if (memcmp(data + 10, "\x00\x00\x00\x00\x00\x00", 6) != 0) {
|
|
memcpy(keyb, data + 10, 6);
|
|
res = 0;
|
|
} else {
|
|
res = 3;
|
|
}
|
|
mifare_classic_halt_ex(c->pcs);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static void chkKey_scanA(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) {
|
|
for (uint8_t s = 0; s < *sectorcnt; s++) {
|
|
|
|
// skip already found A keys
|
|
if (found[(s * 2)])
|
|
continue;
|
|
|
|
c->block = FirstBlockOfSector(s);
|
|
if (chkKey(c) == 0) {
|
|
num_to_bytes(c->key, 6, k_sector[s].keyA);
|
|
found[(s * 2)] = 1;
|
|
++*foundkeys;
|
|
|
|
if (DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Scan A found (%d)", c->block);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void chkKey_scanB(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) {
|
|
for (uint8_t s = 0; s < *sectorcnt; s++) {
|
|
|
|
// skip already found B keys
|
|
if (found[(s * 2) + 1])
|
|
continue;
|
|
|
|
c->block = FirstBlockOfSector(s);
|
|
if (chkKey(c) == 0) {
|
|
num_to_bytes(c->key, 6, k_sector[s].keyB);
|
|
found[(s * 2) + 1] = 1;
|
|
++*foundkeys;
|
|
|
|
if (DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Scan B found (%d)", c->block);
|
|
}
|
|
}
|
|
}
|
|
|
|
// loop all A keys,
|
|
// when A is found but not B, try to read B.
|
|
static void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) {
|
|
|
|
// read Block B, if A is found.
|
|
for (uint8_t s = 0; s < *sectorcnt; ++s) {
|
|
|
|
if (found[(s * 2)] && found[(s * 2) + 1])
|
|
continue;
|
|
|
|
c->block = (FirstBlockOfSector(s) + NumBlocksPerSector(s) - 1);
|
|
|
|
// A but not B
|
|
if (found[(s * 2)] && !found[(s * 2) + 1]) {
|
|
c->key = bytes_to_num(k_sector[s].keyA, 6);
|
|
uint8_t status = chkKey_readb(c, k_sector[s].keyB);
|
|
if (status == 0) {
|
|
found[(s * 2) + 1] = 1;
|
|
++*foundkeys;
|
|
|
|
if (DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Reading B found (%d)", c->block);
|
|
|
|
// try quick find all B?
|
|
// assume: keys comes in groups. Find one B, test against all B.
|
|
c->key = bytes_to_num(k_sector[s].keyB, 6);
|
|
c->keyType = 1;
|
|
chkKey_scanB(c, k_sector, found, sectorcnt, foundkeys);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// get Chunks of keys, to test authentication against card.
|
|
// arg0 = antal sectorer
|
|
// arg0 = first time
|
|
// arg1 = clear trace
|
|
// arg2 = antal nycklar i keychunk
|
|
// datain = keys as array
|
|
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) {
|
|
|
|
// first call or
|
|
uint8_t sectorcnt = arg0 & 0xFF; // 16;
|
|
uint8_t firstchunk = (arg0 >> 8) & 0xF;
|
|
uint8_t lastchunk = (arg0 >> 12) & 0xF;
|
|
uint8_t strategy = arg1 & 0xFF;
|
|
uint8_t use_flashmem = (arg1 >> 8) & 0xFF;
|
|
uint16_t keyCount = arg2 & 0xFF;
|
|
uint8_t status = 0;
|
|
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
struct chk_t chk_data;
|
|
|
|
uint8_t allkeys = sectorcnt << 1;
|
|
|
|
static uint32_t cuid = 0;
|
|
static uint8_t cascade_levels = 0;
|
|
static uint8_t foundkeys = 0;
|
|
static sector_t k_sector[80];
|
|
static uint8_t found[80];
|
|
static uint8_t *uid;
|
|
|
|
int oldbg = DBGLEVEL;
|
|
|
|
#ifdef WITH_FLASH
|
|
if (use_flashmem) {
|
|
BigBuf_free();
|
|
uint16_t isok = 0;
|
|
uint8_t size[2] = {0x00, 0x00};
|
|
isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET, size, 2);
|
|
if (isok != 2)
|
|
goto OUT;
|
|
|
|
keyCount = size[1] << 8 | size[0];
|
|
|
|
if (keyCount == 0)
|
|
goto OUT;
|
|
|
|
// limit size of availlable for keys in bigbuff
|
|
// a key is 6bytes
|
|
uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * 6);
|
|
|
|
keyCount = key_mem_available / 6;
|
|
|
|
datain = BigBuf_malloc(key_mem_available);
|
|
if (datain == NULL)
|
|
goto OUT;
|
|
|
|
isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET + 2, datain, key_mem_available);
|
|
if (isok != key_mem_available)
|
|
goto OUT;
|
|
|
|
}
|
|
#endif
|
|
|
|
if (uid == NULL || firstchunk) {
|
|
uid = BigBuf_malloc(10);
|
|
if (uid == NULL)
|
|
goto OUT;
|
|
}
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
LEDsoff();
|
|
LED_A_ON();
|
|
|
|
if (firstchunk) {
|
|
clear_trace();
|
|
set_tracing(false);
|
|
|
|
memset(k_sector, 0x00, 480 + 10);
|
|
memset(found, 0x00, sizeof(found));
|
|
foundkeys = 0;
|
|
|
|
iso14a_card_select_t card_info;
|
|
if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("ChkKeys_fast: Can't select card (ALL)");
|
|
goto OUT;
|
|
}
|
|
|
|
switch (card_info.uidlen) {
|
|
case 4 :
|
|
cascade_levels = 1;
|
|
break;
|
|
case 7 :
|
|
cascade_levels = 2;
|
|
break;
|
|
case 10:
|
|
cascade_levels = 3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
CHK_TIMEOUT();
|
|
}
|
|
|
|
// clear debug level. We are expecting lots of authentication failures...
|
|
DBGLEVEL = DBG_NONE;
|
|
|
|
// set check struct.
|
|
chk_data.uid = uid;
|
|
chk_data.cuid = cuid;
|
|
chk_data.cl = cascade_levels;
|
|
chk_data.pcs = pcs;
|
|
chk_data.block = 0;
|
|
|
|
// keychunk loop - depth first one sector.
|
|
if (strategy == 1 || use_flashmem) {
|
|
|
|
uint8_t newfound = foundkeys;
|
|
|
|
uint16_t lastpos = 0;
|
|
uint16_t s_point = 0;
|
|
// Sector main loop
|
|
// keep track of how many sectors on card.
|
|
for (uint8_t s = 0; s < sectorcnt; ++s) {
|
|
|
|
if (found[(s * 2)] && found[(s * 2) + 1])
|
|
continue;
|
|
|
|
for (uint16_t i = s_point; i < keyCount; ++i) {
|
|
|
|
// Allow button press / usb cmd to interrupt device
|
|
if (BUTTON_PRESS() || data_available()) {
|
|
goto OUT;
|
|
}
|
|
|
|
// found all keys?
|
|
if (foundkeys == allkeys)
|
|
goto OUT;
|
|
|
|
WDT_HIT();
|
|
|
|
// assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector
|
|
chk_data.block = FirstBlockOfSector(s);
|
|
|
|
// new key
|
|
chk_data.key = bytes_to_num(datain + i * 6, 6);
|
|
|
|
// skip already found A keys
|
|
if (!found[(s * 2)]) {
|
|
chk_data.keyType = 0;
|
|
status = chkKey(&chk_data);
|
|
if (status == 0) {
|
|
memcpy(k_sector[s].keyA, datain + i * 6, 6);
|
|
found[(s * 2)] = 1;
|
|
++foundkeys;
|
|
|
|
chkKey_scanA(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
|
|
|
// read Block B, if A is found.
|
|
chkKey_loopBonly(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
|
|
|
chk_data.keyType = 1;
|
|
chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
|
|
|
chk_data.keyType = 0;
|
|
chk_data.block = FirstBlockOfSector(s);
|
|
|
|
if (use_flashmem) {
|
|
if (lastpos != i && lastpos != 0) {
|
|
if (i - lastpos < 0xF) {
|
|
s_point = i & 0xFFF0;
|
|
}
|
|
} else {
|
|
lastpos = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// skip already found B keys
|
|
if (!found[(s * 2) + 1]) {
|
|
chk_data.keyType = 1;
|
|
status = chkKey(&chk_data);
|
|
if (status == 0) {
|
|
memcpy(k_sector[s].keyB, datain + i * 6, 6);
|
|
found[(s * 2) + 1] = 1;
|
|
++foundkeys;
|
|
|
|
chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
|
|
|
if (use_flashmem) {
|
|
if (lastpos != i && lastpos != 0) {
|
|
|
|
if (i - lastpos < 0xF)
|
|
s_point = i & 0xFFF0;
|
|
} else {
|
|
lastpos = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found[(s * 2)] && found[(s * 2) + 1])
|
|
break;
|
|
|
|
} // end keys test loop - depth first
|
|
|
|
// assume1. if no keys found in first sector, get next keychunk from client
|
|
if (!use_flashmem && (newfound - foundkeys == 0))
|
|
goto OUT;
|
|
|
|
} // end loop - sector
|
|
} // end strategy 1
|
|
|
|
if (foundkeys == allkeys)
|
|
goto OUT;
|
|
|
|
if (strategy == 2 || use_flashmem) {
|
|
|
|
// Keychunk loop
|
|
for (uint16_t i = 0; i < keyCount; i++) {
|
|
|
|
// Allow button press / usb cmd to interrupt device
|
|
if (BUTTON_PRESS() || data_available()) break;
|
|
|
|
// found all keys?
|
|
if (foundkeys == allkeys)
|
|
goto OUT;
|
|
|
|
WDT_HIT();
|
|
|
|
// new key
|
|
chk_data.key = bytes_to_num(datain + i * 6, 6);
|
|
|
|
// Sector main loop
|
|
// keep track of how many sectors on card.
|
|
for (uint8_t s = 0; s < sectorcnt; ++s) {
|
|
|
|
if (found[(s * 2)] && found[(s * 2) + 1]) continue;
|
|
|
|
// found all keys?
|
|
if (foundkeys == allkeys)
|
|
goto OUT;
|
|
|
|
// assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector
|
|
chk_data.block = FirstBlockOfSector(s);
|
|
|
|
// skip already found A keys
|
|
if (!found[(s * 2)]) {
|
|
chk_data.keyType = 0;
|
|
status = chkKey(&chk_data);
|
|
if (status == 0) {
|
|
memcpy(k_sector[s].keyA, datain + i * 6, 6);
|
|
found[(s * 2)] = 1;
|
|
++foundkeys;
|
|
|
|
chkKey_scanA(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
|
|
|
// read Block B, if A is found.
|
|
chkKey_loopBonly(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
|
|
|
chk_data.block = FirstBlockOfSector(s);
|
|
}
|
|
}
|
|
|
|
// skip already found B keys
|
|
if (!found[(s * 2) + 1]) {
|
|
chk_data.keyType = 1;
|
|
status = chkKey(&chk_data);
|
|
if (status == 0) {
|
|
memcpy(k_sector[s].keyB, datain + i * 6, 6);
|
|
found[(s * 2) + 1] = 1;
|
|
++foundkeys;
|
|
|
|
chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
|
}
|
|
}
|
|
} // end loop sectors
|
|
} // end loop keys
|
|
} // end loop strategy 2
|
|
OUT:
|
|
LEDsoff();
|
|
|
|
crypto1_deinit(pcs);
|
|
|
|
// All keys found, send to client, or last keychunk from client
|
|
if (foundkeys == allkeys || lastchunk) {
|
|
|
|
uint64_t foo = 0;
|
|
for (uint8_t m = 0; m < 64; m++) {
|
|
foo |= ((uint64_t)(found[m] & 1) << m);
|
|
}
|
|
|
|
uint16_t bar = 0;
|
|
uint8_t j = 0;
|
|
for (uint8_t m = 64; m < ARRAYLEN(found); m++) {
|
|
bar |= ((uint16_t)(found[m] & 1) << j++);
|
|
}
|
|
|
|
uint8_t *tmp = BigBuf_malloc(480 + 10);
|
|
memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t));
|
|
num_to_bytes(foo, 8, tmp + 480);
|
|
tmp[488] = bar & 0xFF;
|
|
tmp[489] = bar >> 8 & 0xFF;
|
|
|
|
reply_old(CMD_ACK, foundkeys, 0, 0, tmp, 480 + 10);
|
|
|
|
set_tracing(false);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
BigBuf_free();
|
|
BigBuf_Clear_ext(false);
|
|
|
|
// special trick ecfill
|
|
if (use_flashmem && foundkeys == allkeys) {
|
|
|
|
uint8_t block[16] = {0};
|
|
for (int i = 0; i < sectorcnt; i++) {
|
|
|
|
uint8_t blockno;
|
|
if (i < 32) {
|
|
blockno = (i * 4) ^ 0x3;
|
|
} else {
|
|
blockno = (32 * 4 + (i - 32) * 16) ^ 0xF;
|
|
}
|
|
// get ST
|
|
emlGetMem(block, blockno, 1);
|
|
|
|
memcpy(block, k_sector[i].keyA, 6);
|
|
memcpy(block + 10, k_sector[i].keyB, 6);
|
|
|
|
emlSetMem_xt(block, blockno, 1, sizeof(block));
|
|
}
|
|
|
|
MifareECardLoad(sectorcnt, 0);
|
|
MifareECardLoad(sectorcnt, 1);
|
|
}
|
|
} else {
|
|
// partial/none keys found
|
|
reply_mix(CMD_ACK, foundkeys, 0, 0, 0, 0);
|
|
}
|
|
|
|
DBGLEVEL = oldbg;
|
|
}
|
|
|
|
void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) {
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
uint8_t uid[10] = {0x00};
|
|
|
|
uint64_t key = 0;
|
|
uint32_t cuid = 0;
|
|
int i, res;
|
|
uint8_t cascade_levels = 0;
|
|
struct {
|
|
uint8_t key[6];
|
|
bool found;
|
|
} PACKED keyresult;
|
|
keyresult.found = false;
|
|
bool have_uid = false;
|
|
|
|
uint8_t keyType = datain[0];
|
|
uint8_t blockNo = datain[1];
|
|
bool clearTrace = datain[2];
|
|
uint16_t key_count = (datain[3] << 8) | datain[4];
|
|
|
|
uint16_t key_mem_available;
|
|
if (reserved_mem)
|
|
key_mem_available = key_count * 6;
|
|
else
|
|
key_mem_available = MIN((PM3_CMD_DATA_SIZE - 5), key_count * 6);
|
|
|
|
key_count = key_mem_available / 6;
|
|
|
|
datain += 5;
|
|
|
|
LEDsoff();
|
|
LED_A_ON();
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
if (clearTrace)
|
|
clear_trace();
|
|
|
|
int oldbg = DBGLEVEL;
|
|
DBGLEVEL = DBG_NONE;
|
|
|
|
set_tracing(false);
|
|
|
|
for (i = 0; i < key_count; i++) {
|
|
|
|
// Iceman: use piwi's faster nonce collecting part in hardnested.
|
|
if (!have_uid) { // need a full select cycle to get the uid first
|
|
iso14a_card_select_t card_info;
|
|
if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("ChkKeys: Can't select card (ALL)");
|
|
--i; // try same key once again
|
|
continue;
|
|
}
|
|
switch (card_info.uidlen) {
|
|
case 4 :
|
|
cascade_levels = 1;
|
|
break;
|
|
case 7 :
|
|
cascade_levels = 2;
|
|
break;
|
|
case 10:
|
|
cascade_levels = 3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
have_uid = true;
|
|
} else { // no need for anticollision. We can directly select the card
|
|
if (!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("ChkKeys: Can't select card (UID)");
|
|
--i; // try same key once again
|
|
continue;
|
|
}
|
|
}
|
|
|
|
key = bytes_to_num(datain + i * 6, 6);
|
|
res = mifare_classic_auth(pcs, cuid, blockNo, keyType, key, AUTH_FIRST);
|
|
|
|
// CHK_TIMEOUT();
|
|
|
|
if (res)
|
|
continue;
|
|
|
|
memcpy(keyresult.key, datain + i * 6, 6);
|
|
keyresult.found = true;
|
|
break;
|
|
}
|
|
|
|
LED_B_ON();
|
|
|
|
reply_ng(CMD_HF_MIFARE_CHKKEYS, PM3_SUCCESS, (uint8_t *)&keyresult, sizeof(keyresult));
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
|
|
set_tracing(false);
|
|
crypto1_deinit(pcs);
|
|
|
|
DBGLEVEL = oldbg;
|
|
}
|
|
|
|
void MifareChkKeys_file(uint8_t *fn) {
|
|
|
|
#ifdef WITH_FLASH
|
|
BigBuf_free();
|
|
|
|
SpinOff(0);
|
|
|
|
int changed = rdv40_spiffs_lazy_mount();
|
|
uint32_t size = size_in_spiffs((char *)fn);
|
|
uint8_t *mem = BigBuf_malloc(size);
|
|
|
|
rdv40_spiffs_read_as_filetype((char *)fn, mem, size, RDV40_SPIFFS_SAFETY_SAFE);
|
|
|
|
if (changed) {
|
|
rdv40_spiffs_lazy_unmount();
|
|
}
|
|
|
|
SpinOff(0);
|
|
|
|
MifareChkKeys(mem, true);
|
|
|
|
BigBuf_free();
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// MIFARE Personalize UID. Only for Mifare Classic EV1 7Byte UID
|
|
//-----------------------------------------------------------------------------
|
|
void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) {
|
|
|
|
uint16_t isOK = PM3_EUNDEF;
|
|
uint8_t uid[10];
|
|
uint32_t cuid;
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
LED_A_ON();
|
|
|
|
while (true) {
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
break;
|
|
}
|
|
|
|
uint8_t block_number = 0;
|
|
if (mifare_classic_auth(pcs, cuid, block_number, keyType, key, AUTH_FIRST)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error");
|
|
break;
|
|
}
|
|
|
|
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE];
|
|
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE];
|
|
int len = mifare_sendcmd_short(pcs, true, MIFARE_EV1_PERSONAL_UID, perso_option, receivedAnswer, receivedAnswerPar, NULL);
|
|
if (len != 1 || receivedAnswer[0] != CARD_ACK) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
|
|
break;
|
|
}
|
|
|
|
if (mifare_classic_halt(pcs, cuid)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
break;
|
|
}
|
|
isOK = PM3_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
crypto1_deinit(pcs);
|
|
|
|
LED_B_ON();
|
|
reply_ng(CMD_HF_MIFARE_PERSONALIZE_UID, isOK, NULL, 0);
|
|
LED_B_OFF();
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Work with emulator memory
|
|
//
|
|
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not
|
|
// involved in dealing with emulator memory. But if it is called later, it might
|
|
// destroy the Emulator Memory.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MifareEMemClr(void) {
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
emlClearMem();
|
|
}
|
|
|
|
void MifareEMemSet(uint8_t blockno, uint8_t blockcnt, uint8_t blockwidth, uint8_t *datain) {
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
|
|
if (blockwidth == 0)
|
|
blockwidth = 16; // backwards compat... default bytewidth
|
|
|
|
emlSetMem_xt(datain, blockno, blockcnt, blockwidth); // data, block num, blocks count, block byte width
|
|
}
|
|
|
|
void MifareEMemGet(uint8_t blockno, uint8_t blockcnt) {
|
|
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
|
|
|
//
|
|
size_t size = blockcnt * 16;
|
|
if (size > PM3_CMD_DATA_SIZE) {
|
|
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
uint8_t *buf = BigBuf_malloc(size);
|
|
|
|
emlGetMem(buf, blockno, blockcnt); // data, block num, blocks count (max 4)
|
|
|
|
LED_B_ON();
|
|
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size);
|
|
LED_B_OFF();
|
|
BigBuf_free_keep_EM();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Load a card into the emulator memory
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype) {
|
|
int retval = MifareECardLoad(sectorcnt, keytype);
|
|
reply_ng(CMD_HF_MIFARE_EML_LOAD, retval, NULL, 0);
|
|
return retval;
|
|
}
|
|
|
|
int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) {
|
|
|
|
uint32_t cuid = 0;
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
// variables
|
|
uint8_t dataoutbuf[16] = {0x00};
|
|
uint8_t dataoutbuf2[16] = {0x00};
|
|
uint8_t uid[10] = {0x00};
|
|
|
|
LED_A_ON();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
int retval = PM3_SUCCESS;
|
|
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
retval = PM3_ESOFT;
|
|
if (DBGLEVEL > DBG_ERROR) Dbprintf("Can't select card");
|
|
goto out;
|
|
}
|
|
|
|
for (uint8_t sectorNo = 0; sectorNo < sectorcnt; sectorNo++) {
|
|
uint64_t ui64Key = emlGetKey(sectorNo, keytype);
|
|
if (sectorNo == 0) {
|
|
if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keytype, ui64Key, AUTH_FIRST)) {
|
|
retval = PM3_EPARTIAL;
|
|
if (DBGLEVEL > DBG_ERROR) Dbprintf("Sector[%2d]. Auth error", sectorNo);
|
|
continue;
|
|
}
|
|
} else {
|
|
if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keytype, ui64Key, AUTH_NESTED)) {
|
|
retval = PM3_EPARTIAL;
|
|
if (DBGLEVEL > DBG_ERROR) Dbprintf("Sector[%2d]. Auth nested error", sectorNo);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
for (uint8_t blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) {
|
|
if (mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) {
|
|
retval = PM3_EPARTIAL;
|
|
|
|
if (DBGLEVEL > DBG_ERROR) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo);
|
|
continue;
|
|
}
|
|
|
|
if (memcmp(dataoutbuf, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (blockNo < NumBlocksPerSector(sectorNo) - 1) {
|
|
emlSetMem(dataoutbuf, FirstBlockOfSector(sectorNo) + blockNo, 1);
|
|
} else { // sector trailer, keep the keys, set only the AC
|
|
emlGetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1);
|
|
memcpy(dataoutbuf2 + 6, dataoutbuf + 6, 4);
|
|
emlSetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
int res = mifare_classic_halt(pcs, cuid);
|
|
(void)res;
|
|
|
|
if (DBGLEVEL >= DBG_INFO) DbpString("Emulator fill sectors finished");
|
|
|
|
out:
|
|
crypto1_deinit(pcs);
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
return retval;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn)
|
|
//
|
|
// PARAMS - workFlags
|
|
// bit 0 - need get UID
|
|
// bit 1 - need wupC
|
|
// bit 2 - need HALT after sequence
|
|
// bit 3 - need turn on FPGA before sequence
|
|
// bit 4 - need turn off FPGA
|
|
// bit 5 - need to set datain instead of issuing USB reply (called via ARM for StandAloneMode14a)
|
|
// bit 6 - wipe tag.
|
|
//-----------------------------------------------------------------------------
|
|
// magic uid card generation 1 commands
|
|
static uint8_t wupC1[] = { MIFARE_MAGICWUPC1 };
|
|
static uint8_t wupC2[] = { MIFARE_MAGICWUPC2 };
|
|
static uint8_t wipeC[] = { MIFARE_MAGICWIPEC };
|
|
|
|
void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) {
|
|
|
|
// params
|
|
uint8_t workFlags = arg0;
|
|
uint8_t blockNo = arg1;
|
|
|
|
// detect 1a/1b
|
|
bool is1b = false;
|
|
|
|
// variables
|
|
bool isOK = false; //assume we will get an error
|
|
uint8_t errormsg = 0x00;
|
|
uint8_t uid[10] = {0x00};
|
|
uint8_t data[18] = {0x00};
|
|
uint32_t cuid = 0;
|
|
|
|
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
|
|
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00};
|
|
|
|
if (workFlags & MAGIC_INIT) {
|
|
LED_A_ON();
|
|
LED_B_OFF();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
}
|
|
|
|
//loop doesn't loop just breaks out if error
|
|
while (true) {
|
|
// read UID and return to client with write
|
|
if (workFlags & MAGIC_UID) {
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
errormsg = MAGIC_UID;
|
|
mifare_classic_halt_ex(NULL);
|
|
break;
|
|
}
|
|
mifare_classic_halt_ex(NULL);
|
|
}
|
|
|
|
// wipe tag, fill it with zeros
|
|
if (workFlags & MAGIC_WIPE) {
|
|
ReaderTransmitBitsPar(wupC1, 7, NULL, NULL);
|
|
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("wupC1 error");
|
|
errormsg = MAGIC_WIPE;
|
|
break;
|
|
}
|
|
|
|
uint32_t old_timeout = iso14a_get_timeout();
|
|
|
|
// 2000 ms timeout
|
|
// 13560000 / 1000 / (8 * 16) * timeout
|
|
iso14a_set_timeout(21190);
|
|
|
|
ReaderTransmit(wipeC, sizeof(wipeC), NULL);
|
|
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("wipeC error");
|
|
errormsg = MAGIC_WIPE;
|
|
break;
|
|
}
|
|
iso14a_set_timeout(old_timeout);
|
|
|
|
mifare_classic_halt_ex(NULL);
|
|
}
|
|
|
|
// write block
|
|
if (workFlags & MAGIC_WUPC) {
|
|
ReaderTransmitBitsPar(wupC1, 7, NULL, NULL);
|
|
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("wupC1 error");
|
|
errormsg = MAGIC_WUPC;
|
|
break;
|
|
}
|
|
|
|
if (!is1b) {
|
|
ReaderTransmit(wupC2, sizeof(wupC2), NULL);
|
|
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]");
|
|
is1b = true;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("write block send command error");
|
|
errormsg = 4;
|
|
break;
|
|
}
|
|
|
|
memcpy(data, datain, 16);
|
|
AddCrc14A(data, 16);
|
|
|
|
ReaderTransmit(data, sizeof(data), NULL);
|
|
if ((ReaderReceive(receivedAnswer, receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("write block send data error");
|
|
errormsg = 0;
|
|
break;
|
|
}
|
|
|
|
if (workFlags & MAGIC_HALT)
|
|
mifare_classic_halt_ex(NULL);
|
|
|
|
isOK = true;
|
|
break;
|
|
|
|
} // end while
|
|
|
|
if (isOK)
|
|
reply_mix(CMD_ACK, 1, 0, 0, uid, sizeof(uid));
|
|
else
|
|
OnErrorMagic(errormsg);
|
|
|
|
if (workFlags & MAGIC_OFF)
|
|
OnSuccessMagic();
|
|
}
|
|
|
|
void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) {
|
|
|
|
uint8_t workFlags = arg0;
|
|
uint8_t blockNo = arg1;
|
|
uint8_t errormsg = 0x00;
|
|
bool isOK = false; //assume we will get an error
|
|
|
|
// detect 1a/1b
|
|
bool is1b = false;
|
|
|
|
// variables
|
|
uint8_t data[MAX_MIFARE_FRAME_SIZE];
|
|
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
|
|
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00};
|
|
|
|
memset(data, 0x00, sizeof(data));
|
|
|
|
if (workFlags & MAGIC_INIT) {
|
|
LED_A_ON();
|
|
LED_B_OFF();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
}
|
|
|
|
//loop doesn't loop just breaks out if error or done
|
|
while (true) {
|
|
if (workFlags & MAGIC_WUPC) {
|
|
ReaderTransmitBitsPar(wupC1, 7, NULL, NULL);
|
|
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("wupC1 error");
|
|
errormsg = MAGIC_WUPC;
|
|
break;
|
|
}
|
|
|
|
if (!is1b) {
|
|
ReaderTransmit(wupC2, sizeof(wupC2), NULL);
|
|
if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_INFO) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]");
|
|
is1b = true;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// read block
|
|
if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 18)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("read block send command error");
|
|
errormsg = 0;
|
|
break;
|
|
}
|
|
|
|
memcpy(data, receivedAnswer, sizeof(data));
|
|
|
|
// send HALT
|
|
if (workFlags & MAGIC_HALT)
|
|
mifare_classic_halt_ex(NULL);
|
|
|
|
isOK = true;
|
|
break;
|
|
}
|
|
// if MAGIC_DATAIN, the data stays on device side.
|
|
if (workFlags & MAGIC_DATAIN) {
|
|
if (isOK)
|
|
memcpy(datain, data, sizeof(data));
|
|
} else {
|
|
if (isOK)
|
|
reply_old(CMD_ACK, 1, 0, 0, data, sizeof(data));
|
|
else
|
|
OnErrorMagic(errormsg);
|
|
}
|
|
|
|
if (workFlags & MAGIC_OFF)
|
|
OnSuccessMagic();
|
|
}
|
|
|
|
void MifareCIdent(bool is_mfc) {
|
|
// variables
|
|
uint8_t isGen = 0;
|
|
uint8_t rec[1] = {0x00};
|
|
uint8_t recpar[1] = {0x00};
|
|
uint8_t rats[4] = { ISO14443A_CMD_RATS, 0x80, 0x31, 0x73 };
|
|
uint8_t rdblf0[4] = { ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f};
|
|
uint8_t rdbl00[4] = { ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8};
|
|
uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE);
|
|
uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE);
|
|
uint8_t *uid = BigBuf_malloc(10);
|
|
|
|
memset(par, 0x00, MAX_PARITY_SIZE);
|
|
memset(buf, 0x00, PM3_CMD_DATA_SIZE);
|
|
memset(uid, 0x00, 10);
|
|
|
|
uint32_t cuid = 0;
|
|
uint8_t data[1] = {0x00};
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
// Generation 1 test
|
|
ReaderTransmitBitsPar(wupC1, 7, NULL, NULL);
|
|
if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) {
|
|
ReaderTransmit(wupC2, sizeof(wupC2), NULL);
|
|
if (!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) {
|
|
isGen = MAGIC_GEN_1B;
|
|
goto OUT;
|
|
};
|
|
isGen = MAGIC_GEN_1A;
|
|
goto OUT;
|
|
}
|
|
|
|
// reset card
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
SpinDelay(40);
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
int res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
|
if (res == 2) {
|
|
if (cuid == 0xAA55C396) {
|
|
isGen = MAGIC_GEN_UNFUSED;
|
|
goto OUT;
|
|
}
|
|
|
|
ReaderTransmit(rats, sizeof(rats), NULL);
|
|
res = ReaderReceive(buf, par);
|
|
if (res) {
|
|
|
|
// test for some MFC gen2
|
|
if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) {
|
|
|
|
// super card ident
|
|
uint8_t super[] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D};
|
|
ReaderTransmit(super, sizeof(super), NULL);
|
|
res = ReaderReceive(buf, par);
|
|
if (res == 22) {
|
|
isGen = MAGIC_SUPER;
|
|
goto OUT;
|
|
}
|
|
|
|
isGen = MAGIC_GEN_2;
|
|
goto OUT;
|
|
}
|
|
// test for some MFC 7b gen2
|
|
if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) {
|
|
isGen = MAGIC_GEN_2;
|
|
goto OUT;
|
|
}
|
|
// test for Ultralight magic gen2
|
|
if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) {
|
|
isGen = MAGIC_GEN_2;
|
|
goto OUT;
|
|
}
|
|
// test for Ultralight EV1 magic gen2
|
|
if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) {
|
|
isGen = MAGIC_GEN_2;
|
|
goto OUT;
|
|
}
|
|
// test for some other Ultralight EV1 magic gen2
|
|
if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) {
|
|
isGen = MAGIC_GEN_2;
|
|
goto OUT;
|
|
}
|
|
// test for some other Ultralight magic gen2
|
|
if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) {
|
|
isGen = MAGIC_GEN_2;
|
|
goto OUT;
|
|
}
|
|
// test for NTAG213 magic gen2
|
|
if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) {
|
|
isGen = MAGIC_GEN_2;
|
|
goto OUT;
|
|
}
|
|
}
|
|
|
|
if (is_mfc == false) {
|
|
// magic ntag test
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
SpinDelay(40);
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
|
if (res == 2) {
|
|
ReaderTransmit(rdblf0, sizeof(rdblf0), NULL);
|
|
res = ReaderReceive(buf, par);
|
|
if (res == 18) {
|
|
isGen = MAGIC_NTAG21X;
|
|
}
|
|
}
|
|
} else {
|
|
// magic MFC Gen3 test
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
SpinDelay(40);
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
|
|
if (res == 2) {
|
|
ReaderTransmit(rdbl00, sizeof(rdbl00), NULL);
|
|
res = ReaderReceive(buf, par);
|
|
if (res == 18) {
|
|
isGen = MAGIC_GEN_3;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
OUT:
|
|
|
|
data[0] = isGen;
|
|
reply_ng(CMD_HF_MIFARE_CIDENT, PM3_SUCCESS, data, sizeof(data));
|
|
// turns off
|
|
OnSuccessMagic();
|
|
BigBuf_free();
|
|
}
|
|
|
|
void MifareHasStaticNonce(void) {
|
|
|
|
// variables
|
|
int retval = PM3_SUCCESS;
|
|
uint32_t nt = 0;
|
|
uint8_t *uid = BigBuf_malloc(10);
|
|
|
|
memset(uid, 0x00, 10);
|
|
|
|
uint8_t data[1] = { NONCE_FAIL };
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs;
|
|
pcs = &mpcs;
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
uint8_t counter = 0;
|
|
for (uint8_t i = 0; i < 3; i++) {
|
|
|
|
iso14a_card_select_t card_info;
|
|
if (!iso14443a_select_card(uid, &card_info, NULL, true, 0, true)) {
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
|
|
uint8_t rec[4] = {0x00};
|
|
uint8_t recpar[1] = {0x00};
|
|
// Transmit MIFARE_CLASSIC_AUTH 0x60, block 0
|
|
int len = mifare_sendcmd_short(pcs, false, MIFARE_AUTH_KEYA, 0, rec, recpar, NULL);
|
|
if (len != 4) {
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
|
|
// Save the tag nonce (nt)
|
|
if (nt == bytes_to_num(rec, 4)) {
|
|
counter++;
|
|
}
|
|
|
|
nt = bytes_to_num(rec, 4);
|
|
|
|
// some cards with static nonce need to be reset before next query
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
CHK_TIMEOUT();
|
|
|
|
memset(rec, 0x00, sizeof(rec));
|
|
}
|
|
|
|
if (counter) {
|
|
Dbprintf("%u static nonce %08x", data[0], nt);
|
|
data[0] = NONCE_STATIC;
|
|
} else {
|
|
data[0] = NONCE_NORMAL;
|
|
}
|
|
|
|
OUT:
|
|
reply_ng(CMD_HF_MIFARE_STATIC_NONCE, retval, data, sizeof(data));
|
|
// turns off
|
|
OnSuccessMagic();
|
|
BigBuf_free();
|
|
crypto1_deinit(pcs);
|
|
}
|
|
|
|
void OnSuccessMagic(void) {
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
void OnErrorMagic(uint8_t reason) {
|
|
// ACK, ISOK, reason,0,0,0
|
|
reply_mix(CMD_ACK, 0, reason, 0, 0, 0);
|
|
OnSuccessMagic();
|
|
}
|
|
|
|
int DoGen3Cmd(uint8_t *cmd, uint8_t cmd_len) {
|
|
int retval = PM3_SUCCESS;
|
|
uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE);
|
|
uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE);
|
|
|
|
LED_B_ON();
|
|
uint32_t save_iso14a_timeout = iso14a_get_timeout();
|
|
iso14a_set_timeout(13560000 / 1000 / (8 * 16) * 2000); // 2 seconds timeout
|
|
|
|
ReaderTransmit(cmd, cmd_len, NULL);
|
|
int res = ReaderReceive(buf, par);
|
|
if (res == 4 && memcmp(buf, "\x90\x00\xfd\x07", 4) == 0) {
|
|
// timeout for card memory reset
|
|
SpinDelay(1000);
|
|
} else {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card operation not completed");
|
|
retval = PM3_ESOFT;
|
|
}
|
|
iso14a_set_timeout(save_iso14a_timeout);
|
|
LED_B_OFF();
|
|
|
|
return retval;
|
|
}
|
|
|
|
void MifareGen3UID(uint8_t uidlen, uint8_t *uid) {
|
|
int retval = PM3_SUCCESS;
|
|
uint8_t uid_cmd[5] = { 0x90, 0xfb, 0xcc, 0xcc, 0x07 };
|
|
uint8_t *old_uid = BigBuf_malloc(10);
|
|
uint8_t *cmd = BigBuf_malloc(sizeof(uid_cmd) + uidlen + 2);
|
|
iso14a_card_select_t *card_info = (iso14a_card_select_t *) BigBuf_malloc(sizeof(iso14a_card_select_t));
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
if (!iso14443a_select_card(old_uid, card_info, NULL, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected");
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
if (card_info->uidlen != uidlen) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Wrong UID length");
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
|
|
memcpy(cmd, uid_cmd, sizeof(uid_cmd));
|
|
memcpy(&cmd[sizeof(uid_cmd)], uid, uidlen);
|
|
AddCrc14A(cmd, sizeof(uid_cmd) + uidlen);
|
|
|
|
retval = DoGen3Cmd(cmd, sizeof(uid_cmd) + uidlen + 2);
|
|
|
|
OUT:
|
|
reply_ng(CMD_HF_MIFARE_GEN3UID, retval, old_uid, uidlen);
|
|
// turns off
|
|
OnSuccessMagic();
|
|
BigBuf_free();
|
|
}
|
|
|
|
void MifareGen3Blk(uint8_t block_len, uint8_t *block) {
|
|
#define MIFARE_BLOCK_SIZE (MAX_MIFARE_FRAME_SIZE - 2)
|
|
int retval = PM3_SUCCESS;
|
|
uint8_t block_cmd[5] = { 0x90, 0xf0, 0xcc, 0xcc, 0x10 };
|
|
uint8_t *uid = BigBuf_malloc(10);
|
|
uint8_t *cmd = BigBuf_malloc(sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE);
|
|
iso14a_card_select_t *card_info = (iso14a_card_select_t *) BigBuf_malloc(sizeof(iso14a_card_select_t));
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
if (!iso14443a_select_card(uid, card_info, NULL, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected");
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
|
|
bool doReselect = false;
|
|
if (block_len < MIFARE_BLOCK_SIZE) {
|
|
if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, 0, &cmd[sizeof(block_cmd)], NULL, NULL) != MAX_MIFARE_FRAME_SIZE)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Read manufacturer block failed");
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
doReselect = true;
|
|
}
|
|
|
|
if (block_len > 0) {
|
|
memcpy(cmd, block_cmd, sizeof(block_cmd));
|
|
memcpy(&cmd[sizeof(block_cmd)], block, block_len);
|
|
int ofs = sizeof(block_cmd);
|
|
if (card_info->uidlen == 4) {
|
|
cmd[ofs + 4] = cmd[ofs + 0] ^ cmd[ofs + 1] ^ cmd[ofs + 2] ^ cmd[ofs + 3];
|
|
ofs += 5;
|
|
} else if (card_info->uidlen == 7) {
|
|
ofs += 7;
|
|
} else {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Wrong Card UID length");
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
cmd[ofs++] = card_info->sak;
|
|
cmd[ofs++] = card_info->atqa[0];
|
|
cmd[ofs++] = card_info->atqa[1];
|
|
AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE);
|
|
|
|
if (doReselect) {
|
|
if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected");
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
}
|
|
|
|
retval = DoGen3Cmd(cmd, sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE);
|
|
}
|
|
|
|
OUT:
|
|
reply_ng(CMD_HF_MIFARE_GEN3BLK, retval, &cmd[sizeof(block_cmd)], MIFARE_BLOCK_SIZE);
|
|
// turns off
|
|
OnSuccessMagic();
|
|
BigBuf_free();
|
|
}
|
|
|
|
void MifareGen3Freez(void) {
|
|
int retval = PM3_SUCCESS;
|
|
uint8_t freeze_cmd[7] = { 0x90, 0xfd, 0x11, 0x11, 0x00, 0xe7, 0x91 };
|
|
uint8_t *uid = BigBuf_malloc(10);
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected");
|
|
retval = PM3_ESOFT;
|
|
goto OUT;
|
|
}
|
|
|
|
retval = DoGen3Cmd(freeze_cmd, sizeof(freeze_cmd));
|
|
|
|
OUT:
|
|
reply_ng(CMD_HF_MIFARE_GEN3FREEZ, retval, NULL, 0);
|
|
// turns off
|
|
OnSuccessMagic();
|
|
BigBuf_free();
|
|
}
|
|
|
|
void MifareSetMod(uint8_t *datain) {
|
|
|
|
uint8_t mod = datain[0];
|
|
uint64_t ui64Key = bytes_to_num(datain + 1, 6);
|
|
|
|
// variables
|
|
uint16_t isOK = PM3_EUNDEF;
|
|
uint8_t uid[10] = {0};
|
|
uint32_t cuid = 0;
|
|
struct Crypto1State mpcs = {0, 0};
|
|
struct Crypto1State *pcs = &mpcs;
|
|
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0};
|
|
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0};
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
LED_A_ON();
|
|
LED_B_OFF();
|
|
LED_C_OFF();
|
|
|
|
while (true) {
|
|
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
break;
|
|
}
|
|
|
|
if (mifare_classic_auth(pcs, cuid, 0, 0, ui64Key, AUTH_FIRST)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error");
|
|
break;
|
|
}
|
|
|
|
int respLen;
|
|
if (((respLen = mifare_sendcmd_short(pcs, CRYPT_ALL, 0x43, mod, receivedAnswer, receivedAnswerPar, NULL)) != 1) || (receivedAnswer[0] != 0x0a)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("SetMod error; response[0]: %hhX, len: %d", receivedAnswer[0], respLen);
|
|
break;
|
|
}
|
|
|
|
if (mifare_classic_halt(pcs, cuid)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error");
|
|
break;
|
|
}
|
|
|
|
isOK = PM3_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
crypto1_deinit(pcs);
|
|
|
|
LED_B_ON();
|
|
reply_ng(CMD_HF_MIFARE_SETMOD, isOK, NULL, 0);
|
|
LED_B_OFF();
|
|
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
}
|
|
|
|
//
|
|
// DESFIRE
|
|
//
|
|
void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain) {
|
|
uint8_t dataout[12] = {0x00};
|
|
uint8_t uid[10] = {0x00};
|
|
uint32_t cuid = 0;
|
|
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
int len = iso14443a_select_card(uid, NULL, &cuid, true, 0, false);
|
|
if (!len) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
OnError(1);
|
|
return;
|
|
};
|
|
|
|
if (mifare_desfire_des_auth1(cuid, dataout)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Authentication part1: Fail.");
|
|
OnError(4);
|
|
return;
|
|
}
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) DbpString("AUTH 1 FINISHED");
|
|
reply_mix(CMD_ACK, 1, cuid, 0, dataout, sizeof(dataout));
|
|
}
|
|
|
|
void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) {
|
|
uint32_t cuid = arg0;
|
|
uint8_t key[16] = {0x00};
|
|
uint8_t dataout[12] = {0x00};
|
|
uint8_t isOK = 0;
|
|
|
|
memcpy(key, datain, 16);
|
|
|
|
isOK = mifare_desfire_des_auth2(cuid, key, dataout);
|
|
|
|
if (isOK) {
|
|
if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Authentication part2: Failed");
|
|
OnError(4);
|
|
return;
|
|
}
|
|
|
|
if (DBGLEVEL >= DBG_EXTENDED) DbpString("AUTH 2 FINISHED");
|
|
|
|
reply_old(CMD_ACK, isOK, 0, 0, dataout, sizeof(dataout));
|
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
|
LEDsoff();
|
|
set_tracing(false);
|
|
}
|
|
|
|
//
|
|
// Tear-off attack against MFU.
|
|
// - Moebius et al
|
|
void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *data_testwrite) {
|
|
uint8_t blockNo = blno;
|
|
|
|
if (DBGLEVEL >= DBG_DEBUG) DbpString("Preparing OTP tear-off");
|
|
|
|
if (tearoff_time > 43000)
|
|
tearoff_time = 43000;
|
|
tearoff_delay_us = tearoff_time;
|
|
tearoff_enabled = true;
|
|
|
|
LEDsoff();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
// write cmd to send, include CRC
|
|
// 1b write, 1b block, 4b data, 2 crc
|
|
uint8_t cmd[] = {
|
|
MIFARE_ULC_WRITE, blockNo,
|
|
data_testwrite[0], data_testwrite[1], data_testwrite[2], data_testwrite[3],
|
|
0, 0
|
|
};
|
|
AddCrc14A(cmd, sizeof(cmd) - 2);
|
|
|
|
// anticollision / select card
|
|
if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
OnError(1);
|
|
reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_EFAILED, NULL, 0);
|
|
return;
|
|
};
|
|
// send
|
|
LED_D_ON();
|
|
ReaderTransmit(cmd, sizeof(cmd), NULL);
|
|
tearoff_hook();
|
|
reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0);
|
|
}
|
|
|
|
//
|
|
// Tear-off attack against MFU counter
|
|
void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time, uint8_t *datain) {
|
|
|
|
if (tearoff_time > 43000)
|
|
tearoff_time = 43000;
|
|
|
|
LEDsoff();
|
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
|
clear_trace();
|
|
set_tracing(true);
|
|
|
|
// Send MFU counter increase cmd
|
|
uint8_t cmd[] = {
|
|
MIFARE_ULEV1_INCR_CNT,
|
|
counter,
|
|
datain[0], // lsb
|
|
datain[1],
|
|
datain[2], // msb
|
|
datain[3], // rfu
|
|
0,
|
|
0,
|
|
};
|
|
AddCrc14A(cmd, sizeof(cmd) - 2);
|
|
|
|
// anticollision / select card
|
|
if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) {
|
|
if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card");
|
|
OnError(1);
|
|
switch_off();
|
|
LEDsoff();
|
|
return;
|
|
};
|
|
|
|
// send
|
|
ReaderTransmit(cmd, sizeof(cmd), NULL);
|
|
LED_D_ON();
|
|
SpinDelayUsPrecision(tearoff_time);
|
|
switch_off();
|
|
LEDsoff();
|
|
reply_ng(CMD_HF_MFU_COUNTER_TEAROFF, PM3_SUCCESS, NULL, 0);
|
|
}
|