2019-04-10 04:07:17 +08:00
//-----------------------------------------------------------------------------
2019-03-16 04:04:25 +08:00
// Merlok - June 2011, 2012
// Gerhard de Koning Gans - May 2008
// Hagen Fritsch - June 2010
//
// 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.
//-----------------------------------------------------------------------------
// Mifare Classic Card Simulation
//-----------------------------------------------------------------------------
2019-04-06 06:39:27 +08:00
// Verbose Mode:
2019-06-06 16:05:09 +08:00
// DBG_NONE 0
// DBG_ERROR 1
// DBG_INFO 2
// DBG_DEBUG 3
// DBG_EXTENDED 4
2019-04-10 14:42:00 +08:00
2019-04-10 15:32:49 +08:00
// /!\ Printing Debug message is disrupting emulation,
// Only use with caution during debugging
2019-04-09 06:07:06 +08:00
2019-08-08 22:57:33 +08:00
# include "mifaresim.h"
# include <inttypes.h>
2019-04-06 06:32:11 +08:00
2019-03-16 04:04:25 +08:00
# include "iso14443a.h"
# include "BigBuf.h"
# include "string.h"
# include "mifareutil.h"
# include "fpgaloader.h"
2019-08-08 22:57:33 +08:00
# include "proxmark3_arm.h"
2019-03-16 04:04:25 +08:00
# include "cmd.h"
# include "protocols.h"
2019-08-08 22:57:33 +08:00
# include "appmain.h"
# include "util.h"
# include "commonutil.h"
# include "crc16.h"
# include "dbprint.h"
# include "ticks.h"
2019-03-16 04:04:25 +08:00
static bool IsTrailerAccessAllowed ( uint8_t blockNo , uint8_t keytype , uint8_t action ) {
uint8_t sector_trailer [ 16 ] ;
emlGetMem ( sector_trailer , blockNo , 1 ) ;
uint8_t AC = ( ( sector_trailer [ 7 ] > > 5 ) & 0x04 )
| ( ( sector_trailer [ 8 ] > > 2 ) & 0x02 )
| ( ( sector_trailer [ 8 ] > > 7 ) & 0x01 ) ;
switch ( action ) {
case AC_KEYA_READ : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsTrailerAccessAllowed: AC_KEYA_READ " ) ;
2019-03-16 06:14:19 +08:00
return false ;
2019-03-16 04:04:25 +08:00
}
case AC_KEYA_WRITE : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsTrailerAccessAllowed: AC_KEYA_WRITE " ) ;
2019-03-16 04:04:25 +08:00
return ( ( keytype = = AUTHKEYA & & ( AC = = 0x00 | | AC = = 0x01 ) )
2019-03-27 21:18:26 +08:00
| | ( keytype = = AUTHKEYB & & ( AC = = 0x04 | | AC = = 0x03 ) ) ) ;
2019-03-16 04:04:25 +08:00
}
case AC_KEYB_READ : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsTrailerAccessAllowed: AC_KEYB_READ " ) ;
2019-03-16 06:14:19 +08:00
return ( keytype = = AUTHKEYA & & ( AC = = 0x00 | | AC = = 0x02 | | AC = = 0x01 ) ) ;
2019-03-16 04:04:25 +08:00
}
case AC_KEYB_WRITE : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsTrailerAccessAllowed: AC_KEYB_WRITE " ) ;
2019-07-19 21:27:08 +08:00
return ( ( keytype = = AUTHKEYA & & ( AC = = 0x00 | | AC = = 0x01 ) )
2019-03-27 21:18:26 +08:00
| | ( keytype = = AUTHKEYB & & ( AC = = 0x04 | | AC = = 0x03 ) ) ) ;
2019-03-16 04:04:25 +08:00
}
case AC_AC_READ : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsTrailerAccessAllowed: AC_AC_READ " ) ;
2019-03-16 04:04:25 +08:00
return ( ( keytype = = AUTHKEYA )
2019-03-27 21:18:26 +08:00
| | ( keytype = = AUTHKEYB & & ! ( AC = = 0x00 | | AC = = 0x02 | | AC = = 0x01 ) ) ) ;
2019-03-16 04:04:25 +08:00
}
case AC_AC_WRITE : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsTrailerAccessAllowed: AC_AC_WRITE " ) ;
2019-03-16 04:04:25 +08:00
return ( ( keytype = = AUTHKEYA & & ( AC = = 0x01 ) )
2019-03-27 21:18:26 +08:00
| | ( keytype = = AUTHKEYB & & ( AC = = 0x03 | | AC = = 0x05 ) ) ) ;
2019-03-16 04:04:25 +08:00
}
default :
return false ;
}
}
static bool IsDataAccessAllowed ( uint8_t blockNo , uint8_t keytype , uint8_t action ) {
uint8_t sector_trailer [ 16 ] ;
emlGetMem ( sector_trailer , SectorTrailer ( blockNo ) , 1 ) ;
2019-03-27 21:18:26 +08:00
2019-03-16 04:04:25 +08:00
uint8_t sector_block ;
2019-03-18 20:36:36 +08:00
if ( blockNo < = MIFARE_2K_MAXBLOCK ) {
2019-03-16 04:04:25 +08:00
sector_block = blockNo & 0x03 ;
} else {
sector_block = ( blockNo & 0x0f ) / 5 ;
}
uint8_t AC ;
switch ( sector_block ) {
case 0x00 : {
AC = ( ( sector_trailer [ 7 ] > > 2 ) & 0x04 )
2019-03-27 21:18:26 +08:00
| ( ( sector_trailer [ 8 ] < < 1 ) & 0x02 )
| ( ( sector_trailer [ 8 ] > > 4 ) & 0x01 ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsDataAccessAllowed: case 0x00 - %02x " , AC ) ;
2019-03-16 04:04:25 +08:00
break ;
}
case 0x01 : {
AC = ( ( sector_trailer [ 7 ] > > 3 ) & 0x04 )
2019-03-27 21:18:26 +08:00
| ( ( sector_trailer [ 8 ] > > 0 ) & 0x02 )
| ( ( sector_trailer [ 8 ] > > 5 ) & 0x01 ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsDataAccessAllowed: case 0x01 - %02x " , AC ) ;
2019-03-16 04:04:25 +08:00
break ;
}
case 0x02 : {
AC = ( ( sector_trailer [ 7 ] > > 4 ) & 0x04 )
2019-03-27 21:18:26 +08:00
| ( ( sector_trailer [ 8 ] > > 1 ) & 0x02 )
| ( ( sector_trailer [ 8 ] > > 6 ) & 0x01 ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsDataAccessAllowed: case 0x02 - %02x " , AC ) ;
2019-03-16 04:04:25 +08:00
break ;
}
default :
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsDataAccessAllowed: Error " ) ;
2019-03-16 04:04:25 +08:00
return false ;
}
switch ( action ) {
case AC_DATA_READ : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsDataAccessAllowed - AC_DATA_READ: OK " ) ;
2019-03-16 04:04:25 +08:00
return ( ( keytype = = AUTHKEYA & & ! ( AC = = 0x03 | | AC = = 0x05 | | AC = = 0x07 ) )
| | ( keytype = = AUTHKEYB & & ! ( AC = = 0x07 ) ) ) ;
}
case AC_DATA_WRITE : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsDataAccessAllowed - AC_DATA_WRITE: OK " ) ;
2019-03-16 04:04:25 +08:00
return ( ( keytype = = AUTHKEYA & & ( AC = = 0x00 ) )
| | ( keytype = = AUTHKEYB & & ( AC = = 0x00 | | AC = = 0x04 | | AC = = 0x06 | | AC = = 0x03 ) ) ) ;
}
case AC_DATA_INC : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " IsDataAccessAllowed - AC_DATA_INC: OK " ) ;
2019-03-16 04:04:25 +08:00
return ( ( keytype = = AUTHKEYA & & ( AC = = 0x00 ) )
| | ( keytype = = AUTHKEYB & & ( AC = = 0x00 | | AC = = 0x06 ) ) ) ;
}
case AC_DATA_DEC_TRANS_REST : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " AC_DATA_DEC_TRANS_REST: OK " ) ;
2019-03-16 04:04:25 +08:00
return ( ( keytype = = AUTHKEYA & & ( AC = = 0x00 | | AC = = 0x06 | | AC = = 0x01 ) )
| | ( keytype = = AUTHKEYB & & ( AC = = 0x00 | | AC = = 0x06 | | AC = = 0x01 ) ) ) ;
}
}
return false ;
}
static bool IsAccessAllowed ( uint8_t blockNo , uint8_t keytype , uint8_t action ) {
if ( IsSectorTrailer ( blockNo ) ) {
return IsTrailerAccessAllowed ( blockNo , keytype , action ) ;
} else {
return IsDataAccessAllowed ( blockNo , keytype , action ) ;
}
}
2019-08-07 07:32:37 +08:00
static bool MifareSimInit ( uint16_t flags , uint8_t * datain , uint16_t atqa , uint8_t sak , tag_response_info_t * * responses , uint32_t * cuid , uint8_t * uid_len , uint8_t * * rats , uint8_t * rats_len ) {
2019-03-16 04:04:25 +08:00
// SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf
// ATQA
2019-04-17 04:52:05 +08:00
static uint8_t rATQA_Mini [ ] = { 0x04 , 0x00 } ; // indicate Mifare classic Mini 4Byte UID
static uint8_t rATQA_1k [ ] = { 0x04 , 0x00 } ; // indicate Mifare classic 1k 4Byte UID
static uint8_t rATQA_2k [ ] = { 0x04 , 0x00 } ; // indicate Mifare classic 2k 4Byte UID
static uint8_t rATQA_4k [ ] = { 0x02 , 0x00 } ; // indicate Mifare classic 4k 4Byte UID
2019-03-16 04:04:25 +08:00
2019-04-17 04:52:05 +08:00
// SAK
static uint8_t rSAK_Mini = 0x09 ; // mifare Mini
2019-07-24 06:52:24 +08:00
static uint8_t rSAK_1k = 0x08 ; // mifare 1k
static uint8_t rSAK_2k = 0x08 ; // mifare 2k with RATS support
static uint8_t rSAK_4k = 0x18 ; // mifare 4k
static uint8_t rUIDBCC1 [ ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ; // UID 1st cascade level
static uint8_t rUIDBCC1b4 [ ] = { 0x00 , 0x00 , 0x00 , 0x00 } ; // UID 1st cascade level, last 4 bytes
static uint8_t rUIDBCC1b3 [ ] = { 0x00 , 0x00 , 0x00 } ; // UID 1st cascade level, last 3 bytes
static uint8_t rUIDBCC1b2 [ ] = { 0x00 , 0x00 } ; // UID 1st cascade level, last 2 bytes
static uint8_t rUIDBCC1b1 [ ] = { 0x00 } ; // UID 1st cascade level, last byte
static uint8_t rUIDBCC2 [ ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ; // UID 2nd cascade level
static uint8_t rUIDBCC2b4 [ ] = { 0x00 , 0x00 , 0x00 , 0x00 } ; // UID 2st cascade level, last 4 bytes
static uint8_t rUIDBCC2b3 [ ] = { 0x00 , 0x00 , 0x00 } ; // UID 2st cascade level, last 3 bytes
static uint8_t rUIDBCC2b2 [ ] = { 0x00 , 0x00 } ; // UID 2st cascade level, last 2 bytes
static uint8_t rUIDBCC2b1 [ ] = { 0x00 } ; // UID 2st cascade level, last byte
static uint8_t rUIDBCC3 [ ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ; // UID 3nd cascade level
static uint8_t rUIDBCC3b4 [ ] = { 0x00 , 0x00 , 0x00 , 0x00 } ; // UID 3st cascade level, last 4 bytes
static uint8_t rUIDBCC3b3 [ ] = { 0x00 , 0x00 , 0x00 } ; // UID 3st cascade level, last 3 bytes
static uint8_t rUIDBCC3b2 [ ] = { 0x00 , 0x00 } ; // UID 3st cascade level, last 2 bytes
static uint8_t rUIDBCC3b1 [ ] = { 0x00 } ; // UID 3st cascade level, last byte
2019-03-16 04:04:25 +08:00
2019-04-17 04:52:05 +08:00
static uint8_t rATQA [ ] = { 0x00 , 0x00 } ; // Current ATQA
static uint8_t rSAK [ ] = { 0x00 , 0x00 , 0x00 } ; // Current SAK, CRC
static uint8_t rSAKuid [ ] = { 0x04 , 0xda , 0x17 } ; // UID incomplete cascade bit, CRC
2019-03-16 04:04:25 +08:00
2019-04-17 20:54:42 +08:00
// RATS answer for 2K NXP mifare classic (with CRC)
2019-04-17 04:52:05 +08:00
static uint8_t rRATS [ ] = { 0x0c , 0x75 , 0x77 , 0x80 , 0x02 , 0xc1 , 0x05 , 0x2f , 0x2f , 0x01 , 0xbc , 0xd6 , 0x60 , 0xd3 } ;
2019-04-17 20:54:42 +08:00
2019-03-16 04:04:25 +08:00
* uid_len = 0 ;
2019-04-17 04:52:05 +08:00
// By default use 1K tag
memcpy ( rATQA , rATQA_1k , sizeof ( rATQA ) ) ;
rSAK [ 0 ] = rSAK_1k ;
//by default RATS not supported
* rats_len = 0 ;
* rats = NULL ;
2019-04-17 20:54:42 +08:00
2019-03-16 04:04:25 +08:00
// -- Determine the UID
// Can be set from emulator memory or incoming data
// Length: 4,7,or 10 bytes
2019-04-17 04:52:05 +08:00
// Get UID, SAK, ATQA from EMUL
2019-03-16 04:04:25 +08:00
if ( ( flags & FLAG_UID_IN_EMUL ) = = FLAG_UID_IN_EMUL ) {
2019-04-17 04:52:05 +08:00
uint8_t block0 [ 16 ] ;
emlGetMemBt ( block0 , 0 , 16 ) ;
// If uid size defined, copy only uid from EMUL to use, backward compatibility for 'hf_colin.c', 'hf_mattyrun.c'
if ( ( flags & ( FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA ) ) ! = 0 ) {
memcpy ( datain , block0 , 10 ) ; // load 10bytes from EMUL to the datain pointer. to be used below.
2019-04-17 20:54:42 +08:00
} else {
2019-04-17 04:52:05 +08:00
// Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA
if ( ( block0 [ 0 ] ^ block0 [ 1 ] ^ block0 [ 2 ] ^ block0 [ 3 ] ) = = block0 [ 4 ] & & ( block0 [ 6 ] & 0xc0 ) = = 0 ) {
flags | = FLAG_4B_UID_IN_DATA ;
2019-04-17 20:54:42 +08:00
memcpy ( datain , block0 , 4 ) ;
2019-04-17 04:52:05 +08:00
rSAK [ 0 ] = block0 [ 5 ] ;
memcpy ( rATQA , & block0 [ 6 ] , sizeof ( rATQA ) ) ;
}
// Check for 7 bytes UID: double size uid bits in ATQA
else if ( ( block0 [ 8 ] & 0xc0 ) = = 0x40 ) {
flags | = FLAG_7B_UID_IN_DATA ;
2019-04-17 20:54:42 +08:00
memcpy ( datain , block0 , 7 ) ;
2019-04-17 04:52:05 +08:00
rSAK [ 0 ] = block0 [ 7 ] ;
memcpy ( rATQA , & block0 [ 8 ] , sizeof ( rATQA ) ) ;
2019-04-17 20:54:42 +08:00
} else {
2020-09-07 03:40:06 +08:00
Dbprintf ( " ERROR: " _RED_ ( " Invalid dump. UID/SAK/ATQA not found " ) ) ;
2019-04-17 04:52:05 +08:00
return false ;
}
}
}
// Tune tag type, if defined directly
// Otherwise use defined by default or extracted from EMUL
if ( ( flags & FLAG_MF_MINI ) = = FLAG_MF_MINI ) {
memcpy ( rATQA , rATQA_Mini , sizeof ( rATQA ) ) ;
rSAK [ 0 ] = rSAK_Mini ;
2019-08-07 06:47:32 +08:00
if ( DBGLEVEL > DBG_NONE ) Dbprintf ( " Enforcing Mifare Mini ATQA/SAK " ) ;
2019-04-17 20:54:42 +08:00
} else if ( ( flags & FLAG_MF_1K ) = = FLAG_MF_1K ) {
2019-04-17 04:52:05 +08:00
memcpy ( rATQA , rATQA_1k , sizeof ( rATQA ) ) ;
rSAK [ 0 ] = rSAK_1k ;
2019-08-07 06:47:32 +08:00
if ( DBGLEVEL > DBG_NONE ) Dbprintf ( " Enforcing Mifare 1K ATQA/SAK " ) ;
2019-04-17 20:54:42 +08:00
} else if ( ( flags & FLAG_MF_2K ) = = FLAG_MF_2K ) {
2019-04-17 04:52:05 +08:00
memcpy ( rATQA , rATQA_2k , sizeof ( rATQA ) ) ;
rSAK [ 0 ] = rSAK_2k ;
* rats = rRATS ;
* rats_len = sizeof ( rRATS ) ;
2019-08-07 06:47:32 +08:00
if ( DBGLEVEL > DBG_NONE ) Dbprintf ( " Enforcing Mifare 2K ATQA/SAK with RATS support " ) ;
2019-04-17 20:54:42 +08:00
} else if ( ( flags & FLAG_MF_4K ) = = FLAG_MF_4K ) {
2019-04-17 04:52:05 +08:00
memcpy ( rATQA , rATQA_4k , sizeof ( rATQA ) ) ;
rSAK [ 0 ] = rSAK_4k ;
2019-08-07 06:47:32 +08:00
if ( DBGLEVEL > DBG_NONE ) Dbprintf ( " Enforcing Mifare 4K ATQA/SAK " ) ;
2019-03-16 04:04:25 +08:00
}
2019-04-17 04:52:05 +08:00
// Prepare UID arrays
2019-07-24 06:52:24 +08:00
if ( ( flags & FLAG_4B_UID_IN_DATA ) = = FLAG_4B_UID_IN_DATA ) { // get UID from datain
2019-03-16 04:04:25 +08:00
memcpy ( rUIDBCC1 , datain , 4 ) ;
* uid_len = 4 ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " MifareSimInit - FLAG_4B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X " , FLAG_4B_UID_IN_DATA , flags , rUIDBCC1 ) ;
2019-04-17 04:52:05 +08:00
// save CUID
* cuid = bytes_to_num ( rUIDBCC1 , 4 ) ;
// BCC
rUIDBCC1 [ 4 ] = rUIDBCC1 [ 0 ] ^ rUIDBCC1 [ 1 ] ^ rUIDBCC1 [ 2 ] ^ rUIDBCC1 [ 3 ] ;
2019-08-07 06:47:32 +08:00
if ( DBGLEVEL > DBG_NONE ) {
2019-04-17 04:52:05 +08:00
Dbprintf ( " 4B UID: %02x%02x%02x%02x " , rUIDBCC1 [ 0 ] , rUIDBCC1 [ 1 ] , rUIDBCC1 [ 2 ] , rUIDBCC1 [ 3 ] ) ;
}
// Correct uid size bits in ATQA
rATQA [ 0 ] = ( rATQA [ 0 ] & 0x3f ) | 0x00 ; // single size uid
2019-03-16 04:04:25 +08:00
} else if ( ( flags & FLAG_7B_UID_IN_DATA ) = = FLAG_7B_UID_IN_DATA ) {
memcpy ( & rUIDBCC1 [ 1 ] , datain , 3 ) ;
memcpy ( rUIDBCC2 , datain + 3 , 4 ) ;
* uid_len = 7 ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " MifareSimInit - FLAG_7B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X " , FLAG_7B_UID_IN_DATA , flags , rUIDBCC1 ) ;
2019-04-17 04:52:05 +08:00
// save CUID
* cuid = bytes_to_num ( rUIDBCC2 , 4 ) ;
// CascadeTag, CT
rUIDBCC1 [ 0 ] = MIFARE_SELECT_CT ;
// BCC
rUIDBCC1 [ 4 ] = rUIDBCC1 [ 0 ] ^ rUIDBCC1 [ 1 ] ^ rUIDBCC1 [ 2 ] ^ rUIDBCC1 [ 3 ] ;
rUIDBCC2 [ 4 ] = rUIDBCC2 [ 0 ] ^ rUIDBCC2 [ 1 ] ^ rUIDBCC2 [ 2 ] ^ rUIDBCC2 [ 3 ] ;
2019-08-07 06:47:32 +08:00
if ( DBGLEVEL > DBG_NONE ) {
2019-04-17 04:52:05 +08:00
Dbprintf ( " 7B UID: %02x %02x %02x %02x %02x %02x %02x " ,
rUIDBCC1 [ 1 ] , rUIDBCC1 [ 2 ] , rUIDBCC1 [ 3 ] , rUIDBCC2 [ 0 ] , rUIDBCC2 [ 1 ] , rUIDBCC2 [ 2 ] , rUIDBCC2 [ 3 ] ) ;
}
// Correct uid size bits in ATQA
rATQA [ 0 ] = ( rATQA [ 0 ] & 0x3f ) | 0x40 ; // double size uid
2019-03-16 04:04:25 +08:00
} else if ( ( flags & FLAG_10B_UID_IN_DATA ) = = FLAG_10B_UID_IN_DATA ) {
memcpy ( & rUIDBCC1 [ 1 ] , datain , 3 ) ;
memcpy ( & rUIDBCC2 [ 1 ] , datain + 3 , 3 ) ;
memcpy ( rUIDBCC3 , datain + 6 , 4 ) ;
* uid_len = 10 ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " MifareSimInit - FLAG_10B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X " , FLAG_10B_UID_IN_DATA , flags , rUIDBCC1 ) ;
2019-03-16 04:04:25 +08:00
2019-04-17 04:52:05 +08:00
// save CUID
* cuid = bytes_to_num ( rUIDBCC3 , 4 ) ;
// CascadeTag, CT
rUIDBCC1 [ 0 ] = MIFARE_SELECT_CT ;
rUIDBCC2 [ 0 ] = MIFARE_SELECT_CT ;
// BCC
rUIDBCC1 [ 4 ] = rUIDBCC1 [ 0 ] ^ rUIDBCC1 [ 1 ] ^ rUIDBCC1 [ 2 ] ^ rUIDBCC1 [ 3 ] ;
rUIDBCC2 [ 4 ] = rUIDBCC2 [ 0 ] ^ rUIDBCC2 [ 1 ] ^ rUIDBCC2 [ 2 ] ^ rUIDBCC2 [ 3 ] ;
rUIDBCC3 [ 4 ] = rUIDBCC3 [ 0 ] ^ rUIDBCC3 [ 1 ] ^ rUIDBCC3 [ 2 ] ^ rUIDBCC3 [ 3 ] ;
2019-08-07 06:47:32 +08:00
if ( DBGLEVEL > DBG_NONE ) {
2019-04-17 04:52:05 +08:00
Dbprintf ( " 10B UID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x " ,
rUIDBCC1 [ 1 ] , rUIDBCC1 [ 2 ] , rUIDBCC1 [ 3 ] ,
rUIDBCC2 [ 1 ] , rUIDBCC2 [ 2 ] , rUIDBCC2 [ 3 ] ,
rUIDBCC3 [ 0 ] , rUIDBCC3 [ 1 ] , rUIDBCC3 [ 2 ] , rUIDBCC3 [ 3 ]
) ;
}
2019-03-16 04:04:25 +08:00
2019-04-17 04:52:05 +08:00
// Correct uid size bits in ATQA
rATQA [ 0 ] = ( rATQA [ 0 ] & 0x3f ) | 0x80 ; // triple size uid
2019-04-17 20:54:42 +08:00
} else {
2020-09-07 03:40:06 +08:00
Dbprintf ( " ERROR: " _RED_ ( " UID size not defined " ) ) ;
2019-04-17 04:52:05 +08:00
return false ;
2019-03-16 04:04:25 +08:00
}
2019-08-07 07:32:37 +08:00
if ( flags & FLAG_FORCED_ATQA ) {
rATQA [ 0 ] = atqa > > 8 ;
rATQA [ 1 ] = atqa & 0xff ;
}
if ( flags & FLAG_FORCED_SAK ) {
rSAK [ 0 ] = sak ;
}
2019-08-07 06:47:32 +08:00
if ( DBGLEVEL > DBG_NONE ) {
2019-08-07 07:32:37 +08:00
Dbprintf ( " ATQA : %02X %02X " , rATQA [ 1 ] , rATQA [ 0 ] ) ;
Dbprintf ( " SAK : %02X " , rSAK [ 0 ] ) ;
2019-08-07 06:47:32 +08:00
}
2019-03-16 04:04:25 +08:00
2019-04-19 02:02:48 +08:00
// clone UIDs for byte-frame anti-collision multiple tag selection procedure
memcpy ( rUIDBCC1b4 , & rUIDBCC1 [ 1 ] , 4 ) ;
memcpy ( rUIDBCC1b3 , & rUIDBCC1 [ 2 ] , 3 ) ;
memcpy ( rUIDBCC1b2 , & rUIDBCC1 [ 3 ] , 2 ) ;
memcpy ( rUIDBCC1b1 , & rUIDBCC1 [ 4 ] , 1 ) ;
if ( * uid_len > = 7 ) {
memcpy ( rUIDBCC2b4 , & rUIDBCC2 [ 1 ] , 4 ) ;
memcpy ( rUIDBCC2b3 , & rUIDBCC2 [ 2 ] , 3 ) ;
memcpy ( rUIDBCC2b2 , & rUIDBCC2 [ 3 ] , 2 ) ;
memcpy ( rUIDBCC2b1 , & rUIDBCC2 [ 4 ] , 1 ) ;
}
if ( * uid_len = = 10 ) {
memcpy ( rUIDBCC3b4 , & rUIDBCC3 [ 1 ] , 4 ) ;
memcpy ( rUIDBCC3b3 , & rUIDBCC3 [ 2 ] , 3 ) ;
memcpy ( rUIDBCC3b2 , & rUIDBCC3 [ 3 ] , 2 ) ;
memcpy ( rUIDBCC3b1 , & rUIDBCC3 [ 4 ] , 1 ) ;
}
2019-04-17 04:52:05 +08:00
// Calculate actual CRC
AddCrc14A ( rSAK , sizeof ( rSAK ) - 2 ) ;
2019-04-19 02:02:48 +08:00
# define TAG_RESPONSE_COUNT 18
2019-03-16 04:04:25 +08:00
static tag_response_info_t responses_init [ TAG_RESPONSE_COUNT ] = {
2019-07-24 06:52:24 +08:00
{ . response = rATQA , . response_n = sizeof ( rATQA ) } , // Answer to request - respond with card type
2019-04-19 02:02:48 +08:00
{ . response = rSAK , . response_n = sizeof ( rSAK ) } , //
2019-07-24 06:52:24 +08:00
{ . response = rSAKuid , . response_n = sizeof ( rSAKuid ) } , //
2019-04-19 02:02:48 +08:00
// Do not reorder. Block used via relative index of rUIDBCC1
2019-07-24 06:52:24 +08:00
{ . response = rUIDBCC1 , . response_n = sizeof ( rUIDBCC1 ) } , // Anticollision cascade1 - respond with first part of uid
2019-04-19 02:02:48 +08:00
{ . response = rUIDBCC1b4 , . response_n = sizeof ( rUIDBCC1b4 ) } ,
{ . response = rUIDBCC1b3 , . response_n = sizeof ( rUIDBCC1b3 ) } ,
{ . response = rUIDBCC1b2 , . response_n = sizeof ( rUIDBCC1b2 ) } ,
{ . response = rUIDBCC1b1 , . response_n = sizeof ( rUIDBCC1b1 ) } ,
// Do not reorder. Block used via relative index of rUIDBCC2
2019-07-24 06:52:24 +08:00
{ . response = rUIDBCC2 , . response_n = sizeof ( rUIDBCC2 ) } , // Anticollision cascade2 - respond with 2nd part of uid
2019-04-19 02:02:48 +08:00
{ . response = rUIDBCC2b4 , . response_n = sizeof ( rUIDBCC2b4 ) } ,
{ . response = rUIDBCC2b3 , . response_n = sizeof ( rUIDBCC2b3 ) } ,
{ . response = rUIDBCC2b2 , . response_n = sizeof ( rUIDBCC2b2 ) } ,
{ . response = rUIDBCC2b1 , . response_n = sizeof ( rUIDBCC2b1 ) } ,
// Do not reorder. Block used via relative index of rUIDBCC3
2019-07-24 06:52:24 +08:00
{ . response = rUIDBCC3 , . response_n = sizeof ( rUIDBCC3 ) } , // Anticollision cascade3 - respond with 3th part of uid
2019-04-19 02:02:48 +08:00
{ . response = rUIDBCC3b4 , . response_n = sizeof ( rUIDBCC3b4 ) } ,
{ . response = rUIDBCC3b3 , . response_n = sizeof ( rUIDBCC3b3 ) } ,
{ . response = rUIDBCC3b2 , . response_n = sizeof ( rUIDBCC3b2 ) } ,
{ . response = rUIDBCC3b1 , . response_n = sizeof ( rUIDBCC3b1 ) }
2019-03-16 04:04:25 +08:00
} ;
2019-04-10 04:07:17 +08:00
// Prepare ("precompile") the responses of the anticollision phase.
// There will be not enough time to do this at the moment the reader sends its REQA or SELECT
2019-04-19 02:02:48 +08:00
// There are 18 predefined responses with a total of 53 bytes data to transmit.
2019-04-10 04:07:17 +08:00
// Coded responses need one byte per bit to transfer (data, parity, start, stop, correction)
2019-04-19 02:02:48 +08:00
// 53 * 8 data bits, 53 * 1 parity bits, 18 start bits, 18 stop bits, 18 correction bits -> need 571 bytes buffer
# define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 571
2019-03-16 04:04:25 +08:00
2019-04-10 04:07:17 +08:00
uint8_t * free_buffer = BigBuf_malloc ( ALLOCATED_TAG_MODULATION_BUFFER_SIZE ) ;
// modulation buffer pointer and current buffer free space size
uint8_t * free_buffer_pointer = free_buffer ;
2019-03-16 04:04:25 +08:00
size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE ;
for ( size_t i = 0 ; i < TAG_RESPONSE_COUNT ; i + + ) {
2019-04-10 04:07:17 +08:00
if ( prepare_allocated_tag_modulation ( & responses_init [ i ] , & free_buffer_pointer , & free_buffer_size ) = = false ) {
Dbprintf ( " Not enough modulation buffer size, exit after %d elements " , i ) ;
return false ;
}
2019-03-16 04:04:25 +08:00
}
* responses = responses_init ;
// indices into responses array:
# define ATQA 0
2019-04-19 02:02:48 +08:00
# define SAK 1
# define SAKuid 2
# define UIDBCC1 3
# define UIDBCC2 8
# define UIDBCC3 13
2019-03-16 04:04:25 +08:00
2019-04-10 04:07:17 +08:00
return true ;
2019-03-16 04:04:25 +08:00
}
/**
* MIFARE 1 K simulate .
*
* @ param flags :
2019-07-24 06:52:24 +08:00
* FLAG_INTERACTIVE - In interactive mode , we are expected to finish the operation with an ACK
2019-03-16 04:04:25 +08:00
* FLAG_4B_UID_IN_DATA - means that there is a 4 - byte UID in the data - section , we ' re expected to use that
* FLAG_7B_UID_IN_DATA - means that there is a 7 - byte UID in the data - section , we ' re expected to use that
2019-07-24 06:52:24 +08:00
* FLAG_10B_UID_IN_DATA - use 10 - byte UID in the data - section not finished
* FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later
2019-03-16 04:04:25 +08:00
* @ param exitAfterNReads , exit simulation after n blocks have been read , 0 is infinite . . .
* ( unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted )
*/
2019-08-07 07:32:37 +08:00
void Mifare1ksim ( uint16_t flags , uint8_t exitAfterNReads , uint8_t * datain , uint16_t atqa , uint8_t sak ) {
2019-03-16 04:04:25 +08:00
tag_response_info_t * responses ;
2019-05-23 15:11:35 +08:00
uint8_t cardSTATE = MFEMUL_NOFIELD ;
2019-03-16 04:04:25 +08:00
uint8_t uid_len = 0 ; // 4,7, 10
uint32_t cuid = 0 ;
2019-07-24 06:52:24 +08:00
int vHf = 0 ; // in mV
2019-03-16 04:04:25 +08:00
uint32_t selTimer = 0 ;
uint32_t authTimer = 0 ;
uint8_t blockNo ;
2019-03-27 21:18:26 +08:00
uint32_t nr ;
uint32_t ar ;
bool encrypted_data ;
2019-03-16 04:04:25 +08:00
uint8_t cardWRBL = 0 ;
uint8_t cardAUTHSC = 0 ;
uint8_t cardAUTHKEY = AUTHKEYNONE ; // no authentication
uint32_t cardRr = 0 ;
uint32_t ans = 0 ;
uint32_t cardINTREG = 0 ;
uint8_t cardINTBLOCK = 0 ;
struct Crypto1State mpcs = { 0 , 0 } ;
struct Crypto1State * pcs ;
pcs = & mpcs ;
2019-07-24 06:52:24 +08:00
uint32_t numReads = 0 ; //Counts numer of times reader reads a block
2019-03-16 04:04:25 +08:00
uint8_t receivedCmd [ MAX_MIFARE_FRAME_SIZE ] = { 0x00 } ;
uint8_t receivedCmd_dec [ MAX_MIFARE_FRAME_SIZE ] = { 0x00 } ;
uint8_t receivedCmd_par [ MAX_MIFARE_PARITY_SIZE ] = { 0x00 } ;
uint16_t receivedCmd_len ;
uint8_t response [ MAX_MIFARE_FRAME_SIZE ] = { 0x00 } ;
uint8_t response_par [ MAX_MIFARE_PARITY_SIZE ] = { 0x00 } ;
2019-04-17 04:52:05 +08:00
uint8_t * rats = NULL ;
uint8_t rats_len = 0 ;
2019-03-16 04:04:25 +08:00
//Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2
// This will be used in the reader-only attack.
//allow collecting up to 7 sets of nonces to allow recovery of up to 7 keys
# define ATTACK_KEY_COUNT 7 // keep same as define in cmdhfmf.c -> readerAttack() (Cannot be more than 7)
2019-10-09 19:05:23 +08:00
nonces_t ar_nr_resp [ ATTACK_KEY_COUNT * 2 ] ; // *2 for 2 separate attack types (nml, moebius) 36 * 7 * 2 bytes = 504 bytes
2019-03-16 04:04:25 +08:00
memset ( ar_nr_resp , 0x00 , sizeof ( ar_nr_resp ) ) ;
2019-10-09 19:05:23 +08:00
uint8_t ar_nr_collected [ ATTACK_KEY_COUNT * 2 ] ; // *2 for 2nd attack type (moebius)
2019-03-16 04:04:25 +08:00
memset ( ar_nr_collected , 0x00 , sizeof ( ar_nr_collected ) ) ;
2019-07-24 06:52:24 +08:00
uint8_t nonce1_count = 0 ;
uint8_t nonce2_count = 0 ;
uint8_t moebius_n_count = 0 ;
2019-03-16 04:04:25 +08:00
bool gettingMoebius = false ;
2019-07-24 06:52:24 +08:00
uint8_t mM = 0 ; //moebius_modifier for collection storage
2019-03-16 04:04:25 +08:00
// Authenticate response - nonce
2019-04-09 00:15:15 +08:00
uint8_t rAUTH_NT [ 4 ] ;
uint8_t rAUTH_NT_keystream [ 4 ] ;
2019-04-06 06:39:27 +08:00
uint32_t nonce = 0 ;
2019-03-28 22:18:53 +08:00
2019-08-08 22:57:33 +08:00
tUart14a * uart = GetUart14a ( ) ;
2019-04-12 05:10:52 +08:00
2019-04-10 04:07:17 +08:00
// free eventually allocated BigBuf memory but keep Emulator Memory
BigBuf_free_keep_EM ( ) ;
2019-08-07 07:32:37 +08:00
if ( MifareSimInit ( flags , datain , atqa , sak , & responses , & cuid , & uid_len , & rats , & rats_len ) = = false ) {
2019-04-10 04:07:17 +08:00
BigBuf_free_keep_EM ( ) ;
return ;
}
2019-03-16 04:04:25 +08:00
// We need to listen to the high-frequency, peak-detected path.
iso14443a_setup ( FPGA_HF_ISO14443A_TAGSIM_LISTEN ) ;
// clear trace
clear_trace ( ) ;
set_tracing ( true ) ;
LED_D_ON ( ) ;
ResetSspClk ( ) ;
2020-08-31 07:15:49 +08:00
int counter = 0 ;
2019-03-16 04:04:25 +08:00
bool finished = false ;
bool button_pushed = BUTTON_PRESS ( ) ;
2020-08-31 07:15:49 +08:00
while ( ! button_pushed & & ! finished ) {
2019-03-16 04:04:25 +08:00
WDT_HIT ( ) ;
2020-09-07 16:39:15 +08:00
2020-08-31 07:15:49 +08:00
if ( counter = = 2000 ) {
if ( data_available ( ) ) {
break ;
}
counter = 0 ;
} else {
counter + + ;
}
2019-03-16 04:04:25 +08:00
// find reader field
if ( cardSTATE = = MFEMUL_NOFIELD ) {
2020-03-10 00:11:11 +08:00
2020-02-12 17:29:00 +08:00
# if defined RDV4
2020-06-10 05:37:56 +08:00
vHf = ( MAX_ADC_HF_VOLTAGE_RDV40 * SumAdc ( ADC_CHAN_HF_RDV40 , 32 ) ) > > 15 ;
2020-02-12 17:29:00 +08:00
# else
2020-06-10 05:37:56 +08:00
vHf = ( MAX_ADC_HF_VOLTAGE * SumAdc ( ADC_CHAN_HF , 32 ) ) > > 15 ;
2020-02-12 17:29:00 +08:00
# endif
2020-03-10 00:11:11 +08:00
2019-03-16 04:04:25 +08:00
if ( vHf > MF_MINFIELDV ) {
cardSTATE_TO_IDLE ( ) ;
LED_A_ON ( ) ;
}
button_pushed = BUTTON_PRESS ( ) ;
continue ;
}
2019-03-16 04:07:15 +08:00
2020-01-13 17:34:59 +08:00
FpgaEnableTracing ( ) ;
2019-03-16 04:04:25 +08:00
//Now, get data
int res = EmGetCmd ( receivedCmd , & receivedCmd_len , receivedCmd_par ) ;
if ( res = = 2 ) { //Field is off!
2020-04-16 15:53:31 +08:00
//FpgaDisableTracing();
2019-03-16 04:04:25 +08:00
LEDsoff ( ) ;
cardSTATE = MFEMUL_NOFIELD ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " cardSTATE = MFEMUL_NOFIELD " ) ;
2019-03-16 04:04:25 +08:00
continue ;
} else if ( res = = 1 ) { // button pressed
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
button_pushed = true ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " Button pressed " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
// WUPA in HALTED state or REQA or WUPA in any other state
if ( receivedCmd_len = = 1 & & ( ( receivedCmd [ 0 ] = = ISO14443A_CMD_REQA & & cardSTATE ! = MFEMUL_HALTED ) | | receivedCmd [ 0 ] = = ISO14443A_CMD_WUPA ) ) {
selTimer = GetTickCount ( ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " EmSendPrecompiledCmd(&responses[ATQA]); " ) ;
2019-03-16 04:04:25 +08:00
EmSendPrecompiledCmd ( & responses [ ATQA ] ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
// init crypto block
2019-10-18 22:58:24 +08:00
crypto1_deinit ( pcs ) ;
2019-03-16 04:04:25 +08:00
cardAUTHKEY = AUTHKEYNONE ;
2019-03-27 21:18:26 +08:00
nonce = prng_successor ( selTimer , 32 ) ;
2019-04-09 00:15:15 +08:00
// prepare NT for nested authentication
num_to_bytes ( nonce , 4 , rAUTH_NT ) ;
num_to_bytes ( cuid ^ nonce , 4 , rAUTH_NT_keystream ) ;
2019-03-28 22:18:53 +08:00
2019-03-16 04:04:25 +08:00
LED_B_OFF ( ) ;
LED_C_OFF ( ) ;
2019-04-19 02:02:48 +08:00
cardSTATE = MFEMUL_SELECT ;
2019-03-16 04:04:25 +08:00
continue ;
}
switch ( cardSTATE ) {
2020-04-16 15:53:31 +08:00
case MFEMUL_NOFIELD : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " MFEMUL_NOFIELD " ) ;
2019-10-10 05:44:46 +08:00
break ;
2020-04-16 15:53:31 +08:00
}
case MFEMUL_HALTED : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " MFEMUL_HALTED " ) ;
2019-10-10 05:44:46 +08:00
break ;
2020-04-16 15:53:31 +08:00
}
2019-03-16 04:04:25 +08:00
case MFEMUL_IDLE : {
2019-04-12 05:10:52 +08:00
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " MFEMUL_IDLE " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
// The anti-collision sequence, which is a mandatory part of the card activation sequence.
// It auto with 4-byte UID (= Single Size UID),
// 7 -byte UID (= Double Size UID) or 10-byte UID (= Triple Size UID).
2019-04-19 02:02:48 +08:00
// For details see chapter 2 of AN10927.pdf
2019-03-16 04:04:25 +08:00
//
2019-04-19 02:02:48 +08:00
// This case is used for all Cascade Levels, because:
// 1) Any devices (under Android for example) after full select procedure completed,
// when UID is known, uses "fast-selection" method. In this case reader ignores
// first cascades and tries to select tag by last bytes of UID of last cascade
// 2) Any readers (like ACR122U) uses bit oriented anti-collision frames during selectin,
// same as multiple tags. For details see chapter 6.1.5.3 of ISO/IEC 14443-3
case MFEMUL_SELECT : {
int uid_index = - 1 ;
// Extract cascade level
if ( receivedCmd_len > = 2 ) {
switch ( receivedCmd [ 0 ] ) {
case ISO14443A_CMD_ANTICOLL_OR_SELECT :
uid_index = UIDBCC1 ;
2019-03-27 21:18:26 +08:00
break ;
2019-04-19 02:02:48 +08:00
case ISO14443A_CMD_ANTICOLL_OR_SELECT_2 :
uid_index = UIDBCC2 ;
2019-03-27 21:18:26 +08:00
break ;
2019-04-19 02:02:48 +08:00
case ISO14443A_CMD_ANTICOLL_OR_SELECT_3 :
uid_index = UIDBCC3 ;
2019-03-16 04:04:25 +08:00
break ;
2019-04-19 02:02:48 +08:00
}
}
if ( uid_index < 0 ) {
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-03-27 21:18:26 +08:00
cardSTATE_TO_IDLE ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_SELECT] Incorrect cascade level received " ) ;
2019-04-19 02:02:48 +08:00
break ;
2019-03-27 21:18:26 +08:00
}
2019-03-16 04:04:25 +08:00
2019-04-19 02:02:48 +08:00
// Incoming SELECT ALL for any cascade level
if ( receivedCmd_len = = 2 & & receivedCmd [ 1 ] = = 0x20 ) {
EmSendPrecompiledCmd ( & responses [ uid_index ] ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " SELECT ALL - EmSendPrecompiledCmd(%02x) " , & responses [ uid_index ] ) ;
2019-03-27 21:18:26 +08:00
break ;
2019-03-16 04:04:25 +08:00
}
2019-04-19 02:02:48 +08:00
// Incoming SELECT CLx for any cascade level
if ( receivedCmd_len = = 9 & & receivedCmd [ 1 ] = = 0x70 ) {
if ( memcmp ( & receivedCmd [ 2 ] , responses [ uid_index ] . response , 4 ) = = 0 ) {
2019-06-12 21:41:23 +08:00
bool cl_finished = ( uid_len = = 4 & & uid_index = = UIDBCC1 ) | |
2019-07-13 06:38:30 +08:00
( uid_len = = 7 & & uid_index = = UIDBCC2 ) | |
( uid_len = = 10 & & uid_index = = UIDBCC3 ) ;
2019-06-12 21:41:23 +08:00
EmSendPrecompiledCmd ( & responses [ cl_finished ? SAK : SAKuid ] ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " SELECT CLx %02x%02x%02x%02x received " , receivedCmd [ 2 ] , receivedCmd [ 3 ] , receivedCmd [ 4 ] , receivedCmd [ 5 ] ) ;
2019-06-12 21:41:23 +08:00
if ( cl_finished ) {
2019-03-16 04:04:25 +08:00
LED_B_ON ( ) ;
2019-04-19 02:02:48 +08:00
cardSTATE = MFEMUL_WORK ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_SELECT] cardSTATE = MFEMUL_WORK " ) ;
2019-04-19 02:02:48 +08:00
}
} else {
// IDLE, not our UID
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
cardSTATE_TO_IDLE ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_SELECT] cardSTATE = MFEMUL_IDLE " ) ;
2019-03-16 04:04:25 +08:00
}
break ;
}
2019-04-19 02:02:48 +08:00
// Incoming anti-collision frame
2019-07-15 04:18:07 +08:00
// receivedCmd[1] indicates number of byte and bit collision, supports only for bit collision is zero
if ( receivedCmd_len > = 3 & & receivedCmd_len < = 6 & & ( receivedCmd [ 1 ] & 0x0f ) = = 0 ) {
2019-04-19 02:02:48 +08:00
// we can process only full-byte frame anti-collision procedure
if ( memcmp ( & receivedCmd [ 2 ] , responses [ uid_index ] . response , receivedCmd_len - 2 ) = = 0 ) {
// response missing part of UID via relative array index
EmSendPrecompiledCmd ( & responses [ uid_index + receivedCmd_len - 2 ] ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " SELECT ANTICOLLISION - EmSendPrecompiledCmd(%02x) " , & responses [ uid_index ] ) ;
2019-04-19 02:02:48 +08:00
} else {
// IDLE, not our UID or split-byte frame anti-collision (not supports)
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
cardSTATE_TO_IDLE ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_SELECT] cardSTATE = MFEMUL_IDLE " ) ;
2019-03-27 21:18:26 +08:00
}
2019-04-19 02:02:48 +08:00
break ;
2019-03-16 04:04:25 +08:00
}
2019-04-19 02:02:48 +08:00
// Unknown selection procedure
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
cardSTATE_TO_IDLE ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_SELECT] Unknown selection procedure " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
2019-03-27 21:18:26 +08:00
// WORK
2019-03-16 04:04:25 +08:00
case MFEMUL_WORK : {
2019-03-27 21:18:26 +08:00
2020-04-16 15:53:31 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) {
2019-07-24 06:52:24 +08:00
Dbprintf ( " [MFEMUL_WORK] Enter in case " ) ;
2020-04-16 15:53:31 +08:00
}
2019-03-27 21:18:26 +08:00
if ( receivedCmd_len = = 0 ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] NO CMD received " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
2019-03-27 21:18:26 +08:00
encrypted_data = ( cardAUTHKEY ! = AUTHKEYNONE ) ;
2019-03-16 04:04:25 +08:00
if ( encrypted_data ) {
// decrypt seqence
mf_crypto1_decryptEx ( pcs , receivedCmd , receivedCmd_len , receivedCmd_dec ) ;
2020-04-16 15:53:31 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] Decrypt sequence " ) ;
2019-03-16 04:04:25 +08:00
} else {
2019-03-27 21:18:26 +08:00
// Data in clear
2019-03-16 04:04:25 +08:00
memcpy ( receivedCmd_dec , receivedCmd , receivedCmd_len ) ;
}
2019-03-27 21:18:26 +08:00
2020-04-16 15:53:31 +08:00
// all commands must have a valid CRC
if ( ! CheckCrc14A ( receivedCmd_dec , receivedCmd_len ) ) {
2019-04-05 09:58:15 +08:00
EmSend4bit ( encrypted_data ? mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) : CARD_NACK_NA ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] All commands must have a valid CRC %02X (%d) " , receivedCmd_dec , receivedCmd_len ) ;
2019-03-16 04:04:25 +08:00
break ;
}
if ( receivedCmd_len = = 4 & & ( receivedCmd_dec [ 0 ] = = MIFARE_AUTH_KEYA | | receivedCmd_dec [ 0 ] = = MIFARE_AUTH_KEYB ) ) {
2019-03-27 21:18:26 +08:00
// Reader asks for AUTH: 6X XX
// RCV: 60 XX => Using KEY A
// RCV: 61 XX => Using KEY B
// XX: Block number
authTimer = GetTickCount ( ) ;
// received block num -> sector
// Example: 6X [00]
2019-04-09 00:15:15 +08:00
// 4K tags have 16 blocks per sector 32..39
cardAUTHSC = MifareBlockToSector ( receivedCmd_dec [ 1 ] ) ;
2019-03-27 21:18:26 +08:00
2019-04-06 06:39:27 +08:00
// cardAUTHKEY: 60 => Auth use Key A
2019-03-27 21:18:26 +08:00
// cardAUTHKEY: 61 => Auth use Key B
2019-03-16 04:04:25 +08:00
cardAUTHKEY = receivedCmd_dec [ 0 ] & 0x01 ;
2019-03-27 21:18:26 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] KEY %c: %012 " PRIx64 , ( cardAUTHKEY = = 0 ) ? ' A ' : ' B ' , emlGetKey ( cardAUTHSC , cardAUTHKEY ) ) ;
2019-03-27 21:18:26 +08:00
// first authentication
2019-10-18 22:58:24 +08:00
crypto1_deinit ( pcs ) ;
2019-03-27 21:18:26 +08:00
// Load key into crypto
2019-10-18 22:58:24 +08:00
crypto1_init ( pcs , emlGetKey ( cardAUTHSC , cardAUTHKEY ) ) ;
2019-03-16 04:04:25 +08:00
if ( ! encrypted_data ) {
2019-03-27 21:18:26 +08:00
// Receive Cmd in clear txt
// Update crypto state (UID ^ NONCE)
crypto1_word ( pcs , cuid ^ nonce , 0 ) ;
2019-04-09 00:15:15 +08:00
// rAUTH_NT contains prepared nonce for authenticate
EmSendCmd ( rAUTH_NT , sizeof ( rAUTH_NT ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-07-19 21:27:08 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] Reader authenticating for block %d (0x%02x) with key %c - nonce: %02X - ciud: %02X " , receivedCmd_dec [ 1 ] , receivedCmd_dec [ 1 ] , ( cardAUTHKEY = = 0 ) ? ' A ' : ' B ' , rAUTH_NT , cuid ) ;
2019-03-27 21:18:26 +08:00
} else {
// nested authentication
2019-04-09 00:15:15 +08:00
/*
2019-03-16 04:04:25 +08:00
ans = nonce ^ crypto1_word ( pcs , cuid ^ nonce , 0 ) ;
num_to_bytes ( ans , 4 , rAUTH_AT ) ;
2019-04-09 00:15:15 +08:00
*/
// rAUTH_NT, rAUTH_NT_keystream contains prepared nonce and keystream for nested authentication
// we need calculate parity bits for non-encrypted sequence
mf_crypto1_encryptEx ( pcs , rAUTH_NT , rAUTH_NT_keystream , response , 4 , response_par ) ;
EmSendCmdPar ( response , 4 , response_par ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] Reader doing nested authentication for block %d (0x%02x) with key %c " , receivedCmd_dec [ 1 ] , receivedCmd_dec [ 1 ] , ( cardAUTHKEY = = 0 ) ? ' A ' : ' B ' ) ;
2019-03-16 04:04:25 +08:00
}
2019-03-27 21:18:26 +08:00
2019-03-16 04:04:25 +08:00
cardSTATE = MFEMUL_AUTH1 ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] cardSTATE = MFEMUL_AUTH1 - rAUTH_NT: %02X " , rAUTH_NT ) ;
2019-04-10 04:07:17 +08:00
break ;
2019-03-27 21:18:26 +08:00
}
// rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued
// BUT... ACK --> NACK
if ( receivedCmd_len = = 1 & & receivedCmd_dec [ 0 ] = = CARD_ACK ) {
2019-04-05 09:58:15 +08:00
EmSend4bit ( encrypted_data ? mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) : CARD_NACK_NA ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
break ;
}
2019-03-27 21:18:26 +08:00
// rule 12 of 7.5.3. in ISO 14443-4. R(NAK) --> R(ACK)
if ( receivedCmd_len = = 1 & & receivedCmd_dec [ 0 ] = = CARD_NACK_NA ) {
2019-04-05 09:58:15 +08:00
EmSend4bit ( encrypted_data ? mf_crypto1_encrypt4bit ( pcs , CARD_ACK ) : CARD_ACK ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
break ;
}
2019-03-16 04:07:15 +08:00
2019-03-27 21:18:26 +08:00
// case MFEMUL_WORK => if Cmd is Read, Write, Inc, Dec, Restore, Transfert
2019-04-10 04:07:17 +08:00
if ( receivedCmd_len = = 4 & & ( receivedCmd_dec [ 0 ] = = ISO14443A_CMD_READBLOCK
2019-04-10 15:32:49 +08:00
| | receivedCmd_dec [ 0 ] = = ISO14443A_CMD_WRITEBLOCK
| | receivedCmd_dec [ 0 ] = = MIFARE_CMD_INC
| | receivedCmd_dec [ 0 ] = = MIFARE_CMD_DEC
| | receivedCmd_dec [ 0 ] = = MIFARE_CMD_RESTORE
| | receivedCmd_dec [ 0 ] = = MIFARE_CMD_TRANSFER ) ) {
2019-04-10 04:07:17 +08:00
// all other commands must be encrypted (authenticated)
if ( ! encrypted_data ) {
EmSend4bit ( CARD_NACK_NA ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] Commands must be encrypted (authenticated) " ) ;
2019-04-10 04:07:17 +08:00
break ;
}
2019-10-09 16:59:10 +08:00
// iceman, u8 can never be larger the MIFARE_4K_MAXBLOCK (256)
2019-03-16 04:04:25 +08:00
// Check if Block num is not too far
2019-10-09 16:59:10 +08:00
/*
2019-03-16 04:04:25 +08:00
if ( receivedCmd_dec [ 1 ] > MIFARE_4K_MAXBLOCK ) {
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " [MFEMUL_WORK] Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking " , receivedCmd_dec [ 0 ] , receivedCmd_dec [ 1 ] , receivedCmd_dec [ 1 ] ) ;
2019-03-16 04:04:25 +08:00
break ;
}
2019-10-09 16:59:10 +08:00
*/
2019-04-10 04:07:17 +08:00
if ( MifareBlockToSector ( receivedCmd_dec [ 1 ] ) ! = cardAUTHSC ) {
2019-03-16 04:04:25 +08:00
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " [MFEMUL_WORK] Reader tried to operate (0x%02x) on block (0x%02x) not authenticated for (0x%02x), nacking " , receivedCmd_dec [ 0 ] , receivedCmd_dec [ 1 ] , cardAUTHSC ) ;
2019-03-16 04:04:25 +08:00
break ;
}
}
2019-03-16 04:07:15 +08:00
2019-03-27 21:18:26 +08:00
// case MFEMUL_WORK => CMD READ block
2019-04-10 04:07:17 +08:00
if ( receivedCmd_len = = 4 & & receivedCmd_dec [ 0 ] = = ISO14443A_CMD_READBLOCK ) {
2019-03-16 04:04:25 +08:00
blockNo = receivedCmd_dec [ 1 ] ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] Reader reading block %d (0x%02x) " , blockNo , blockNo ) ;
2019-03-16 04:04:25 +08:00
emlGetMem ( response , blockNo , 1 ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) {
2019-03-27 21:18:26 +08:00
Dbprintf ( " [MFEMUL_WORK - ISO14443A_CMD_READBLOCK] Data Block[%d]: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x " , blockNo ,
response [ 0 ] , response [ 1 ] , response [ 2 ] , response [ 3 ] , response [ 4 ] , response [ 5 ] , response [ 6 ] ,
response [ 7 ] , response [ 8 ] , response [ 9 ] , response [ 10 ] , response [ 11 ] , response [ 12 ] , response [ 13 ] ,
response [ 14 ] , response [ 15 ] ) ;
2019-03-16 08:25:10 +08:00
}
2019-03-16 04:07:15 +08:00
2019-03-28 22:18:53 +08:00
// Access permission managment:
//
// Sector Trailer:
// - KEY A access
// - KEY B access
// - AC bits access
//
// Data block:
// - Data access
// If permission is not allowed, data is cleared (00) in emulator memeory.
// ex: a0a1a2a3a4a561e789c1b0b1b2b3b4b5 => 00000000000061e789c1b0b1b2b3b4b5
// Check if selected Block is a Sector Trailer
2019-03-16 04:04:25 +08:00
if ( IsSectorTrailer ( blockNo ) ) {
2019-03-27 21:18:26 +08:00
2019-03-18 20:36:36 +08:00
if ( ! IsAccessAllowed ( blockNo , cardAUTHKEY , AC_KEYA_READ ) ) {
2019-07-24 06:52:24 +08:00
memset ( response , 0x00 , 6 ) ; // keyA can never be read
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK - IsSectorTrailer] keyA can never be read - block %d (0x%02x) " , blockNo , blockNo ) ;
2019-03-18 20:36:36 +08:00
}
2019-03-16 04:04:25 +08:00
if ( ! IsAccessAllowed ( blockNo , cardAUTHKEY , AC_KEYB_READ ) ) {
2019-07-24 06:52:24 +08:00
memset ( response + 10 , 0x00 , 6 ) ; // keyB cannot be read
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK - IsSectorTrailer] keyB cannot be read - block %d (0x%02x) " , blockNo , blockNo ) ;
2019-03-16 04:04:25 +08:00
}
if ( ! IsAccessAllowed ( blockNo , cardAUTHKEY , AC_AC_READ ) ) {
2019-07-24 06:52:24 +08:00
memset ( response + 6 , 0x00 , 4 ) ; // AC bits cannot be read
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK - IsAccessAllowed] AC bits cannot be read - block %d (0x%02x) " , blockNo , blockNo ) ;
2019-03-16 04:04:25 +08:00
}
} else {
if ( ! IsAccessAllowed ( blockNo , cardAUTHKEY , AC_DATA_READ ) ) {
2019-07-24 06:52:24 +08:00
memset ( response , 0x00 , 16 ) ; // datablock cannot be read
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK - IsAccessAllowed] Data block %d (0x%02x) cannot be read " , blockNo , blockNo ) ;
2019-03-16 04:04:25 +08:00
}
}
2019-04-09 00:15:15 +08:00
AddCrc14A ( response , 16 ) ;
2019-03-16 10:01:03 +08:00
mf_crypto1_encrypt ( pcs , response , MAX_MIFARE_FRAME_SIZE , response_par ) ;
EmSendCmdPar ( response , MAX_MIFARE_FRAME_SIZE , response_par ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) {
2019-03-27 21:18:26 +08:00
Dbprintf ( " [MFEMUL_WORK - EmSendCmdPar] Data Block[%d]: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x " , blockNo ,
response [ 0 ] , response [ 1 ] , response [ 2 ] , response [ 3 ] , response [ 4 ] , response [ 5 ] , response [ 6 ] ,
response [ 7 ] , response [ 8 ] , response [ 9 ] , response [ 10 ] , response [ 11 ] , response [ 12 ] , response [ 13 ] ,
response [ 14 ] , response [ 15 ] ) ;
}
2019-03-16 04:04:25 +08:00
numReads + + ;
2019-03-18 20:36:36 +08:00
2019-03-16 04:04:25 +08:00
if ( exitAfterNReads > 0 & & numReads = = exitAfterNReads ) {
2019-03-27 21:18:26 +08:00
Dbprintf ( " [MFEMUL_WORK] %d reads done, exiting " , numReads ) ;
2019-03-16 04:04:25 +08:00
finished = true ;
}
break ;
2019-03-16 04:07:15 +08:00
2019-03-27 21:18:26 +08:00
} // End receivedCmd_dec[0] == ISO14443A_CMD_READBLOCK
// case MFEMUL_WORK => CMD WRITEBLOCK
2019-04-10 04:07:17 +08:00
if ( receivedCmd_len = = 4 & & receivedCmd_dec [ 0 ] = = ISO14443A_CMD_WRITEBLOCK ) {
2019-03-16 04:04:25 +08:00
blockNo = receivedCmd_dec [ 1 ] ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] RECV 0xA0 write block %d (%02x) " , blockNo , blockNo ) ;
2019-03-16 04:04:25 +08:00
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_ACK ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2020-01-16 02:25:29 +08:00
2019-03-16 04:04:25 +08:00
cardWRBL = blockNo ;
cardSTATE = MFEMUL_WRITEBL2 ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] cardSTATE = MFEMUL_WRITEBL2 " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
2019-03-27 21:18:26 +08:00
// case MFEMUL_WORK => CMD INC/DEC/REST
2019-04-10 04:07:17 +08:00
if ( receivedCmd_len = = 4 & & ( receivedCmd_dec [ 0 ] = = MIFARE_CMD_INC | | receivedCmd_dec [ 0 ] = = MIFARE_CMD_DEC | | receivedCmd_dec [ 0 ] = = MIFARE_CMD_RESTORE ) ) {
2019-03-16 04:04:25 +08:00
blockNo = receivedCmd_dec [ 1 ] ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x) " , receivedCmd_dec [ 0 ] , blockNo , blockNo ) ;
2019-03-16 04:04:25 +08:00
if ( emlCheckValBl ( blockNo ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_ERROR ) Dbprintf ( " [MFEMUL_WORK] Reader tried to operate on block, but emlCheckValBl failed, nacking " ) ;
2019-03-16 04:04:25 +08:00
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
break ;
}
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_ACK ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
cardWRBL = blockNo ;
// INC
if ( receivedCmd_dec [ 0 ] = = MIFARE_CMD_INC ) {
cardSTATE = MFEMUL_INTREG_INC ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] cardSTATE = MFEMUL_INTREG_INC " ) ;
2019-03-16 04:04:25 +08:00
}
// DEC
if ( receivedCmd_dec [ 0 ] = = MIFARE_CMD_DEC ) {
cardSTATE = MFEMUL_INTREG_DEC ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] cardSTATE = MFEMUL_INTREG_DEC " ) ;
2019-03-16 04:04:25 +08:00
}
// REST
if ( receivedCmd_dec [ 0 ] = = MIFARE_CMD_RESTORE ) {
cardSTATE = MFEMUL_INTREG_REST ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] cardSTATE = MFEMUL_INTREG_REST " ) ;
2019-03-16 04:04:25 +08:00
}
2019-03-27 21:18:26 +08:00
break ;
} // End case MFEMUL_WORK => CMD INC/DEC/REST
2019-03-16 04:07:15 +08:00
2019-03-27 21:18:26 +08:00
// case MFEMUL_WORK => CMD TRANSFER
2019-04-10 04:07:17 +08:00
if ( receivedCmd_len = = 4 & & receivedCmd_dec [ 0 ] = = MIFARE_CMD_TRANSFER ) {
2019-03-16 04:04:25 +08:00
blockNo = receivedCmd_dec [ 1 ] ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WORK] RECV 0x%02x transfer block %d (%02x) " , receivedCmd_dec [ 0 ] , blockNo , blockNo ) ;
2019-03-16 04:04:25 +08:00
if ( emlSetValBl ( cardINTREG , cardINTBLOCK , receivedCmd_dec [ 1 ] ) )
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) ) ;
else
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_ACK ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
break ;
}
2019-03-27 21:18:26 +08:00
// case MFEMUL_WORK => CMD HALT
2019-04-10 04:07:17 +08:00
if ( receivedCmd_len > 1 & & receivedCmd_dec [ 0 ] = = ISO14443A_CMD_HALT & & receivedCmd_dec [ 1 ] = = 0x00 ) {
2019-04-12 05:10:52 +08:00
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-03-16 04:04:25 +08:00
LED_B_OFF ( ) ;
LED_C_OFF ( ) ;
cardSTATE = MFEMUL_HALTED ;
2019-04-10 04:07:17 +08:00
cardAUTHKEY = AUTHKEYNONE ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " [MFEMUL_WORK] cardSTATE = MFEMUL_HALTED " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
2019-03-27 21:18:26 +08:00
// case MFEMUL_WORK => CMD RATS
2019-04-17 04:52:05 +08:00
if ( receivedCmd_len = = 4 & & receivedCmd_dec [ 0 ] = = ISO14443A_CMD_RATS & & receivedCmd_dec [ 1 ] = = 0x80 ) {
if ( rats & & rats_len ) {
if ( encrypted_data ) {
memcpy ( response , rats , rats_len ) ;
mf_crypto1_encrypt ( pcs , response , rats_len , response_par ) ;
EmSendCmdPar ( response , rats_len , response_par ) ;
2020-01-13 18:36:39 +08:00
} else {
2019-04-17 04:52:05 +08:00
EmSendCmd ( rats , rats_len ) ;
2020-01-13 18:36:39 +08:00
}
FpgaDisableTracing ( ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " [MFEMUL_WORK] RCV RATS => ACK " ) ;
2019-04-17 20:54:42 +08:00
} else {
2019-04-17 04:52:05 +08:00
EmSend4bit ( encrypted_data ? mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) : CARD_NACK_NA ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " [MFEMUL_WORK] RCV RATS => NACK " ) ;
2019-04-17 04:52:05 +08:00
}
break ;
}
2019-04-17 20:54:42 +08:00
2019-04-17 04:52:05 +08:00
// case MFEMUL_WORK => ISO14443A_CMD_NXP_DESELECT
if ( receivedCmd_len = = 3 & & receivedCmd_dec [ 0 ] = = ISO14443A_CMD_NXP_DESELECT ) {
if ( rats & & rats_len ) {
// response back NXP_DESELECT
if ( encrypted_data ) {
memcpy ( response , receivedCmd_dec , receivedCmd_len ) ;
mf_crypto1_encrypt ( pcs , response , receivedCmd_len , response_par ) ;
EmSendCmdPar ( response , receivedCmd_len , response_par ) ;
2019-04-17 20:54:42 +08:00
} else
2019-04-17 04:52:05 +08:00
EmSendCmd ( receivedCmd_dec , receivedCmd_len ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " [MFEMUL_WORK] RCV NXP DESELECT => ACK " ) ;
2019-04-17 20:54:42 +08:00
} else {
2019-04-17 04:52:05 +08:00
EmSend4bit ( encrypted_data ? mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) : CARD_NACK_NA ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " [MFEMUL_WORK] RCV NXP DESELECT => NACK " ) ;
2019-04-17 04:52:05 +08:00
}
2019-03-16 04:04:25 +08:00
break ;
}
2019-03-27 21:18:26 +08:00
// case MFEMUL_WORK => command not allowed
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " Received command not allowed, nacking " ) ;
2019-04-05 09:58:15 +08:00
EmSend4bit ( encrypted_data ? mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) : CARD_NACK_NA ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-04-05 09:58:15 +08:00
break ;
2019-03-16 04:04:25 +08:00
}
2019-03-27 21:18:26 +08:00
// AUTH1
2019-03-16 04:04:25 +08:00
case MFEMUL_AUTH1 : {
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " [MFEMUL_AUTH1] Enter case " ) ;
2019-03-16 04:07:15 +08:00
2019-04-10 04:07:17 +08:00
if ( receivedCmd_len ! = 8 ) {
2019-04-06 06:39:27 +08:00
cardSTATE_TO_IDLE ( ) ;
2019-04-12 05:10:52 +08:00
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
Dbprintf ( " MFEMUL_AUTH1: receivedCmd_len != 8 (%d) => cardSTATE_TO_IDLE()) " , receivedCmd_len ) ;
2019-04-06 06:39:27 +08:00
break ;
2019-04-06 06:32:11 +08:00
}
2019-03-27 21:18:26 +08:00
nr = bytes_to_num ( receivedCmd , 4 ) ;
ar = bytes_to_num ( & receivedCmd [ 4 ] , 4 ) ;
2019-03-16 04:04:25 +08:00
// Collect AR/NR per keytype & sector
if ( ( flags & FLAG_NR_AR_ATTACK ) = = FLAG_NR_AR_ATTACK ) {
2019-05-27 01:42:27 +08:00
2019-03-16 04:04:25 +08:00
for ( uint8_t i = 0 ; i < ATTACK_KEY_COUNT ; i + + ) {
if ( ar_nr_collected [ i + mM ] = = 0 | | ( ( cardAUTHSC = = ar_nr_resp [ i + mM ] . sector ) & & ( cardAUTHKEY = = ar_nr_resp [ i + mM ] . keytype ) & & ( ar_nr_collected [ i + mM ] > 0 ) ) ) {
// if first auth for sector, or matches sector and keytype of previous auth
if ( ar_nr_collected [ i + mM ] < 2 ) {
// if we haven't already collected 2 nonces for this sector
if ( ar_nr_resp [ ar_nr_collected [ i + mM ] ] . ar ! = ar ) {
// Avoid duplicates... probably not necessary, ar should vary.
if ( ar_nr_collected [ i + mM ] = = 0 ) {
// first nonce collect
ar_nr_resp [ i + mM ] . cuid = cuid ;
ar_nr_resp [ i + mM ] . sector = cardAUTHSC ;
ar_nr_resp [ i + mM ] . keytype = cardAUTHKEY ;
ar_nr_resp [ i + mM ] . nonce = nonce ;
ar_nr_resp [ i + mM ] . nr = nr ;
ar_nr_resp [ i + mM ] . ar = ar ;
nonce1_count + + ;
// add this nonce to first moebius nonce
ar_nr_resp [ i + ATTACK_KEY_COUNT ] . cuid = cuid ;
ar_nr_resp [ i + ATTACK_KEY_COUNT ] . sector = cardAUTHSC ;
ar_nr_resp [ i + ATTACK_KEY_COUNT ] . keytype = cardAUTHKEY ;
ar_nr_resp [ i + ATTACK_KEY_COUNT ] . nonce = nonce ;
ar_nr_resp [ i + ATTACK_KEY_COUNT ] . nr = nr ;
ar_nr_resp [ i + ATTACK_KEY_COUNT ] . ar = ar ;
ar_nr_collected [ i + ATTACK_KEY_COUNT ] + + ;
} else { // second nonce collect (std and moebius)
ar_nr_resp [ i + mM ] . nonce2 = nonce ;
ar_nr_resp [ i + mM ] . nr2 = nr ;
ar_nr_resp [ i + mM ] . ar2 = ar ;
2019-06-08 03:40:33 +08:00
2019-03-16 04:04:25 +08:00
if ( ! gettingMoebius ) {
nonce2_count + + ;
// check if this was the last second nonce we need for std attack
if ( nonce2_count = = nonce1_count ) {
// done collecting std test switch to moebius
// first finish incrementing last sample
ar_nr_collected [ i + mM ] + + ;
// switch to moebius collection
gettingMoebius = true ;
mM = ATTACK_KEY_COUNT ;
2019-03-28 05:35:11 +08:00
nonce = nonce * 7 ;
2019-03-16 04:04:25 +08:00
break ;
}
} else {
moebius_n_count + + ;
// if we've collected all the nonces we need - finish.
2019-06-08 03:40:33 +08:00
if ( nonce1_count = = moebius_n_count )
2019-05-27 01:42:27 +08:00
finished = true ;
2019-03-16 04:04:25 +08:00
}
}
ar_nr_collected [ i + mM ] + + ;
}
}
// we found right spot for this nonce stop looking
break ;
}
}
}
// --- crypto
crypto1_word ( pcs , nr , 1 ) ;
cardRr = ar ^ crypto1_word ( pcs , 0 , 0 ) ;
2019-03-27 21:18:26 +08:00
// test if auth KO
2019-03-16 04:04:25 +08:00
if ( cardRr ! = prng_successor ( nonce , 64 ) ) {
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) {
2019-03-27 21:18:26 +08:00
Dbprintf ( " [MFEMUL_AUTH1] AUTH FAILED for sector %d with key %c. [nr=%08x cardRr=%08x] [nt=%08x succ=%08x] "
2019-03-16 04:04:25 +08:00
, cardAUTHSC
, ( cardAUTHKEY = = 0 ) ? ' A ' : ' B '
, nr
, cardRr
, nonce // nt
, prng_successor ( nonce , 64 )
) ;
}
2019-07-24 06:52:24 +08:00
cardAUTHKEY = AUTHKEYNONE ; // not authenticated
2019-03-16 04:07:15 +08:00
cardSTATE_TO_IDLE ( ) ;
2019-07-15 04:12:39 +08:00
// Really tags not respond NACK on invalid authentication
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-03-16 04:07:15 +08:00
break ;
}
2019-03-27 21:18:26 +08:00
ans = prng_successor ( nonce , 96 ) ;
2019-07-19 21:27:08 +08:00
num_to_bytes ( ans , 4 , response ) ;
mf_crypto1_encrypt ( pcs , response , 4 , response_par ) ;
EmSendCmdPar ( response , 4 , response_par ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-27 21:18:26 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) {
2019-03-27 21:18:26 +08:00
Dbprintf ( " [MFEMUL_AUTH1] AUTH COMPLETED for sector %d with key %c. time=%d " ,
2019-03-16 04:04:25 +08:00
cardAUTHSC ,
cardAUTHKEY = = 0 ? ' A ' : ' B ' ,
2019-10-16 06:03:53 +08:00
GetTickCountDelta ( authTimer )
2019-03-16 04:04:25 +08:00
) ;
}
LED_C_ON ( ) ;
2019-03-16 08:25:10 +08:00
cardSTATE = MFEMUL_WORK ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_AUTH1] cardSTATE = MFEMUL_WORK " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
// WRITE BL2
case MFEMUL_WRITEBL2 : {
2019-03-16 10:01:03 +08:00
if ( receivedCmd_len = = MAX_MIFARE_FRAME_SIZE ) {
2019-03-16 04:04:25 +08:00
mf_crypto1_decryptEx ( pcs , receivedCmd , receivedCmd_len , receivedCmd_dec ) ;
2019-04-12 05:35:02 +08:00
if ( CheckCrc14A ( receivedCmd_dec , receivedCmd_len ) ) {
2019-03-16 04:04:25 +08:00
if ( IsSectorTrailer ( cardWRBL ) ) {
emlGetMem ( response , cardWRBL , 1 ) ;
if ( ! IsAccessAllowed ( cardWRBL , cardAUTHKEY , AC_KEYA_WRITE ) ) {
2019-07-24 06:52:24 +08:00
memcpy ( receivedCmd_dec , response , 6 ) ; // don't change KeyA
2019-03-16 04:04:25 +08:00
}
if ( ! IsAccessAllowed ( cardWRBL , cardAUTHKEY , AC_KEYB_WRITE ) ) {
2019-07-24 06:52:24 +08:00
memcpy ( receivedCmd_dec + 10 , response + 10 , 6 ) ; // don't change KeyA
2019-03-16 04:04:25 +08:00
}
if ( ! IsAccessAllowed ( cardWRBL , cardAUTHKEY , AC_AC_WRITE ) ) {
2019-07-24 06:52:24 +08:00
memcpy ( receivedCmd_dec + 6 , response + 6 , 4 ) ; // don't change AC bits
2019-03-16 04:04:25 +08:00
}
} else {
if ( ! IsAccessAllowed ( cardWRBL , cardAUTHKEY , AC_DATA_WRITE ) ) {
2019-07-24 06:52:24 +08:00
memcpy ( receivedCmd_dec , response , 16 ) ; // don't change anything
2019-03-16 04:04:25 +08:00
}
}
emlSetMem ( receivedCmd_dec , cardWRBL , 1 ) ;
2019-07-24 06:52:24 +08:00
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_ACK ) ) ; // always ACK?
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
cardSTATE = MFEMUL_WORK ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WRITEBL2] cardSTATE = MFEMUL_WORK " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
}
2019-03-27 21:18:26 +08:00
cardSTATE_TO_IDLE ( ) ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_WRITEBL2] cardSTATE = MFEMUL_IDLE " ) ;
2019-04-12 05:10:52 +08:00
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-03-16 04:04:25 +08:00
break ;
}
// INC
case MFEMUL_INTREG_INC : {
if ( receivedCmd_len = = 6 ) {
mf_crypto1_decryptEx ( pcs , receivedCmd , receivedCmd_len , ( uint8_t * ) & ans ) ;
if ( emlGetValBl ( & cardINTREG , & cardINTBLOCK , cardWRBL ) ) {
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
cardSTATE_TO_IDLE ( ) ;
break ;
}
2019-04-12 05:10:52 +08:00
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-03-16 04:04:25 +08:00
cardINTREG = cardINTREG + ans ;
cardSTATE = MFEMUL_WORK ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_INTREG_INC] cardSTATE = MFEMUL_WORK " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
}
// DEC
case MFEMUL_INTREG_DEC : {
2019-03-27 21:18:26 +08:00
if ( receivedCmd_len = = 6 ) { // Data is encrypted
// Decrypted cmd
2019-03-16 04:04:25 +08:00
mf_crypto1_decryptEx ( pcs , receivedCmd , receivedCmd_len , ( uint8_t * ) & ans ) ;
if ( emlGetValBl ( & cardINTREG , & cardINTBLOCK , cardWRBL ) ) {
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
cardSTATE_TO_IDLE ( ) ;
break ;
}
}
2019-04-12 05:10:52 +08:00
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-03-16 04:04:25 +08:00
cardINTREG = cardINTREG - ans ;
cardSTATE = MFEMUL_WORK ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_INTREG_DEC] cardSTATE = MFEMUL_WORK " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
// REST
case MFEMUL_INTREG_REST : {
mf_crypto1_decryptEx ( pcs , receivedCmd , receivedCmd_len , ( uint8_t * ) & ans ) ;
if ( emlGetValBl ( & cardINTREG , & cardINTBLOCK , cardWRBL ) ) {
EmSend4bit ( mf_crypto1_encrypt4bit ( pcs , CARD_NACK_NA ) ) ;
2020-01-13 17:34:59 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
cardSTATE_TO_IDLE ( ) ;
break ;
}
2019-04-12 05:10:52 +08:00
LogTrace ( uart - > output , uart - > len , uart - > startTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > endTime * 16 - DELAY_AIR2ARM_AS_TAG , uart - > parity , true ) ;
2019-03-16 04:04:25 +08:00
cardSTATE = MFEMUL_WORK ;
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL > = DBG_EXTENDED ) Dbprintf ( " [MFEMUL_INTREG_REST] cardSTATE = MFEMUL_WORK " ) ;
2019-03-16 04:04:25 +08:00
break ;
}
} // End Switch Loop
button_pushed = BUTTON_PRESS ( ) ;
2019-03-16 04:07:15 +08:00
2019-03-16 04:04:25 +08:00
} // End While Loop
2020-04-16 15:53:31 +08:00
FpgaDisableTracing ( ) ;
2019-03-16 04:04:25 +08:00
// NR AR ATTACK
2020-04-21 07:56:51 +08:00
// mfkey32
2019-06-06 16:05:09 +08:00
if ( ( ( flags & FLAG_NR_AR_ATTACK ) = = FLAG_NR_AR_ATTACK ) & & ( DBGLEVEL > = DBG_INFO ) ) {
2019-07-24 06:52:24 +08:00
for ( uint8_t i = 0 ; i < ATTACK_KEY_COUNT ; i + + ) {
2019-03-16 04:04:25 +08:00
if ( ar_nr_collected [ i ] = = 2 ) {
Dbprintf ( " Collected two pairs of AR/NR which can be used to extract %s from reader for sector %d: " , ( i < ATTACK_KEY_COUNT / 2 ) ? " keyA " : " keyB " , ar_nr_resp [ i ] . sector ) ;
Dbprintf ( " ../tools/mfkey/mfkey32 %08x %08x %08x %08x %08x %08x " ,
ar_nr_resp [ i ] . cuid , //UID
ar_nr_resp [ i ] . nonce , //NT
ar_nr_resp [ i ] . nr , //NR1
ar_nr_resp [ i ] . ar , //AR1
ar_nr_resp [ i ] . nr2 , //NR2
ar_nr_resp [ i ] . ar2 //AR2
) ;
}
}
}
2020-04-21 07:56:51 +08:00
2020-04-16 15:53:31 +08:00
// mfkey32 v2
2019-07-24 06:52:24 +08:00
for ( uint8_t i = ATTACK_KEY_COUNT ; i < ATTACK_KEY_COUNT * 2 ; i + + ) {
2019-03-16 04:04:25 +08:00
if ( ar_nr_collected [ i ] = = 2 ) {
Dbprintf ( " Collected two pairs of AR/NR which can be used to extract %s from reader for sector %d: " , ( i < ATTACK_KEY_COUNT / 2 ) ? " keyA " : " keyB " , ar_nr_resp [ i ] . sector ) ;
Dbprintf ( " ../tools/mfkey/mfkey32v2 %08x %08x %08x %08x %08x %08x %08x " ,
ar_nr_resp [ i ] . cuid , //UID
ar_nr_resp [ i ] . nonce , //NT
ar_nr_resp [ i ] . nr , //NR1
ar_nr_resp [ i ] . ar , //AR1
ar_nr_resp [ i ] . nonce2 , //NT2
ar_nr_resp [ i ] . nr2 , //NR2
ar_nr_resp [ i ] . ar2 //AR2
) ;
}
}
2019-07-24 06:52:24 +08:00
if ( DBGLEVEL > = DBG_ERROR ) {
2019-03-16 04:07:15 +08:00
Dbprintf ( " Emulator stopped. Tracing: %d trace length: %d " , get_tracing ( ) , BigBuf_get_traceLen ( ) ) ;
}
2019-03-16 04:04:25 +08:00
2019-03-27 21:18:26 +08:00
if ( ( flags & FLAG_INTERACTIVE ) = = FLAG_INTERACTIVE ) { // Interactive mode flag, means we need to send ACK
//Send the collected ar_nr in the response
2020-01-13 17:34:59 +08:00
reply_mix ( CMD_ACK , CMD_HF_MIFARE_SIMULATE , button_pushed , 0 , & ar_nr_resp , sizeof ( ar_nr_resp ) ) ;
2019-03-27 21:18:26 +08:00
}
2019-03-16 04:04:25 +08:00
2019-03-16 04:07:15 +08:00
FpgaWriteConfWord ( FPGA_MAJOR_MODE_OFF ) ;
LEDsoff ( ) ;
set_tracing ( false ) ;
2019-04-10 04:07:17 +08:00
BigBuf_free_keep_EM ( ) ;
2019-03-16 04:04:25 +08:00
}