2019-03-09 15:49:41 +08:00
//-----------------------------------------------------------------------------
// 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"
2019-07-16 19:50:38 +08:00
# include "pmflash.h"
2019-08-08 22:57:33 +08:00
# 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"
2019-07-16 19:50:38 +08:00
2019-03-09 15:49:41 +08:00
# ifndef HARDNESTED_AUTHENTICATION_TIMEOUT
2019-03-10 03:34:41 +08:00
# define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation)
2019-03-09 15:49:41 +08:00
# endif
# ifndef HARDNESTED_PRE_AUTHENTICATION_LEADTIME
2019-03-10 03:34:41 +08:00
# define HARDNESTED_PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication
2019-03-09 15:59:13 +08:00
# endif
2019-03-09 15:49:41 +08:00
// send an incomplete dummy response in order to trigger the card's authentication failure timeout
# ifndef CHK_TIMEOUT
# define CHK_TIMEOUT() { \
2019-03-10 07:00:59 +08:00
ReaderTransmit ( & dummy_answer , 1 , NULL ) ; \
uint32_t timeout = GetCountSspClk ( ) + HARDNESTED_AUTHENTICATION_TIMEOUT ; \
while ( GetCountSspClk ( ) < timeout ) { } ; \
}
2019-03-09 15:49:41 +08:00
# endif
static uint8_t dummy_answer = 0 ;
//-----------------------------------------------------------------------------
2019-03-09 15:59:13 +08:00
// Select, Authenticate, Read a MIFARE tag.
2019-03-09 15:49:41 +08:00
// read block
//-----------------------------------------------------------------------------
2019-05-29 01:20:56 +08:00
void MifareReadBlock ( uint8_t blockNo , uint8_t keyType , uint8_t * datain ) {
2019-03-10 07:00:59 +08:00
// params
2019-03-10 03:34:41 +08:00
uint64_t ui64Key = 0 ;
ui64Key = bytes_to_num ( datain , 6 ) ;
// variables
2019-03-21 22:19:18 +08:00
uint8_t dataoutbuf [ 16 ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
uint8_t uid [ 10 ] = { 0x00 } ;
2019-05-29 01:20:56 +08:00
uint32_t cuid = 0 , status = PM3_EOPABORTED ;
2019-06-08 03:40:33 +08:00
2019-03-10 03:34:41 +08:00
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 ) {
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
break ;
} ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_auth ( pcs , cuid , blockNo , keyType , ui64Key , AUTH_FIRST ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Auth error " ) ;
2019-03-10 03:34:41 +08:00
break ;
} ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_readblock ( pcs , cuid , blockNo , dataoutbuf ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Read block error " ) ;
2019-03-10 03:34:41 +08:00
break ;
} ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_halt ( pcs , cuid ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
break ;
} ;
2019-05-29 01:20:56 +08:00
status = PM3_SUCCESS ;
2019-03-10 03:34:41 +08:00
break ;
}
crypto1_destroy ( pcs ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) DbpString ( " READ BLOCK FINISHED " ) ;
2019-03-10 03:34:41 +08:00
LED_B_ON ( ) ;
2019-08-04 01:17:00 +08:00
reply_ng ( CMD_HF_MIFARE_READBL , status , dataoutbuf , 16 ) ;
2019-03-10 03:34:41 +08:00
LED_B_OFF ( ) ;
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
2019-03-09 15:49:41 +08:00
}
2019-04-07 18:30:49 +08:00
void MifareUC_Auth ( uint8_t arg0 , uint8_t * keybytes ) {
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
bool turnOffField = ( arg0 = = 1 ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 07:00:59 +08:00
LED_A_ON ( ) ;
LED_B_OFF ( ) ;
LED_C_OFF ( ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
clear_trace ( ) ;
set_tracing ( true ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( NULL , NULL , NULL , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ;
} ;
2019-03-09 15:59:13 +08:00
2019-04-07 18:30:49 +08:00
if ( ! mifare_ultra_auth ( keybytes ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Authentication failed " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 1 ) ;
return ;
}
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
if ( turnOffField ) {
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
}
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , 1 , 0 , 0 , 0 , 0 ) ;
2019-03-09 15:49:41 +08:00
}
// Arg0 = BlockNo,
// Arg1 = UsePwd bool
// datain = PWD bytes,
2019-03-10 18:20:22 +08:00
void MifareUReadBlock ( uint8_t arg0 , uint8_t arg1 , uint8_t * datain ) {
2019-03-10 03:34:41 +08:00
uint8_t blockNo = arg0 ;
2019-03-21 22:19:18 +08:00
uint8_t dataout [ 16 ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
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 ) ;
2019-03-10 07:00:59 +08:00
if ( ! len ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Can't select card (RC:%02X) " , len ) ;
2019-03-10 03:34:41 +08:00
OnError ( 1 ) ;
return ;
}
// UL-C authentication
2019-03-10 07:00:59 +08:00
if ( useKey ) {
2019-03-10 03:34:41 +08:00
uint8_t key [ 16 ] = { 0x00 } ;
2019-03-10 07:00:59 +08:00
memcpy ( key , datain , sizeof ( key ) ) ;
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
if ( ! mifare_ultra_auth ( key ) ) {
2019-03-10 03:34:41 +08:00
OnError ( 1 ) ;
return ;
}
}
// UL-EV1 / NTAG authentication
2019-03-10 07:00:59 +08:00
if ( usePwd ) {
2019-03-10 03:34:41 +08:00
uint8_t pwd [ 4 ] = { 0x00 } ;
memcpy ( pwd , datain , 4 ) ;
2019-03-10 07:00:59 +08:00
uint8_t pack [ 4 ] = { 0 , 0 , 0 , 0 } ;
2019-03-10 03:34:41 +08:00
if ( ! mifare_ul_ev1_auth ( pwd , pack ) ) {
OnError ( 1 ) ;
return ;
}
}
2019-03-10 07:00:59 +08:00
if ( mifare_ultra_readblock ( blockNo , dataout ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Read block error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 2 ) ;
return ;
}
2019-03-10 07:00:59 +08:00
if ( mifare_ultra_halt ( ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 3 ) ;
return ;
}
2019-03-09 15:49:41 +08:00
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , 1 , 0 , 0 , dataout , 16 ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
2019-03-09 15:49:41 +08:00
}
//-----------------------------------------------------------------------------
2019-03-09 15:59:13 +08:00
// Select, Authenticate, Read a MIFARE tag.
2019-03-09 15:49:41 +08:00
// read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes)
//-----------------------------------------------------------------------------
2019-05-13 18:49:41 +08:00
void MifareReadSector ( uint8_t arg0 , uint8_t arg1 , uint8_t * datain ) {
2019-03-10 07:00:59 +08:00
// params
2019-03-10 03:34:41 +08:00
uint8_t sectorNo = arg0 ;
uint8_t keyType = arg1 ;
uint64_t ui64Key = 0 ;
ui64Key = bytes_to_num ( datain , 6 ) ;
// variables
2019-03-21 22:19:18 +08:00
uint8_t isOK = 0 ;
uint8_t dataoutbuf [ 16 * 16 ] ;
2019-03-10 03:34:41 +08:00
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 ;
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ) {
2019-03-10 03:34:41 +08:00
isOK = 0 ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
}
2019-03-10 07:00:59 +08:00
if ( isOK & & mifare_classic_auth ( pcs , cuid , FirstBlockOfSector ( sectorNo ) , keyType , ui64Key , AUTH_FIRST ) ) {
2019-03-10 03:34:41 +08:00
isOK = 0 ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Auth error " ) ;
2019-03-10 03:34:41 +08:00
}
for ( uint8_t blockNo = 0 ; isOK & & blockNo < NumBlocksPerSector ( sectorNo ) ; blockNo + + ) {
2019-03-10 07:00:59 +08:00
if ( mifare_classic_readblock ( pcs , cuid , FirstBlockOfSector ( sectorNo ) + blockNo , dataoutbuf + 16 * blockNo ) ) {
2019-03-10 03:34:41 +08:00
isOK = 0 ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Read sector %2d block %2d error " , sectorNo , blockNo ) ;
2019-03-10 03:34:41 +08:00
break ;
}
}
2019-03-10 07:00:59 +08:00
if ( mifare_classic_halt ( pcs , cuid ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
}
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) DbpString ( " READ SECTOR FINISHED " ) ;
2019-03-10 03:34:41 +08:00
crypto1_destroy ( pcs ) ;
LED_B_ON ( ) ;
2019-04-18 18:43:35 +08:00
reply_old ( CMD_ACK , isOK , 0 , 0 , dataoutbuf , 16 * NumBlocksPerSector ( sectorNo ) ) ;
2019-03-10 03:34:41 +08:00
LED_B_OFF ( ) ;
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-03-09 15:49:41 +08:00
}
// arg0 = blockNo (start)
// arg1 = Pages (number of blocks)
// arg2 = useKey
// datain = KEY bytes
2019-03-10 18:20:22 +08:00
void MifareUReadCard ( uint8_t arg0 , uint16_t arg1 , uint8_t arg2 , uint8_t * datain ) {
2019-03-10 03:34:41 +08:00
LEDsoff ( ) ;
LED_A_ON ( ) ;
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
// free eventually allocated BigBuf memory
2019-03-10 07:00:59 +08:00
BigBuf_free ( ) ;
BigBuf_Clear_ext ( false ) ;
2019-03-10 03:34:41 +08:00
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 ) ;
2019-03-10 07:00:59 +08:00
if ( dataout = = NULL ) {
2019-03-10 03:34:41 +08:00
Dbprintf ( " out of memory " ) ;
OnError ( 1 ) ;
return ;
}
int len = iso14443a_select_card ( NULL , NULL , NULL , true , 0 , true ) ;
if ( ! len ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Can't select card (RC:%d) " , len ) ;
2019-03-10 03:34:41 +08:00
OnError ( 1 ) ;
return ;
}
// UL-C authentication
2019-03-10 07:00:59 +08:00
if ( useKey ) {
2019-03-10 03:34:41 +08:00
uint8_t key [ 16 ] = { 0x00 } ;
2019-03-10 07:00:59 +08:00
memcpy ( key , datain , sizeof ( key ) ) ;
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
if ( ! mifare_ultra_auth ( key ) ) {
2019-03-10 03:34:41 +08:00
OnError ( 1 ) ;
return ;
}
}
// UL-EV1 / NTAG authentication
if ( usePwd ) {
uint8_t pwd [ 4 ] = { 0x00 } ;
memcpy ( pwd , datain , sizeof ( pwd ) ) ;
2019-03-10 07:00:59 +08:00
uint8_t pack [ 4 ] = { 0 , 0 , 0 , 0 } ;
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
if ( ! mifare_ul_ev1_auth ( pwd , pack ) ) {
2019-03-10 03:34:41 +08:00
OnError ( 1 ) ;
return ;
}
}
2019-03-10 07:00:59 +08:00
for ( int i = 0 ; i < blocks ; i + + ) {
if ( ( i * 4 ) + 4 > = CARD_MEMORY_SIZE ) {
2019-03-10 03:34:41 +08:00
Dbprintf ( " Data exceeds buffer!! " ) ;
break ;
}
len = mifare_ultra_readblock ( blockNo + i , dataout + 4 * i ) ;
if ( len ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Read block %d error " , i ) ;
2019-03-10 03:34:41 +08:00
// 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 ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 3 ) ;
return ;
}
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " Blocks read %d " , countblocks ) ;
2019-03-10 03:34:41 +08:00
countblocks * = 4 ;
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , 1 , countblocks , BigBuf_max_traceLen ( ) , 0 , 0 ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
BigBuf_free ( ) ;
set_tracing ( false ) ;
2019-03-09 15:49:41 +08:00
}
//-----------------------------------------------------------------------------
2019-03-09 15:59:13 +08:00
// Select, Authenticate, Write a MIFARE tag.
2019-03-09 15:49:41 +08:00
// read block
//-----------------------------------------------------------------------------
2019-05-13 18:49:41 +08:00
void MifareWriteBlock ( uint8_t arg0 , uint8_t arg1 , uint8_t * datain ) {
2019-03-10 03:34:41 +08:00
// params
uint8_t blockNo = arg0 ;
uint8_t keyType = arg1 ;
uint64_t ui64Key = 0 ;
2019-03-21 22:19:18 +08:00
uint8_t blockdata [ 16 ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
ui64Key = bytes_to_num ( datain , 6 ) ;
memcpy ( blockdata , datain + 10 , 16 ) ;
// variables
2019-03-21 22:19:18 +08:00
uint8_t isOK = 0 ;
2019-03-10 03:34:41 +08:00
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 ) {
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
break ;
} ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_auth ( pcs , cuid , blockNo , keyType , ui64Key , AUTH_FIRST ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Auth error " ) ;
2019-03-10 03:34:41 +08:00
break ;
} ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_writeblock ( pcs , cuid , blockNo , blockdata ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Write block error " ) ;
2019-03-10 03:34:41 +08:00
break ;
} ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_halt ( pcs , cuid ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
break ;
} ;
isOK = 1 ;
break ;
}
crypto1_destroy ( pcs ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) DbpString ( " WRITE BLOCK FINISHED " ) ;
2019-03-10 03:34:41 +08:00
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , isOK , 0 , 0 , 0 , 0 ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-03-09 15:49:41 +08:00
}
2019-03-09 15:59:13 +08:00
/* // Command not needed but left for future testing
2019-03-09 15:49:41 +08:00
void MifareUWriteBlockCompat ( uint8_t arg0 , uint8_t * datain )
{
2019-03-10 03:34:41 +08:00
uint8_t blockNo = arg0 ;
2019-03-21 22:19:18 +08:00
uint8_t blockdata [ 16 ] = { 0x00 } ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
memcpy ( blockdata , datain , 16 ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
uint8_t uid [ 10 ] = { 0x00 } ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
LED_A_ON ( ) ; LED_B_OFF ( ) ; LED_C_OFF ( ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
clear_trace ( ) ;
set_tracing ( true ) ;
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
if ( ! iso14443a_select_card ( uid , NULL , NULL , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ;
} ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
if ( mifare_ultra_writeblock_compat ( blockNo , blockdata ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Write block error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ; } ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
if ( mifare_ultra_halt ( ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ;
} ;
2019-03-09 15:49:41 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) DbpString ( " WRITE BLOCK FINISHED " ) ;
2019-03-09 15:49:41 +08:00
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , 1 , 0 , 0 , 0 , 0 ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
2019-03-09 15:49:41 +08:00
}
*/
// 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.
2019-03-10 18:20:22 +08:00
void MifareUWriteBlock ( uint8_t arg0 , uint8_t arg1 , uint8_t * datain ) {
2019-03-10 03:34:41 +08:00
uint8_t blockNo = arg0 ;
bool useKey = ( arg1 = = 1 ) ; //UL_C
bool usePwd = ( arg1 = = 2 ) ; //UL_EV1/NTAG
2019-03-21 22:19:18 +08:00
uint8_t blockdata [ 4 ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
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 ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ;
} ;
// UL-C authentication
2019-03-10 07:00:59 +08:00
if ( useKey ) {
2019-03-10 03:34:41 +08:00
uint8_t key [ 16 ] = { 0x00 } ;
2019-03-10 07:00:59 +08:00
memcpy ( key , datain + 4 , sizeof ( key ) ) ;
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
if ( ! mifare_ultra_auth ( key ) ) {
2019-03-10 03:34:41 +08:00
OnError ( 1 ) ;
return ;
}
}
// UL-EV1 / NTAG authentication
if ( usePwd ) {
uint8_t pwd [ 4 ] = { 0x00 } ;
2019-03-10 07:00:59 +08:00
memcpy ( pwd , datain + 4 , 4 ) ;
uint8_t pack [ 4 ] = { 0 , 0 , 0 , 0 } ;
2019-03-10 03:34:41 +08:00
if ( ! mifare_ul_ev1_auth ( pwd , pack ) ) {
OnError ( 1 ) ;
return ;
}
}
if ( mifare_ultra_writeblock ( blockNo , blockdata ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Write block error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ;
} ;
if ( mifare_ultra_halt ( ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ;
} ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) DbpString ( " WRITE BLOCK FINISHED " ) ;
2019-03-10 03:34:41 +08:00
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , 1 , 0 , 0 , 0 , 0 ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
void MifareUSetPwd ( uint8_t arg0 , uint8_t * datain ) {
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
uint8_t pwd [ 16 ] = { 0x00 } ;
2019-03-21 22:19:18 +08:00
uint8_t blockdata [ 4 ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
memcpy ( pwd , datain , 16 ) ;
2019-03-10 07:00:59 +08:00
LED_A_ON ( ) ;
LED_B_OFF ( ) ;
LED_C_OFF ( ) ;
2019-03-10 03:34:41 +08:00
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
clear_trace ( ) ;
set_tracing ( true ) ;
if ( ! iso14443a_select_card ( NULL , NULL , NULL , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ;
} ;
blockdata [ 0 ] = pwd [ 7 ] ;
blockdata [ 1 ] = pwd [ 6 ] ;
blockdata [ 2 ] = pwd [ 5 ] ;
blockdata [ 3 ] = pwd [ 4 ] ;
2019-03-10 07:00:59 +08:00
if ( mifare_ultra_writeblock ( 44 , blockdata ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Write block error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 44 ) ;
return ;
} ;
blockdata [ 0 ] = pwd [ 3 ] ;
blockdata [ 1 ] = pwd [ 2 ] ;
blockdata [ 2 ] = pwd [ 1 ] ;
blockdata [ 3 ] = pwd [ 0 ] ;
2019-03-10 07:00:59 +08:00
if ( mifare_ultra_writeblock ( 45 , blockdata ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Write block error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 45 ) ;
return ;
} ;
blockdata [ 0 ] = pwd [ 15 ] ;
blockdata [ 1 ] = pwd [ 14 ] ;
blockdata [ 2 ] = pwd [ 13 ] ;
blockdata [ 3 ] = pwd [ 12 ] ;
2019-03-10 07:00:59 +08:00
if ( mifare_ultra_writeblock ( 46 , blockdata ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Write block error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 46 ) ;
return ;
} ;
blockdata [ 0 ] = pwd [ 11 ] ;
blockdata [ 1 ] = pwd [ 10 ] ;
blockdata [ 2 ] = pwd [ 9 ] ;
blockdata [ 3 ] = pwd [ 8 ] ;
2019-03-10 07:00:59 +08:00
if ( mifare_ultra_writeblock ( 47 , blockdata ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Write block error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 47 ) ;
return ;
} ;
if ( mifare_ultra_halt ( ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 0 ) ;
return ;
} ;
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , 1 , 0 , 0 , 0 , 0 ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-03-09 15:49:41 +08:00
}
// Return 1 if the nonce is invalid else return 0
2019-03-10 18:20:22 +08:00
int valid_nonce ( uint32_t Nt , uint32_t NtEnc , uint32_t Ks1 , uint8_t * parity ) {
2019-03-10 07:00:59 +08:00
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 ;
2019-03-09 15:49:41 +08:00
}
2019-05-13 19:31:11 +08:00
void MifareAcquireNonces ( uint32_t arg0 , uint32_t flags ) {
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
uint8_t uid [ 10 ] = { 0x00 } ;
uint8_t answer [ MAX_MIFARE_FRAME_SIZE ] = { 0x00 } ;
uint8_t par [ 1 ] = { 0x00 } ;
2019-05-01 03:10:11 +08:00
uint8_t buf [ PM3_CMD_DATA_SIZE ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
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 ( ) ;
2019-03-10 07:00:59 +08:00
BigBuf_free ( ) ;
BigBuf_Clear_ext ( false ) ;
2019-03-10 03:34:41 +08:00
clear_trace ( ) ;
set_tracing ( true ) ;
if ( initialize )
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
LED_C_ON ( ) ;
2019-05-01 03:10:11 +08:00
for ( uint16_t i = 0 ; i < = PM3_CMD_DATA_SIZE - 4 ; i + = 4 ) {
2019-03-10 03:34:41 +08:00
// 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 ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " AcquireNonces: Can't select card (ALL) " ) ;
2019-03-10 03:34:41 +08:00
continue ;
}
switch ( card_info . uidlen ) {
2019-03-10 07:00:59 +08:00
case 4 :
cascade_levels = 1 ;
break ;
case 7 :
cascade_levels = 2 ;
break ;
case 10 :
cascade_levels = 3 ;
break ;
default :
break ;
2019-03-10 03:34:41 +08:00
}
have_uid = true ;
} else { // no need for anticollision. We can directly select the card
if ( ! iso14443a_fast_select_card ( uid , cascade_levels ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " AcquireNonces: Can't select card (UID) " ) ;
2019-03-10 03:34:41 +08:00
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 ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " AcquireNonces: Auth1 error " ) ;
2019-03-10 03:34:41 +08:00
continue ;
}
num_nonces + + ;
// Save the tag nonce (nt)
buf [ i ] = answer [ 0 ] ;
2019-03-10 07:00:59 +08:00
buf [ i + 1 ] = answer [ 1 ] ;
buf [ i + 2 ] = answer [ 2 ] ;
buf [ i + 3 ] = answer [ 3 ] ;
2019-03-10 03:34:41 +08:00
}
LED_C_OFF ( ) ;
LED_B_ON ( ) ;
2019-04-18 18:43:35 +08:00
reply_old ( CMD_ACK , isOK , cuid , num_nonces - 1 , buf , sizeof ( buf ) ) ;
2019-03-10 03:34:41 +08:00
LED_B_OFF ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) DbpString ( " AcquireNonces finished " ) ;
2019-03-10 03:34:41 +08:00
if ( field_off ) {
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
}
2019-03-09 15:49:41 +08:00
}
//-----------------------------------------------------------------------------
// acquire encrypted nonces in order to perform the attack described in
// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened
2019-03-09 15:59:13 +08:00
// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on
2019-03-09 15:49:41 +08:00
// Computer and Communications Security, 2015
//-----------------------------------------------------------------------------
2019-03-10 18:20:22 +08:00
void MifareAcquireEncryptedNonces ( uint32_t arg0 , uint32_t arg1 , uint32_t flags , uint8_t * datain ) {
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
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 } ;
2019-05-01 03:10:11 +08:00
uint8_t buf [ PM3_CMD_DATA_SIZE ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
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 ( ) ;
2019-03-10 07:00:59 +08:00
BigBuf_free ( ) ;
BigBuf_Clear_ext ( false ) ;
2019-03-10 03:34:41 +08:00
clear_trace ( ) ;
set_tracing ( false ) ;
if ( initialize )
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
LED_C_ON ( ) ;
2019-05-01 03:10:11 +08:00
for ( uint16_t i = 0 ; i < = PM3_CMD_DATA_SIZE - 9 ; ) {
2019-03-10 03:34:41 +08:00
// Test if the action was cancelled
2019-03-10 07:00:59 +08:00
if ( BUTTON_PRESS ( ) ) {
2019-03-10 03:34:41 +08:00
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 ;
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , & card_info , & cuid , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " AcquireNonces: Can't select card (ALL) " ) ;
2019-03-10 03:34:41 +08:00
continue ;
}
switch ( card_info . uidlen ) {
2019-03-10 07:00:59 +08:00
case 4 :
cascade_levels = 1 ;
break ;
case 7 :
cascade_levels = 2 ;
break ;
case 10 :
cascade_levels = 3 ;
break ;
default :
break ;
2019-03-10 03:34:41 +08:00
}
have_uid = true ;
} else { // no need for anticollision. We can directly select the card
if ( ! iso14443a_fast_select_card ( uid , cascade_levels ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " AcquireNonces: Can't select card (UID) " ) ;
2019-03-10 03:34:41 +08:00
continue ;
}
}
if ( slow )
SpinDelayUs ( HARDNESTED_PRE_AUTHENTICATION_LEADTIME ) ;
uint32_t nt1 ;
if ( mifare_classic_authex ( pcs , cuid , blockNo , keyType , ui64Key , AUTH_FIRST , & nt1 , NULL ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " AcquireNonces: Auth1 error " ) ;
2019-03-10 03:34:41 +08:00
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 ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " AcquireNonces: Auth2 error len=%d " , len ) ;
2019-03-10 03:34:41 +08:00
continue ;
}
num_nonces + + ;
if ( num_nonces % 2 ) {
2019-03-10 07:00:59 +08:00
memcpy ( buf + i , receivedAnswer , 4 ) ;
2019-03-10 03:34:41 +08:00
nt_par_enc = par_enc [ 0 ] & 0xf0 ;
} else {
nt_par_enc | = par_enc [ 0 ] > > 4 ;
2019-03-10 07:00:59 +08:00
memcpy ( buf + i + 4 , receivedAnswer , 4 ) ;
memcpy ( buf + i + 8 , & nt_par_enc , 1 ) ;
2019-03-10 03:34:41 +08:00
i + = 9 ;
}
}
LED_C_OFF ( ) ;
crypto1_destroy ( pcs ) ;
LED_B_ON ( ) ;
2019-04-18 18:43:35 +08:00
reply_old ( CMD_ACK , isOK , cuid , num_nonces , buf , sizeof ( buf ) ) ;
2019-03-10 03:34:41 +08:00
LED_B_OFF ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) DbpString ( " AcquireEncryptedNonces finished " ) ;
2019-03-10 03:34:41 +08:00
if ( field_off ) {
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
}
2019-03-09 15:49:41 +08:00
}
//-----------------------------------------------------------------------------
2019-03-09 15:59:13 +08:00
// MIFARE nested authentication.
//
2019-03-09 15:49:41 +08:00
//-----------------------------------------------------------------------------
2019-04-07 02:35:58 +08:00
void MifareNested ( uint32_t arg0 , uint32_t arg1 , uint32_t arg2 , uint8_t * datain ) {
2019-03-10 03:34:41 +08:00
// params
uint8_t blockNo = arg0 & 0xff ;
uint8_t keyType = ( arg0 > > 8 ) & 0xff ;
uint8_t targetBlockNo = arg1 & 0xff ;
uint8_t targetKeyType = ( arg1 > > 8 ) & 0xff ;
2019-04-07 02:35:58 +08:00
// calibrate = arg2
2019-03-10 03:34:41 +08:00
uint64_t ui64Key = 0 ;
ui64Key = bytes_to_num ( datain , 6 ) ;
// variables
2019-06-08 00:41:39 +08:00
uint16_t i , j , len ;
2019-03-10 03:34:41 +08:00
static uint16_t dmin , dmax ;
uint8_t uid [ 10 ] = { 0x00 } ;
2019-06-08 00:41:39 +08:00
uint32_t cuid = 0 , nt1 , nt2 , nttest , ks1 ;
2019-03-10 03:34:41 +08:00
uint8_t par [ 1 ] = { 0x00 } ;
uint32_t target_nt [ 2 ] = { 0x00 } , target_ks [ 2 ] = { 0x00 } ;
uint8_t par_array [ 4 ] = { 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
2019-03-10 07:00:59 +08:00
BigBuf_free ( ) ;
BigBuf_Clear_ext ( false ) ;
2019-03-10 03:34:41 +08:00
2019-04-07 02:35:58 +08:00
if ( arg2 ) clear_trace ( ) ;
2019-03-10 03:34:41 +08:00
set_tracing ( true ) ;
// statistics on nonce distance
int16_t isOK = 0 ;
2019-03-10 07:00:59 +08:00
# define NESTED_MAX_TRIES 12
2019-04-07 02:35:58 +08:00
if ( arg2 ) { // calibrate: for first call only. Otherwise reuse previous calibration
2019-03-10 03:34:41 +08:00
LED_B_ON ( ) ;
WDT_HIT ( ) ;
2019-06-08 00:41:39 +08:00
uint16_t unsuccessfull_tries = 0 ;
uint16_t davg = 0 ;
dmax = 0 ;
2019-03-10 03:34:41 +08:00
dmin = 2000 ;
delta_time = 0 ;
2019-06-08 00:41:39 +08:00
uint16_t rtr ;
2019-03-10 03:34:41 +08:00
for ( rtr = 0 ; rtr < 17 ; rtr + + ) {
// Test if the action was cancelled
2019-03-10 07:00:59 +08:00
if ( BUTTON_PRESS ( ) ) {
2019-03-10 03:34:41 +08:00
isOK = - 2 ;
break ;
}
// prepare next select. No need to power down the card.
2019-03-10 07:00:59 +08:00
if ( mifare_classic_halt ( pcs , cuid ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " Nested: Halt error " ) ;
2019-03-10 03:34:41 +08:00
rtr - - ;
continue ;
}
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " Nested: Can't select card " ) ;
2019-03-10 03:34:41 +08:00
rtr - - ;
continue ;
} ;
auth1_time = 0 ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_authex ( pcs , cuid , blockNo , keyType , ui64Key , AUTH_FIRST , & nt1 , & auth1_time ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " Nested: Auth1 error " ) ;
2019-03-10 03:34:41 +08:00
rtr - - ;
continue ;
} ;
auth2_time = ( delta_time ) ? auth1_time + delta_time : 0 ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_authex ( pcs , cuid , blockNo , keyType , ui64Key , AUTH_NESTED , & nt2 , & auth2_time ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " Nested: Auth2 error " ) ;
2019-03-10 03:34:41 +08:00
rtr - - ;
continue ;
} ;
2019-06-08 00:41:39 +08:00
uint32_t nttmp = prng_successor ( nt1 , 100 ) ; //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160
2019-03-10 03:34:41 +08:00
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 ) ;
2019-03-10 07:00:59 +08:00
} else {
2019-03-10 03:34:41 +08:00
delta_time = auth2_time - auth1_time + 32 ; // allow some slack for proper timing
}
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " Nested: calibrating... ntdist=%d " , i ) ;
2019-03-10 03:34:41 +08:00
} else {
unsuccessfull_tries + + ;
if ( unsuccessfull_tries > NESTED_MAX_TRIES ) { // card isn't vulnerable to nested attack (random numbers are not predictable)
isOK = - 3 ;
}
}
}
2019-03-10 07:00:59 +08:00
davg = ( davg + ( rtr - 1 ) / 2 ) / ( rtr - 1 ) ;
2019-03-10 03:34:41 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " rtr=%d isOK=%d min=%d max=%d avg=%d, delta_time=%d " , rtr , isOK , dmin , dmax , davg , delta_time ) ;
2019-03-10 03:34:41 +08:00
dmin = davg - 2 ;
dmax = davg + 2 ;
LED_B_OFF ( ) ;
}
2019-03-09 15:59:13 +08:00
// -------------------------------------------------------------------------------------------------
2019-03-10 03:34:41 +08:00
LED_C_ON ( ) ;
// get crypted nonces for target sector
2019-03-10 07:00:59 +08:00
for ( i = 0 ; i < 2 & & ! isOK ; i + + ) { // look for exactly two different nonces
2019-03-10 03:34:41 +08:00
target_nt [ i ] = 0 ;
2019-03-10 07:00:59 +08:00
while ( target_nt [ i ] = = 0 ) { // continue until we have an unambiguous nonce
2019-03-10 03:34:41 +08:00
// prepare next select. No need to power down the card.
2019-03-10 07:00:59 +08:00
if ( mifare_classic_halt ( pcs , cuid ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " Nested: Halt error " ) ;
2019-03-10 03:34:41 +08:00
continue ;
}
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " Nested: Can't select card " ) ;
2019-03-10 03:34:41 +08:00
continue ;
} ;
auth1_time = 0 ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_authex ( pcs , cuid , blockNo , keyType , ui64Key , AUTH_FIRST , & nt1 , & auth1_time ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " Nested: Auth1 error " ) ;
2019-03-10 03:34:41 +08:00
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 ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 2 ) Dbprintf ( " Nested: Auth2 error len=%d " , len ) ;
2019-03-10 03:34:41 +08:00
continue ;
} ;
nt2 = bytes_to_num ( receivedAnswer , 4 ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " Nonce#%d: Testing nt1=%08x nt2enc=%08x nt2par=%02x " , i + 1 , nt1 , nt2 , par [ 0 ] ) ;
2019-03-10 03:34:41 +08:00
// Parity validity check
for ( j = 0 ; j < 4 ; j + + ) {
2019-03-10 07:00:59 +08:00
par_array [ j ] = ( oddparity8 ( receivedAnswer [ j ] ) ! = ( ( par [ 0 ] > > ( 7 - j ) ) & 0x01 ) ) ;
2019-03-10 03:34:41 +08:00
}
ncount = 0 ;
nttest = prng_successor ( nt1 , dmin - 1 ) ;
for ( j = dmin ; j < dmax + 1 ; j + + ) {
nttest = prng_successor ( nttest , 1 ) ;
ks1 = nt2 ^ nttest ;
2019-03-10 07:00:59 +08:00
if ( valid_nonce ( nttest , nt2 , ks1 , par_array ) ) {
2019-03-10 03:34:41 +08:00
if ( ncount > 0 ) { // we are only interested in disambiguous nonces, try again
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " Nonce#%d: dismissed (ambigous), ntdist=%d " , i + 1 , j ) ;
2019-03-10 03:34:41 +08:00
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 ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " Nonce#2: dismissed (= nonce#1), ntdist=%d " , j ) ;
2019-03-10 03:34:41 +08:00
break ;
}
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " Nonce#%d: valid, ntdist=%d " , i + 1 , j ) ;
2019-03-10 03:34:41 +08:00
}
}
2019-06-06 16:05:09 +08:00
if ( target_nt [ i ] = = 0 & & j = = dmax + 1 & & DBGLEVEL > = 3 ) Dbprintf ( " Nonce#%d: dismissed (all invalid) " , i + 1 ) ;
2019-03-10 03:34:41 +08:00
}
}
LED_C_OFF ( ) ;
crypto1_destroy ( pcs ) ;
uint8_t buf [ 4 + 4 * 4 ] = { 0 } ;
memcpy ( buf , & cuid , 4 ) ;
2019-03-10 07:00:59 +08:00
memcpy ( buf + 4 , & target_nt [ 0 ] , 4 ) ;
memcpy ( buf + 8 , & target_ks [ 0 ] , 4 ) ;
memcpy ( buf + 12 , & target_nt [ 1 ] , 4 ) ;
memcpy ( buf + 16 , & target_ks [ 1 ] , 4 ) ;
2019-03-10 03:34:41 +08:00
LED_B_ON ( ) ;
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , isOK , 0 , targetBlockNo + ( targetKeyType * 0x100 ) , buf , sizeof ( buf ) ) ;
2019-03-10 03:34:41 +08:00
LED_B_OFF ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) DbpString ( " NESTED FINISHED " ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-03-09 15:49:41 +08:00
}
//-----------------------------------------------------------------------------
2019-03-09 15:59:13 +08:00
// MIFARE check keys. key count up to 85.
//
//-----------------------------------------------------------------------------
2019-03-09 15:49:41 +08:00
typedef struct sector_t {
2019-03-10 03:34:41 +08:00
uint8_t keyA [ 6 ] ;
uint8_t keyB [ 6 ] ;
2019-03-09 15:49:41 +08:00
} sector_t ;
typedef struct chk_t {
2019-03-10 03:34:41 +08:00
uint64_t key ;
uint32_t cuid ;
uint8_t cl ;
uint8_t block ;
uint8_t keyType ;
uint8_t * uid ;
struct Crypto1State * pcs ;
2019-03-09 15:49:41 +08:00
} chk_t ;
// checks one key.
// fast select, tries 5 times to select
2019-03-09 15:59:13 +08:00
//
2019-03-09 15:49:41 +08:00
// return:
// 2 = failed to select.
// 1 = wrong key
// 0 = correct key
2019-03-10 18:20:22 +08:00
uint8_t chkKey ( struct chk_t * c ) {
2019-03-10 03:34:41 +08:00
uint8_t i = 0 , res = 2 ;
2019-03-10 07:00:59 +08:00
while ( i < 5 ) {
2019-03-10 03:34:41 +08:00
// 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 successfull auth, send HALT
// if ( !res )
2019-03-10 07:00:59 +08:00
// mifare_classic_halt_ex(c->pcs);
2019-03-10 03:34:41 +08:00
break ;
}
return res ;
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
uint8_t chkKey_readb ( struct chk_t * c , uint8_t * keyb ) {
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
if ( ! iso14443a_fast_select_card ( c - > uid , c - > cl ) )
return 2 ;
2019-03-10 07:00:59 +08:00
if ( mifare_classic_authex ( c - > pcs , c - > cuid , c - > block , 0 , c - > key , AUTH_FIRST , NULL , NULL ) )
2019-03-10 03:34:41 +08:00
return 1 ;
uint8_t data [ 16 ] = { 0x00 } ;
uint8_t res = mifare_classic_readblock ( c - > pcs , c - > cuid , c - > block , data ) ;
// successful read
2019-03-10 07:00:59 +08:00
if ( ! res ) {
2019-03-10 03:34:41 +08:00
// data was something else than zeros.
2019-03-10 07:00:59 +08:00
if ( memcmp ( data + 10 , " \x00 \x00 \x00 \x00 \x00 \x00 " , 6 ) ! = 0 ) {
memcpy ( keyb , data + 10 , 6 ) ;
2019-03-10 03:34:41 +08:00
res = 0 ;
} else {
res = 3 ;
}
mifare_classic_halt_ex ( c - > pcs ) ;
}
return res ;
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
void chkKey_scanA ( struct chk_t * c , struct sector_t * k_sector , uint8_t * found , uint8_t * sectorcnt , uint8_t * foundkeys ) {
2019-03-10 03:34:41 +08:00
for ( uint8_t s = 0 ; s < * sectorcnt ; s + + ) {
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// skip already found A keys
2019-03-10 07:00:59 +08:00
if ( found [ ( s * 2 ) ] )
2019-03-10 03:34:41 +08:00
continue ;
2019-03-09 15:49:41 +08:00
2019-03-10 07:00:59 +08:00
c - > block = FirstBlockOfSector ( s ) ;
if ( chkKey ( c ) = = 0 ) {
2019-03-10 03:34:41 +08:00
num_to_bytes ( c - > key , 6 , k_sector [ s ] . keyA ) ;
2019-03-10 07:00:59 +08:00
found [ ( s * 2 ) ] = 1 ;
2019-03-10 03:34:41 +08:00
+ + * foundkeys ;
2019-03-09 15:59:13 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " ChkKeys_fast: Scan A found (%d) " , c - > block ) ;
2019-03-10 03:34:41 +08:00
}
}
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
void chkKey_scanB ( struct chk_t * c , struct sector_t * k_sector , uint8_t * found , uint8_t * sectorcnt , uint8_t * foundkeys ) {
2019-03-10 03:34:41 +08:00
for ( uint8_t s = 0 ; s < * sectorcnt ; s + + ) {
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// skip already found B keys
2019-03-10 07:00:59 +08:00
if ( found [ ( s * 2 ) + 1 ] )
2019-03-10 03:34:41 +08:00
continue ;
2019-03-09 15:49:41 +08:00
2019-03-10 07:00:59 +08:00
c - > block = FirstBlockOfSector ( s ) ;
if ( chkKey ( c ) = = 0 ) {
2019-03-10 03:34:41 +08:00
num_to_bytes ( c - > key , 6 , k_sector [ s ] . keyB ) ;
2019-03-10 07:00:59 +08:00
found [ ( s * 2 ) + 1 ] = 1 ;
2019-03-10 03:34:41 +08:00
+ + * foundkeys ;
2019-03-09 15:59:13 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " ChkKeys_fast: Scan B found (%d) " , c - > block ) ;
2019-03-10 03:34:41 +08:00
}
}
2019-03-09 15:49:41 +08:00
}
// loop all A keys,
// when A is found but not B, try to read B.
2019-03-10 18:20:22 +08:00
void chkKey_loopBonly ( struct chk_t * c , struct sector_t * k_sector , uint8_t * found , uint8_t * sectorcnt , uint8_t * foundkeys ) {
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// read Block B, if A is found.
for ( uint8_t s = 0 ; s < * sectorcnt ; + + s ) {
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
if ( found [ ( s * 2 ) ] & & found [ ( s * 2 ) + 1 ] )
2019-03-10 03:34:41 +08:00
continue ;
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
c - > block = ( FirstBlockOfSector ( s ) + NumBlocksPerSector ( s ) - 1 ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// A but not B
2019-03-10 07:00:59 +08:00
if ( found [ ( s * 2 ) ] & & ! found [ ( s * 2 ) + 1 ] ) {
2019-03-10 03:34:41 +08:00
c - > key = bytes_to_num ( k_sector [ s ] . keyA , 6 ) ;
uint8_t status = chkKey_readb ( c , k_sector [ s ] . keyB ) ;
2019-03-10 07:00:59 +08:00
if ( status = = 0 ) {
found [ ( s * 2 ) + 1 ] = 1 ;
2019-03-10 03:34:41 +08:00
+ + * foundkeys ;
2019-03-09 15:49:41 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 3 ) Dbprintf ( " ChkKeys_fast: Reading B found (%d) " , c - > block ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// try quick find all B?
// assume: keys comes in groups. Find one B, test against all B.
2019-03-10 07:00:59 +08:00
c - > key = bytes_to_num ( k_sector [ s ] . keyB , 6 ) ;
2019-03-10 03:34:41 +08:00
c - > keyType = 1 ;
chkKey_scanB ( c , k_sector , found , sectorcnt , foundkeys ) ;
}
}
}
2019-03-09 15:49:41 +08:00
}
// 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
2019-03-10 18:20:22 +08:00
void MifareChkKeys_fast ( uint32_t arg0 , uint32_t arg1 , uint32_t arg2 , uint8_t * datain ) {
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// 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 ;
2019-03-09 15:49:41 +08:00
2019-03-09 15:59:13 +08:00
# ifdef WITH_FLASH
2019-03-10 07:00:59 +08:00
if ( use_flashmem ) {
2019-03-10 03:34:41 +08:00
BigBuf_free ( ) ;
uint16_t isok = 0 ;
uint8_t size [ 2 ] = { 0x00 , 0x00 } ;
isok = Flash_ReadData ( DEFAULT_MF_KEYS_OFFSET , size , 2 ) ;
2019-03-10 07:00:59 +08:00
if ( isok ! = 2 )
2019-03-10 03:34:41 +08:00
goto OUT ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
keyCount = size [ 1 ] < < 8 | size [ 0 ] ;
2019-03-09 15:59:13 +08:00
2019-03-13 07:30:11 +08:00
if ( keyCount = = 0 | | keyCount = = 0xFFFF )
2019-03-10 03:34:41 +08:00
goto OUT ;
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
datain = BigBuf_malloc ( keyCount * 6 ) ;
if ( datain = = NULL )
2019-03-10 03:34:41 +08:00
goto OUT ;
2019-03-09 15:49:41 +08:00
2019-03-10 07:00:59 +08:00
isok = Flash_ReadData ( DEFAULT_MF_KEYS_OFFSET + 2 , datain , keyCount * 6 ) ;
if ( isok ! = keyCount * 6 )
2019-03-10 03:34:41 +08:00
goto OUT ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
}
2019-03-09 15:49:41 +08:00
# endif
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
if ( uid = = NULL | | firstchunk ) {
uid = BigBuf_malloc ( 10 ) ;
2019-03-10 07:00:59 +08:00
if ( uid = = NULL )
2019-03-10 03:34:41 +08:00
goto OUT ;
}
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
LEDsoff ( ) ;
LED_A_ON ( ) ;
2019-03-10 07:00:59 +08:00
if ( firstchunk ) {
2019-03-10 03:34:41 +08:00
clear_trace ( ) ;
set_tracing ( false ) ;
2019-03-10 07:00:59 +08:00
memset ( k_sector , 0x00 , 480 + 10 ) ;
2019-03-10 03:34:41 +08:00
memset ( found , 0x00 , sizeof ( found ) ) ;
foundkeys = 0 ;
iso14a_card_select_t card_info ;
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , & card_info , & cuid , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " ChkKeys_fast: Can't select card (ALL) " ) ;
2019-03-10 03:34:41 +08:00
goto OUT ;
}
switch ( card_info . uidlen ) {
2019-03-10 07:00:59 +08:00
case 4 :
cascade_levels = 1 ;
break ;
case 7 :
cascade_levels = 2 ;
break ;
case 10 :
cascade_levels = 3 ;
break ;
default :
break ;
2019-03-10 03:34:41 +08:00
}
CHK_TIMEOUT ( ) ;
}
// 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.
2019-03-10 07:00:59 +08:00
if ( strategy = = 1 | | use_flashmem ) {
2019-03-10 03:34:41 +08:00
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 ) {
2019-03-09 15:49:41 +08:00
2019-03-10 07:00:59 +08:00
if ( found [ ( s * 2 ) ] & & found [ ( s * 2 ) + 1 ] )
2019-03-10 03:34:41 +08:00
continue ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
for ( uint16_t i = s_point ; i < keyCount ; + + i ) {
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// Allow button press / usb cmd to interrupt device
2019-06-03 06:01:08 +08:00
if ( BUTTON_PRESS ( ) & & ! data_available ( ) ) {
2019-03-10 03:34:41 +08:00
goto OUT ;
}
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// found all keys?
2019-03-10 07:00:59 +08:00
if ( foundkeys = = allkeys )
2019-03-10 03:34:41 +08:00
goto OUT ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
WDT_HIT ( ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector
2019-03-10 07:00:59 +08:00
chk_data . block = FirstBlockOfSector ( s ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// new key
chk_data . key = bytes_to_num ( datain + i * 6 , 6 ) ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// skip already found A keys
2019-03-10 07:00:59 +08:00
if ( ! found [ ( s * 2 ) ] ) {
2019-03-10 03:34:41 +08:00
chk_data . keyType = 0 ;
2019-03-10 07:00:59 +08:00
status = chkKey ( & chk_data ) ;
if ( status = = 0 ) {
2019-03-10 03:34:41 +08:00
memcpy ( k_sector [ s ] . keyA , datain + i * 6 , 6 ) ;
2019-03-10 07:00:59 +08:00
found [ ( s * 2 ) ] = 1 ;
2019-03-10 03:34:41 +08:00
+ + foundkeys ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
chkKey_scanA ( & chk_data , k_sector , found , & sectorcnt , & foundkeys ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// read Block B, if A is found.
2019-03-10 07:00:59 +08:00
chkKey_loopBonly ( & chk_data , k_sector , found , & sectorcnt , & foundkeys ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
chk_data . keyType = 1 ;
chkKey_scanB ( & chk_data , k_sector , found , & sectorcnt , & foundkeys ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
chk_data . keyType = 0 ;
2019-03-10 07:00:59 +08:00
chk_data . block = FirstBlockOfSector ( s ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
if ( use_flashmem ) {
if ( lastpos ! = i & & lastpos ! = 0 ) {
if ( i - lastpos < 0xF ) {
2019-03-10 03:34:41 +08:00
s_point = i & 0xFFF0 ;
}
} else {
lastpos = i ;
}
}
}
}
// skip already found B keys
2019-03-10 07:00:59 +08:00
if ( ! found [ ( s * 2 ) + 1 ] ) {
2019-03-10 03:34:41 +08:00
chk_data . keyType = 1 ;
2019-03-10 07:00:59 +08:00
status = chkKey ( & chk_data ) ;
if ( status = = 0 ) {
2019-03-10 03:34:41 +08:00
memcpy ( k_sector [ s ] . keyB , datain + i * 6 , 6 ) ;
2019-03-10 07:00:59 +08:00
found [ ( s * 2 ) + 1 ] = 1 ;
2019-03-10 03:34:41 +08:00
+ + foundkeys ;
chkKey_scanB ( & chk_data , k_sector , found , & sectorcnt , & foundkeys ) ;
2019-03-10 07:00:59 +08:00
if ( use_flashmem ) {
if ( lastpos ! = i & & lastpos ! = 0 ) {
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
if ( i - lastpos < 0xF )
2019-03-10 03:34:41 +08:00
s_point = i & 0xFFF0 ;
} else {
lastpos = i ;
}
}
}
}
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
if ( found [ ( s * 2 ) ] & & found [ ( s * 2 ) + 1 ] )
2019-03-10 03:34:41 +08:00
break ;
} // end keys test loop - depth first
// assume1. if no keys found in first sector, get next keychunk from client
2019-03-10 07:00:59 +08:00
if ( ! use_flashmem & & ( newfound - foundkeys = = 0 ) )
2019-03-10 03:34:41 +08:00
goto OUT ;
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
} // end loop - sector
} // end strategy 1
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
if ( foundkeys = = allkeys )
2019-03-10 03:34:41 +08:00
goto OUT ;
2019-03-10 07:00:59 +08:00
if ( strategy = = 2 | | use_flashmem ) {
2019-03-10 03:34:41 +08:00
// Keychunk loop
for ( uint16_t i = 0 ; i < keyCount ; i + + ) {
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// Allow button press / usb cmd to interrupt device
2019-06-03 06:01:08 +08:00
if ( BUTTON_PRESS ( ) & & ! data_available ( ) ) break ;
2019-03-10 03:34:41 +08:00
// found all keys?
2019-03-10 07:00:59 +08:00
if ( foundkeys = = allkeys )
2019-03-10 03:34:41 +08:00
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 ) {
2019-03-10 07:00:59 +08:00
if ( found [ ( s * 2 ) ] & & found [ ( s * 2 ) + 1 ] ) continue ;
2019-03-10 03:34:41 +08:00
// found all keys?
2019-03-10 07:00:59 +08:00
if ( foundkeys = = allkeys )
2019-03-10 03:34:41 +08:00
goto OUT ;
// assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector
2019-03-10 07:00:59 +08:00
chk_data . block = FirstBlockOfSector ( s ) ;
2019-03-10 03:34:41 +08:00
// skip already found A keys
2019-03-10 07:00:59 +08:00
if ( ! found [ ( s * 2 ) ] ) {
2019-03-10 03:34:41 +08:00
chk_data . keyType = 0 ;
2019-03-10 07:00:59 +08:00
status = chkKey ( & chk_data ) ;
if ( status = = 0 ) {
2019-03-10 03:34:41 +08:00
memcpy ( k_sector [ s ] . keyA , datain + i * 6 , 6 ) ;
2019-03-10 07:00:59 +08:00
found [ ( s * 2 ) ] = 1 ;
2019-03-10 03:34:41 +08:00
+ + foundkeys ;
2019-03-10 07:00:59 +08:00
chkKey_scanA ( & chk_data , k_sector , found , & sectorcnt , & foundkeys ) ;
2019-03-10 03:34:41 +08:00
// read Block B, if A is found.
2019-03-10 07:00:59 +08:00
chkKey_loopBonly ( & chk_data , k_sector , found , & sectorcnt , & foundkeys ) ;
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
chk_data . block = FirstBlockOfSector ( s ) ;
2019-03-10 03:34:41 +08:00
}
}
// skip already found B keys
2019-03-10 07:00:59 +08:00
if ( ! found [ ( s * 2 ) + 1 ] ) {
2019-03-10 03:34:41 +08:00
chk_data . keyType = 1 ;
2019-03-10 07:00:59 +08:00
status = chkKey ( & chk_data ) ;
if ( status = = 0 ) {
2019-03-10 03:34:41 +08:00
memcpy ( k_sector [ s ] . keyB , datain + i * 6 , 6 ) ;
2019-03-10 07:00:59 +08:00
found [ ( s * 2 ) + 1 ] = 1 ;
2019-03-10 03:34:41 +08:00
+ + foundkeys ;
chkKey_scanB ( & chk_data , k_sector , found , & sectorcnt , & foundkeys ) ;
}
}
} // end loop sectors
} // end loop keys
} // end loop strategy 2
2019-03-09 15:49:41 +08:00
OUT :
2019-03-10 03:34:41 +08:00
LEDsoff ( ) ;
crypto1_destroy ( pcs ) ;
// All keys found, send to client, or last keychunk from client
2019-03-10 07:00:59 +08:00
if ( foundkeys = = allkeys | | lastchunk ) {
2019-03-10 03:34:41 +08:00
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 ;
2019-08-01 06:14:09 +08:00
for ( uint8_t m = 64 ; m < ARRAYLEN ( found ) ; m + + ) {
2019-03-10 03:34:41 +08:00
bar | = ( ( uint16_t ) ( found [ m ] & 1 ) < < j + + ) ;
}
2019-03-10 07:00:59 +08:00
uint8_t * tmp = BigBuf_malloc ( 480 + 10 ) ;
memcpy ( tmp , k_sector , sectorcnt * sizeof ( sector_t ) ) ;
num_to_bytes ( foo , 8 , tmp + 480 ) ;
2019-03-10 03:34:41 +08:00
tmp [ 488 ] = bar & 0xFF ;
tmp [ 489 ] = bar > > 8 & 0xFF ;
2019-04-18 18:43:35 +08:00
reply_old ( CMD_ACK , foundkeys , 0 , 0 , tmp , 480 + 10 ) ;
2019-03-10 03:34:41 +08:00
set_tracing ( false ) ;
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
2019-03-10 07:00:59 +08:00
BigBuf_free ( ) ;
BigBuf_Clear_ext ( false ) ;
2019-08-30 16:45:52 +08:00
// 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 ) ) ;
}
int oldbg = DBGLEVEL ;
DBGLEVEL = DBG_NONE ;
MifareECardLoad ( sectorcnt , 0 ) ;
MifareECardLoad ( sectorcnt , 1 ) ;
DBGLEVEL = oldbg ;
}
2019-03-10 03:34:41 +08:00
} else {
// partial/none keys found
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , foundkeys , 0 , 0 , 0 , 0 ) ;
2019-03-10 03:34:41 +08:00
}
2019-03-09 15:49:41 +08:00
}
2019-05-13 18:25:11 +08:00
void MifareChkKeys ( uint8_t * datain ) {
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
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 ;
2019-05-01 18:19:51 +08:00
uint8_t cascade_levels = 0 ;
struct {
uint8_t key [ 6 ] ;
bool found ;
} PACKED keyresult ;
keyresult . found = false ;
2019-04-29 18:12:14 +08:00
uint8_t blockNo , keyType , keyCount ;
bool clearTrace , have_uid = false ;
2019-05-13 18:25:11 +08:00
keyType = datain [ 0 ] ;
blockNo = datain [ 1 ] ;
clearTrace = datain [ 2 ] ;
keyCount = datain [ 3 ] ;
datain + = 4 ;
2019-03-10 03:34:41 +08:00
LEDsoff ( ) ;
LED_A_ON ( ) ;
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
if ( clearTrace )
clear_trace ( ) ;
set_tracing ( true ) ;
for ( i = 0 ; i < keyCount ; 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 ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " ChkKeys: Can't select card (ALL) " ) ;
2019-03-10 03:34:41 +08:00
- - i ; // try same key once again
continue ;
}
switch ( card_info . uidlen ) {
2019-03-10 07:00:59 +08:00
case 4 :
cascade_levels = 1 ;
break ;
case 7 :
cascade_levels = 2 ;
break ;
case 10 :
cascade_levels = 3 ;
break ;
default :
break ;
2019-03-10 03:34:41 +08:00
}
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 ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " ChkKeys: Can't select card (UID) " ) ;
2019-03-10 03:34:41 +08:00
- - 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 ;
2019-05-01 18:19:51 +08:00
memcpy ( keyresult . key , datain + i * 6 , 6 ) ;
keyresult . found = true ;
2019-03-10 03:34:41 +08:00
break ;
}
LED_B_ON ( ) ;
2019-03-09 15:49:41 +08:00
2019-08-04 01:17:00 +08:00
reply_ng ( CMD_HF_MIFARE_CHKKEYS , PM3_SUCCESS , ( uint8_t * ) & keyresult , sizeof ( keyresult ) ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
set_tracing ( false ) ;
crypto1_destroy ( pcs ) ;
2019-03-09 15:49:41 +08:00
}
//-----------------------------------------------------------------------------
// Work with emulator memory
2019-03-09 15:59:13 +08:00
//
2019-03-09 15:49:41 +08:00
// 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.
//-----------------------------------------------------------------------------
2019-05-13 18:30:27 +08:00
void MifareEMemClr ( void ) {
2019-03-10 03:34:41 +08:00
FpgaDownloadAndGo ( FPGA_BITSTREAM_HF ) ;
2019-06-08 03:40:33 +08:00
emlClearMem ( ) ;
2019-03-09 15:49:41 +08:00
}
2019-05-27 19:46:27 +08:00
void MifareEMemSet ( uint8_t blockno , uint8_t blockcnt , uint8_t blockwidth , uint8_t * datain ) {
2019-03-10 03:34:41 +08:00
FpgaDownloadAndGo ( FPGA_BITSTREAM_HF ) ;
2019-05-27 19:46:27 +08:00
if ( blockwidth = = 0 )
blockwidth = 16 ; // backwards compat... default bytewidth
2019-06-08 03:40:33 +08:00
2019-05-27 19:46:27 +08:00
emlSetMem_xt ( datain , blockno , blockcnt , blockwidth ) ; // data, block num, blocks count, block byte width
2019-03-09 15:49:41 +08:00
}
2019-05-27 19:46:27 +08:00
void MifareEMemGet ( uint8_t blockno , uint8_t blockcnt ) {
2019-03-10 03:34:41 +08:00
FpgaDownloadAndGo ( FPGA_BITSTREAM_HF ) ;
2019-06-08 03:40:33 +08:00
//
2019-05-27 19:46:27 +08:00
size_t size = blockcnt * 16 ;
2019-06-08 03:40:33 +08:00
if ( size > PM3_CMD_DATA_SIZE ) {
2019-08-04 01:17:00 +08:00
reply_ng ( CMD_HF_MIFARE_EML_MEMGET , PM3_EMALLOC , NULL , 0 ) ;
2019-05-27 19:46:27 +08:00
return ;
}
2019-06-08 03:40:33 +08:00
2019-05-27 19:46:27 +08:00
uint8_t * buf = BigBuf_malloc ( size ) ;
2019-06-08 03:40:33 +08:00
2019-05-27 19:46:27 +08:00
emlGetMem ( buf , blockno , blockcnt ) ; // data, block num, blocks count (max 4)
2019-03-09 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
LED_B_ON ( ) ;
2019-08-04 01:17:00 +08:00
reply_ng ( CMD_HF_MIFARE_EML_MEMGET , PM3_SUCCESS , buf , size ) ;
2019-03-10 03:34:41 +08:00
LED_B_OFF ( ) ;
2019-05-27 19:46:27 +08:00
BigBuf_free_keep_EM ( ) ;
2019-03-09 15:49:41 +08:00
}
//-----------------------------------------------------------------------------
// Load a card into the emulator memory
2019-03-09 15:59:13 +08:00
//
2019-03-09 15:49:41 +08:00
//-----------------------------------------------------------------------------
2019-08-29 03:23:31 +08:00
int MifareECardLoadExt ( uint8_t numSectors , uint8_t keyType ) {
2019-08-30 16:45:52 +08:00
int retval = MifareECardLoad ( numSectors , keyType ) ;
reply_ng ( CMD_HF_MIFARE_EML_LOAD , retval , NULL , 0 ) ;
return retval ;
2019-08-29 03:23:31 +08:00
}
int MifareECardLoad ( uint8_t numSectors , uint8_t keyType ) {
2019-04-10 02:39:38 +08:00
uint32_t cuid = 0 ;
2019-03-10 03:34:41 +08:00
struct Crypto1State mpcs = { 0 , 0 } ;
struct Crypto1State * pcs ;
pcs = & mpcs ;
// variables
2019-03-21 22:19:18 +08:00
uint8_t dataoutbuf [ 16 ] = { 0x00 } ;
uint8_t dataoutbuf2 [ 16 ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
uint8_t uid [ 10 ] = { 0x00 } ;
LED_A_ON ( ) ;
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
clear_trace ( ) ;
set_tracing ( true ) ;
2019-08-29 03:23:31 +08:00
int retval ;
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ) {
2019-08-29 03:23:31 +08:00
retval = PM3_ESOFT ;
if ( DBGLEVEL > DBG_ERROR ) Dbprintf ( " Can't select card " ) ;
2019-08-30 16:45:52 +08:00
goto out ;
2019-03-10 03:34:41 +08:00
}
2019-08-29 03:23:31 +08:00
for ( uint8_t sectorNo = 0 ; sectorNo < numSectors ; sectorNo + + ) {
2019-06-08 00:41:39 +08:00
uint64_t ui64Key = emlGetKey ( sectorNo , keyType ) ;
2019-03-10 07:00:59 +08:00
if ( sectorNo = = 0 ) {
2019-08-29 03:23:31 +08:00
if ( mifare_classic_auth ( pcs , cuid , FirstBlockOfSector ( sectorNo ) , keyType , ui64Key , AUTH_FIRST ) ) {
if ( DBGLEVEL > DBG_ERROR ) Dbprintf ( " Sector[%2d]. Auth error " , sectorNo ) ;
2019-03-10 03:34:41 +08:00
break ;
}
} else {
2019-08-29 03:23:31 +08:00
if ( mifare_classic_auth ( pcs , cuid , FirstBlockOfSector ( sectorNo ) , keyType , ui64Key , AUTH_NESTED ) ) {
retval = PM3_ESOFT ;
if ( DBGLEVEL > DBG_ERROR ) Dbprintf ( " Sector[%2d]. Auth nested error " , sectorNo ) ;
goto out ;
2019-03-10 03:34:41 +08:00
}
}
2019-08-29 03:23:31 +08:00
for ( uint8_t blockNo = 0 ; blockNo < NumBlocksPerSector ( sectorNo ) ; blockNo + + ) {
if ( mifare_classic_readblock ( pcs , cuid , FirstBlockOfSector ( sectorNo ) + blockNo , dataoutbuf ) ) {
retval = PM3_ESOFT ;
if ( DBGLEVEL > DBG_ERROR ) Dbprintf ( " Error reading sector %2d block %2d " , sectorNo , blockNo ) ;
2019-03-10 03:34:41 +08:00
break ;
}
2019-08-30 16:45:52 +08:00
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 ) ;
2019-03-10 03:34:41 +08:00
}
}
2019-08-30 16:45:52 +08:00
}
2019-03-10 03:34:41 +08:00
2019-08-29 03:23:31 +08:00
if ( mifare_classic_halt ( pcs , cuid ) ) {
if ( DBGLEVEL > DBG_ERROR )
2019-03-10 03:34:41 +08:00
Dbprintf ( " Halt error " ) ;
2019-08-30 16:45:52 +08:00
}
2019-03-10 03:34:41 +08:00
2019-08-30 16:45:52 +08:00
if ( DBGLEVEL > = DBG_INFO ) DbpString ( " Emulator fill sectors finished " ) ;
2019-03-10 03:34:41 +08:00
2019-08-29 03:23:31 +08:00
out :
crypto1_destroy ( pcs ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-08-29 03:23:31 +08:00
return retval ;
2019-03-09 15:49:41 +08:00
}
//-----------------------------------------------------------------------------
// Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn)
2019-03-09 15:59:13 +08:00
//
2019-03-09 15:49:41 +08:00
// 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
2019-03-09 15:59:13 +08:00
uint8_t wupC1 [ ] = { MIFARE_MAGICWUPC1 } ;
uint8_t wupC2 [ ] = { MIFARE_MAGICWUPC2 } ;
uint8_t wipeC [ ] = { MIFARE_MAGICWIPEC } ;
2019-03-10 18:20:22 +08:00
void MifareCSetBlock ( uint32_t arg0 , uint32_t arg1 , uint8_t * datain ) {
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// 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 ) {
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
errormsg = MAGIC_UID ;
}
mifare_classic_halt_ex ( NULL ) ;
break ;
}
// wipe tag, fill it with zeros
2019-03-10 07:00:59 +08:00
if ( workFlags & MAGIC_WIPE ) {
2019-03-10 03:34:41 +08:00
ReaderTransmitBitsPar ( wupC1 , 7 , NULL , NULL ) ;
2019-03-10 07:00:59 +08:00
if ( ! ReaderReceive ( receivedAnswer , receivedAnswerPar ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " wupC1 error " ) ;
2019-03-10 03:34:41 +08:00
errormsg = MAGIC_WIPE ;
break ;
}
ReaderTransmit ( wipeC , sizeof ( wipeC ) , NULL ) ;
2019-03-10 07:00:59 +08:00
if ( ! ReaderReceive ( receivedAnswer , receivedAnswerPar ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " wipeC error " ) ;
2019-03-10 03:34:41 +08:00
errormsg = MAGIC_WIPE ;
break ;
}
mifare_classic_halt_ex ( NULL ) ;
}
// write block
if ( workFlags & MAGIC_WUPC ) {
ReaderTransmitBitsPar ( wupC1 , 7 , NULL , NULL ) ;
2019-03-10 07:00:59 +08:00
if ( ! ReaderReceive ( receivedAnswer , receivedAnswerPar ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " wupC1 error " ) ;
2019-03-10 03:34:41 +08:00
errormsg = MAGIC_WUPC ;
break ;
}
2019-03-10 07:00:59 +08:00
if ( ! is1b ) {
2019-03-10 03:34:41 +08:00
ReaderTransmit ( wupC2 , sizeof ( wupC2 ) , NULL ) ;
2019-03-10 07:00:59 +08:00
if ( ! ReaderReceive ( receivedAnswer , receivedAnswerPar ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_INFO ) Dbprintf ( " Assuming Magic Gen 1B tag. [wupC2 failed] " ) ;
2019-03-10 03:34:41 +08:00
is1b = true ;
continue ;
}
}
}
if ( ( mifare_sendcmd_short ( NULL , 0 , ISO14443A_CMD_WRITEBLOCK , blockNo , receivedAnswer , receivedAnswerPar , NULL ) ! = 1 ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " write block send command error " ) ;
2019-03-10 03:34:41 +08:00
errormsg = 4 ;
break ;
}
memcpy ( data , datain , 16 ) ;
AddCrc14A ( data , 16 ) ;
ReaderTransmit ( data , sizeof ( data ) , NULL ) ;
if ( ( ReaderReceive ( receivedAnswer , receivedAnswerPar ) ! = 1 ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " write block send data error " ) ;
2019-03-10 03:34:41 +08:00
errormsg = 0 ;
break ;
}
if ( workFlags & MAGIC_HALT )
mifare_classic_halt_ex ( NULL ) ;
isOK = true ;
break ;
} // end while
2019-03-10 07:00:59 +08:00
if ( isOK )
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , 1 , 0 , 0 , uid , sizeof ( uid ) ) ;
2019-03-10 03:34:41 +08:00
else
OnErrorMagic ( errormsg ) ;
if ( workFlags & MAGIC_OFF )
OnSuccessMagic ( ) ;
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
void MifareCGetBlock ( uint32_t arg0 , uint32_t arg1 , uint8_t * datain ) {
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
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 ) ;
2019-03-10 07:00:59 +08:00
if ( ! ReaderReceive ( receivedAnswer , receivedAnswerPar ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " wupC1 error " ) ;
2019-03-10 03:34:41 +08:00
errormsg = MAGIC_WUPC ;
break ;
}
2019-03-10 07:00:59 +08:00
if ( ! is1b ) {
2019-03-10 03:34:41 +08:00
ReaderTransmit ( wupC2 , sizeof ( wupC2 ) , NULL ) ;
2019-03-10 07:00:59 +08:00
if ( ! ReaderReceive ( receivedAnswer , receivedAnswerPar ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_INFO ) Dbprintf ( " Assuming Magic Gen 1B tag. [wupC2 failed] " ) ;
2019-03-10 03:34:41 +08:00
is1b = true ;
continue ;
}
}
}
// read block
if ( ( mifare_sendcmd_short ( NULL , 0 , ISO14443A_CMD_READBLOCK , blockNo , receivedAnswer , receivedAnswerPar , NULL ) ! = 18 ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " read block send command error " ) ;
2019-03-10 03:34:41 +08:00
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 )
2019-04-18 18:43:35 +08:00
reply_old ( CMD_ACK , 1 , 0 , 0 , data , sizeof ( data ) ) ;
2019-03-10 03:34:41 +08:00
else
OnErrorMagic ( errormsg ) ;
}
if ( workFlags & MAGIC_OFF )
OnSuccessMagic ( ) ;
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
void MifareCIdent ( ) {
2019-03-10 07:00:59 +08:00
# define GEN_1A 1
# define GEN_1B 2
# define GEN_2 4
2019-08-20 20:53:52 +08:00
# define GEN_UNFUSED 5
2019-03-10 03:34:41 +08:00
// variables
uint8_t isGen = 0 ;
uint8_t rec [ 1 ] = { 0x00 } ;
uint8_t recpar [ 1 ] = { 0x00 } ;
2019-03-21 19:53:05 +08:00
uint8_t rats [ 4 ] = { ISO14443A_CMD_RATS , 0x80 , 0x31 , 0x73 } ;
uint8_t * par = BigBuf_malloc ( MAX_PARITY_SIZE ) ;
2019-05-01 03:10:11 +08:00
uint8_t * buf = BigBuf_malloc ( PM3_CMD_DATA_SIZE ) ;
2019-03-21 19:53:05 +08:00
uint8_t * uid = BigBuf_malloc ( 10 ) ;
uint32_t cuid = 0 ;
2019-05-15 19:03:19 +08:00
uint8_t data [ 1 ] = { 0x00 } ;
2019-03-28 21:19:41 +08:00
2019-03-10 03:34:41 +08:00
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
// Generation 1 test
ReaderTransmitBitsPar ( wupC1 , 7 , NULL , NULL ) ;
2019-03-10 07:00:59 +08:00
if ( ! ReaderReceive ( rec , recpar ) | | ( rec [ 0 ] ! = 0x0a ) ) {
2019-03-28 21:19:41 +08:00
goto TEST2 ;
2019-03-10 03:34:41 +08:00
} ;
ReaderTransmit ( wupC2 , sizeof ( wupC2 ) , NULL ) ;
2019-03-10 07:00:59 +08:00
if ( ! ReaderReceive ( rec , recpar ) | | ( rec [ 0 ] ! = 0x0a ) ) {
2019-06-12 21:41:23 +08:00
isGen = GEN_1B ;
2019-03-10 03:34:41 +08:00
goto OUT ;
} ;
isGen = GEN_1A ;
goto OUT ;
2019-03-09 15:49:41 +08:00
2019-03-10 07:00:59 +08:00
TEST2 :
2019-03-21 19:53:05 +08:00
// reset card
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
SpinDelay ( 100 ) ;
iso14443a_setup ( FPGA_HF_ISO14443A_READER_LISTEN ) ;
2019-03-28 21:19:41 +08:00
2019-03-21 19:53:05 +08:00
int res = iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ;
2019-03-28 21:19:41 +08:00
if ( res = = 2 ) {
2019-08-27 04:28:39 +08:00
if ( cuid = = 0xAA55C396 ) {
isGen = GEN_UNFUSED ;
goto OUT ;
2019-08-20 20:53:52 +08:00
}
2019-03-28 21:19:41 +08:00
ReaderTransmit ( rats , sizeof ( rats ) , NULL ) ;
2019-03-21 19:53:05 +08:00
res = ReaderReceive ( buf , par ) ;
if ( memcmp ( buf , " \x09 \x78 \x00 \x91 \x02 \xDA \xBC \x19 \x10 \xF0 \x05 " , 11 ) = = 0 ) {
isGen = GEN_2 ;
2019-03-10 07:00:59 +08:00
goto OUT ;
2019-03-21 19:53:05 +08:00
}
if ( memcmp ( buf , " \x0D \x78 \x00 \x71 \x02 \x88 \x49 \xA1 \x30 \x20 \x15 \x06 \x08 \x56 \x3D " , 15 ) = = 0 ) {
2019-03-10 07:00:59 +08:00
isGen = GEN_2 ;
2019-03-21 19:53:05 +08:00
}
} ;
2019-03-28 21:19:41 +08:00
2019-03-10 07:00:59 +08:00
OUT :
2019-06-08 03:40:33 +08:00
2019-05-15 19:03:19 +08:00
data [ 0 ] = isGen ;
2019-08-04 01:17:00 +08:00
reply_ng ( CMD_HF_MIFARE_CIDENT , PM3_SUCCESS , data , sizeof ( data ) ) ;
2019-03-10 03:34:41 +08:00
// turns off
OnSuccessMagic ( ) ;
2019-03-21 19:53:05 +08:00
BigBuf_free ( ) ;
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
void OnSuccessMagic ( ) {
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
void OnErrorMagic ( uint8_t reason ) {
2019-03-10 03:34:41 +08:00
// ACK, ISOK, reason,0,0,0
2019-05-15 19:47:46 +08:00
reply_mix ( CMD_ACK , 0 , reason , 0 , 0 , 0 ) ;
2019-03-10 03:34:41 +08:00
OnSuccessMagic ( ) ;
2019-03-09 15:49:41 +08:00
}
2019-05-15 18:52:22 +08:00
void MifareSetMod ( uint8_t * datain ) {
uint8_t mod = datain [ 0 ] ;
uint64_t ui64Key = bytes_to_num ( datain + 1 , 6 ) ;
2019-03-10 03:34:41 +08:00
// variables
2019-05-22 17:58:48 +08:00
uint16_t isOK = PM3_EUNDEF ;
2019-03-10 03:34:41 +08:00
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 ) {
2019-03-10 07:00:59 +08:00
if ( ! iso14443a_select_card ( uid , NULL , & cuid , true , 0 , true ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
break ;
}
2019-03-10 07:00:59 +08:00
if ( mifare_classic_auth ( pcs , cuid , 0 , 0 , ui64Key , AUTH_FIRST ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Auth error " ) ;
2019-03-10 03:34:41 +08:00
break ;
}
2019-06-08 00:41:39 +08:00
int respLen ;
2019-03-10 03:34:41 +08:00
if ( ( ( respLen = mifare_sendcmd_short ( pcs , 1 , 0x43 , mod , receivedAnswer , receivedAnswerPar , NULL ) ) ! = 1 ) | | ( receivedAnswer [ 0 ] ! = 0x0a ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " SetMod error; response[0]: %hhX, len: %d " , receivedAnswer [ 0 ] , respLen ) ;
2019-03-10 03:34:41 +08:00
break ;
}
2019-03-10 07:00:59 +08:00
if ( mifare_classic_halt ( pcs , cuid ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = 1 ) Dbprintf ( " Halt error " ) ;
2019-03-10 03:34:41 +08:00
break ;
}
2019-05-15 18:52:22 +08:00
isOK = PM3_SUCCESS ;
2019-03-10 03:34:41 +08:00
break ;
}
crypto1_destroy ( pcs ) ;
LED_B_ON ( ) ;
2019-08-04 01:17:00 +08:00
reply_ng ( CMD_HF_MIFARE_SETMOD , isOK , NULL , 0 ) ;
2019-05-15 18:52:22 +08:00
2019-03-10 03:34:41 +08:00
LED_B_OFF ( ) ;
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
2019-03-09 15:49:41 +08:00
}
//
// DESFIRE
//
2019-03-10 18:20:22 +08:00
void Mifare_DES_Auth1 ( uint8_t arg0 , uint8_t * datain ) {
2019-03-21 22:19:18 +08:00
uint8_t dataout [ 12 ] = { 0x00 } ;
2019-03-10 03:34:41 +08:00
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 ) ;
2019-03-10 07:00:59 +08:00
if ( ! len ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Can't select card " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 1 ) ;
return ;
} ;
2019-03-10 07:00:59 +08:00
if ( mifare_desfire_des_auth1 ( cuid , dataout ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " Authentication part1: Fail. " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 4 ) ;
return ;
}
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) DbpString ( " AUTH 1 FINISHED " ) ;
2019-05-29 01:20:56 +08:00
reply_mix ( CMD_ACK , 1 , cuid , 0 , dataout , sizeof ( dataout ) ) ;
2019-03-09 15:49:41 +08:00
}
2019-03-10 18:20:22 +08:00
void Mifare_DES_Auth2 ( uint32_t arg0 , uint8_t * datain ) {
2019-03-10 03:34:41 +08:00
uint32_t cuid = arg0 ;
uint8_t key [ 16 ] = { 0x00 } ;
2019-03-21 22:19:18 +08:00
uint8_t dataout [ 12 ] = { 0x00 } ;
uint8_t isOK = 0 ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
memcpy ( key , datain , 16 ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
isOK = mifare_desfire_des_auth2 ( cuid , key , dataout ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
if ( isOK ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " Authentication part2: Failed " ) ;
2019-03-10 03:34:41 +08:00
OnError ( 4 ) ;
return ;
}
2019-03-09 15:49:41 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) DbpString ( " AUTH 2 FINISHED " ) ;
2019-03-09 15:49:41 +08:00
2019-04-18 18:43:35 +08:00
reply_old ( CMD_ACK , isOK , 0 , 0 , dataout , sizeof ( dataout ) ) ;
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-03-12 07:12:26 +08:00
}