2011-05-18 20:33:32 +08:00
//-----------------------------------------------------------------------------
2018-05-03 17:00:15 +08:00
//-----------------------------------------------------------------------------
2011-05-18 20:33:32 +08:00
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>, Hagen Fritsch
// Copyright (C) 2011 Gerhard de Koning Gans
2014-06-29 04:57:48 +08:00
// Copyright (C) 2014 Midnitesnake & Andy Davies & Martin Holst Swende
2011-05-18 20:33:32 +08:00
//
// 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.
//-----------------------------------------------------------------------------
// High frequency iClass commands
//-----------------------------------------------------------------------------
# include "cmdhficlass.h"
2015-10-08 05:00:46 +08:00
2017-09-07 22:14:33 +08:00
# define NUM_CSNS 9
2015-10-08 05:00:46 +08:00
# define ICLASS_KEYS_MAX 8
2017-08-19 22:13:36 +08:00
static int CmdHelp ( const char * Cmd ) ;
2015-10-08 05:00:46 +08:00
static uint8_t iClass_Key_Table [ ICLASS_KEYS_MAX ] [ 8 ] = {
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }
} ;
2017-01-11 01:23:05 +08:00
int usage_hf_iclass_sim ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass sim <option> [CSN] " ) ;
PrintAndLogEx ( NORMAL , " options " ) ;
PrintAndLogEx ( NORMAL , " 0 <CSN> simulate the given CSN " ) ;
PrintAndLogEx ( NORMAL , " 1 simulate default CSN " ) ;
PrintAndLogEx ( NORMAL , " 2 Reader-attack, gather reader responses to extract elite key " ) ;
PrintAndLogEx ( NORMAL , " 3 Full simulation using emulator memory (see 'hf iclass eload') " ) ;
PrintAndLogEx ( NORMAL , " 4 Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass sim 0 031FEC8AF7FF12E0 " ) ;
PrintAndLogEx ( NORMAL , " hf iclass sim 2 " ) ;
PrintAndLogEx ( NORMAL , " hf iclass eload 'tagdump.bin' " ) ;
PrintAndLogEx ( NORMAL , " hf iclass sim 3 " ) ;
PrintAndLogEx ( NORMAL , " hf iclass sim 4 " ) ;
2017-01-11 01:23:05 +08:00
return 0 ;
}
int usage_hf_iclass_eload ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Loads iclass tag-dump into emulator memory on device " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass eload f <filename> " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass eload f iclass_tagdump-aa162d30f8ff12f1.bin " ) ;
2017-01-11 01:23:05 +08:00
return 0 ;
}
int usage_hf_iclass_decrypt ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " This is simple implementation, it tries to decrypt every block after block 6. " ) ;
PrintAndLogEx ( NORMAL , " Correct behaviour would be to decrypt only the application areas where the key is valid, " ) ;
PrintAndLogEx ( NORMAL , " which is defined by the configuration block. " ) ;
PrintAndLogEx ( NORMAL , " OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside " ) ;
PrintAndLogEx ( NORMAL , " in the working directory. The file should be 16 bytes binary data " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass decrypt f <tagdump> " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " S hf iclass decrypt f tagdump_12312342343.bin " ) ;
2018-02-02 01:24:38 +08:00
return 0 ;
2017-01-11 01:23:05 +08:00
}
int usage_hf_iclass_encrypt ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside " ) ;
PrintAndLogEx ( NORMAL , " in the working directory. The file should be 16 bytes binary data " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass encrypt <BlockData> " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass encrypt 0102030405060708 " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
2017-01-11 01:23:05 +08:00
return 0 ;
}
int usage_hf_iclass_dump ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass dump f <fileName> k <key> c <creditkey> [e|r|v] \n " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " f <filename> : specify a filename to save dump to " ) ;
PrintAndLogEx ( NORMAL , " k <key> : <required> access Key as 16 hex symbols or 1 hex to select key from memory " ) ;
PrintAndLogEx ( NORMAL , " c <creditkey>: credit key as 16 hex symbols or 1 hex to select key from memory " ) ;
PrintAndLogEx ( NORMAL , " e : elite computations applied to key " ) ;
PrintAndLogEx ( NORMAL , " r : raw, the key is interpreted as raw block 3/4 " ) ;
PrintAndLogEx ( NORMAL , " v : verbose output " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass dump k 001122334455667B " ) ;
PrintAndLogEx ( NORMAL , " hf iclass dump k AAAAAAAAAAAAAAAA c 001122334455667B " ) ;
PrintAndLogEx ( NORMAL , " hf iclass dump k AAAAAAAAAAAAAAAA e " ) ;
2017-01-11 01:23:05 +08:00
return 0 ;
}
int usage_hf_iclass_clone ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass clone f <tagfile.bin> b <first block> l <last block> k <KEY> c e|r " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " f <filename>: specify a filename to clone from " ) ;
PrintAndLogEx ( NORMAL , " b <Block> : The first block to clone as 2 hex symbols " ) ;
PrintAndLogEx ( NORMAL , " l <Last Blk>: Set the Data to write as 16 hex symbols " ) ;
PrintAndLogEx ( NORMAL , " k <Key> : Access Key as 16 hex symbols or 1 hex to select key from memory " ) ;
PrintAndLogEx ( NORMAL , " c : If 'c' is specified, the key set is assumed to be the credit key \n " ) ;
PrintAndLogEx ( NORMAL , " e : If 'e' is specified, elite computations applied to key " ) ;
PrintAndLogEx ( NORMAL , " r : If 'r' is specified, no computations applied to key " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass clone f iclass_tagdump-121345.bin b 06 l 1A k 1122334455667788 e " ) ;
PrintAndLogEx ( NORMAL , " hf iclass clone f iclass_tagdump-121345.bin b 05 l 19 k 0 " ) ;
PrintAndLogEx ( NORMAL , " hf iclass clone f iclass_tagdump-121345.bin b 06 l 19 k 0 e " ) ;
2018-02-02 01:24:38 +08:00
return 0 ;
2017-01-11 01:23:05 +08:00
}
int usage_hf_iclass_writeblock ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass writeblk b <block> d <data> k <key> [c|e|r|v] \n " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " b <Block> : The block number as 2 hex symbols " ) ;
PrintAndLogEx ( NORMAL , " d <data> : set the Data to write as 16 hex symbols " ) ;
PrintAndLogEx ( NORMAL , " k <Key> : access Key as 16 hex symbols or 1 hex to select key from memory " ) ;
PrintAndLogEx ( NORMAL , " c : credit key assumed \n " ) ;
PrintAndLogEx ( NORMAL , " e : elite computations applied to key " ) ;
PrintAndLogEx ( NORMAL , " r : raw, no computations applied to key " ) ;
PrintAndLogEx ( NORMAL , " v : verbose output " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass writeblk b 0A d AAAAAAAAAAAAAAAA k 001122334455667B " ) ;
PrintAndLogEx ( NORMAL , " hf iclass writeblk b 1B d AAAAAAAAAAAAAAAA k 001122334455667B c " ) ;
2017-01-11 01:23:05 +08:00
return 0 ;
}
int usage_hf_iclass_readblock ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass readblk b <block> k <key> [c|e|r|v] \n " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " b <block> : The block number as 2 hex symbols " ) ;
PrintAndLogEx ( NORMAL , " k <key> : Access Key as 16 hex symbols or 1 hex to select key from memory " ) ;
PrintAndLogEx ( NORMAL , " c : credit key assumed \n " ) ;
PrintAndLogEx ( NORMAL , " e : elite computations applied to key " ) ;
PrintAndLogEx ( NORMAL , " r : raw, no computations applied to key " ) ;
PrintAndLogEx ( NORMAL , " v : verbose output " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass readblk b 06 k 0011223344556677 " ) ;
PrintAndLogEx ( NORMAL , " hf iclass readblk b 1B k 0011223344556677 c " ) ;
PrintAndLogEx ( NORMAL , " hf iclass readblk b 0A k 0 " ) ;
2017-01-11 01:23:05 +08:00
return 0 ;
}
int usage_hf_iclass_readtagfile ( ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass readtagfile <filename> [startblock] [endblock] " ) ;
2018-02-02 01:24:38 +08:00
return 0 ;
2017-01-11 01:23:05 +08:00
}
int usage_hf_iclass_calc_newkey ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Calculate new key for updating \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass calc_newkey o <Old key> n <New key> s [csn] e " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " o <oldkey> : *specify a key as 16 hex symbols or a key number as 1 symbol " ) ;
PrintAndLogEx ( NORMAL , " n <newkey> : *specify a key as 16 hex symbols or a key number as 1 symbol " ) ;
PrintAndLogEx ( NORMAL , " s <csn> : specify a card Serial number to diversify the key (if omitted will attempt to read a csn) " ) ;
PrintAndLogEx ( NORMAL , " e : specify new key as elite calc " ) ;
PrintAndLogEx ( NORMAL , " ee : specify old and new key as elite calc " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " e key to e key given csn : hf iclass calcnewkey o 1122334455667788 n 2233445566778899 s deadbeafdeadbeaf ee " ) ;
PrintAndLogEx ( NORMAL , " std key to e key read csn : hf iclass calcnewkey o 1122334455667788 n 2233445566778899 e " ) ;
PrintAndLogEx ( NORMAL , " std to std read csn : hf iclass calcnewkey o 1122334455667788 n 2233445566778899 " ) ;
PrintAndLogEx ( NORMAL , " \n NOTE: * = required \n " ) ;
2018-02-02 01:24:38 +08:00
return 0 ;
2017-01-11 01:23:05 +08:00
}
int usage_hf_iclass_managekeys ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " HELP : Manage iClass Keys in client memory: \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass managekeys n [keynbr] k [key] f [filename] s l p \n " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " n <keynbr> : specify the keyNbr to set in memory " ) ;
PrintAndLogEx ( NORMAL , " k <key> : set a key in memory " ) ;
PrintAndLogEx ( NORMAL , " f <filename>: specify a filename to use with load or save operations " ) ;
PrintAndLogEx ( NORMAL , " s : save keys in memory to file specified by filename " ) ;
PrintAndLogEx ( NORMAL , " l : load keys to memory from file specified by filename " ) ;
PrintAndLogEx ( NORMAL , " p : print keys loaded into memory \n " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " set key : hf iclass managekeys n 0 k 1122334455667788 " ) ;
PrintAndLogEx ( NORMAL , " save key file: hf iclass managekeys f mykeys.bin s " ) ;
PrintAndLogEx ( NORMAL , " load key file: hf iclass managekeys f mykeys.bin l " ) ;
PrintAndLogEx ( NORMAL , " print keys : hf iclass managekeys p \n " ) ;
2017-01-11 01:23:05 +08:00
return 0 ;
}
2017-01-11 05:21:16 +08:00
int usage_hf_iclass_reader ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Act as a Iclass reader. Look for iClass tags until a key or the pm3 button is pressed \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass reader [h] [1] \n " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " h This help text " ) ;
PrintAndLogEx ( NORMAL , " 1 read only 1 tag " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass reader 1 " ) ;
2017-01-11 05:21:16 +08:00
return 0 ;
}
2017-08-18 16:23:46 +08:00
int usage_hf_iclass_replay ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Replay a collected mac message " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass replay [h] <mac> " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " h This help text " ) ;
PrintAndLogEx ( NORMAL , " <mac> Mac bytes to replay (8 hexsymbols) " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass replay 00112233 " ) ;
2017-01-11 05:21:16 +08:00
return 0 ;
}
2017-08-26 18:57:18 +08:00
int usage_hf_iclass_sniff ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Sniff the communication between reader and tag " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass sniff [h] " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass sniff " ) ;
2017-01-11 05:21:16 +08:00
return 0 ;
}
2017-08-18 16:23:46 +08:00
int usage_hf_iclass_loclass ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass loclass [options] " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " t Perform self-test " ) ;
PrintAndLogEx ( NORMAL , " f <filename> Bruteforce iclass dumpfile " ) ;
PrintAndLogEx ( NORMAL , " An iclass dumpfile is assumed to consist of an arbitrary number of " ) ;
PrintAndLogEx ( NORMAL , " malicious CSNs, and their protocol responses " ) ;
PrintAndLogEx ( NORMAL , " The binary format of the file is expected to be as follows: " ) ;
PrintAndLogEx ( NORMAL , " <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC> " ) ;
PrintAndLogEx ( NORMAL , " <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC> " ) ;
PrintAndLogEx ( NORMAL , " <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC> " ) ;
PrintAndLogEx ( NORMAL , " ... totalling N*24 bytes " ) ;
2017-08-18 16:23:46 +08:00
return 0 ;
}
2017-12-13 17:18:38 +08:00
int usage_hf_iclass_chk ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass chk [h|e|r] [f (*.dic)] " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " f <filename> Dictionary file with default iclass keys " ) ;
PrintAndLogEx ( NORMAL , " r raw " ) ;
PrintAndLogEx ( NORMAL , " e elite " ) ;
2018-07-01 04:47:07 +08:00
PrintAndLogEx ( NORMAL , " c credit key (if not use, default is debit) " ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass chk f default_iclass_keys.dic " ) ;
PrintAndLogEx ( NORMAL , " hf iclass chk f default_iclass_keys.dic e " ) ;
2017-12-13 17:18:38 +08:00
return 0 ;
}
2018-01-31 23:50:41 +08:00
int usage_hf_iclass_lookup ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass lookup [h|e|r] [f (*.dic)] [u <csn>] [p <epurse>] [m <macs>] " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " f <filename> Dictionary file with default iclass keys " ) ;
PrintAndLogEx ( NORMAL , " u CSN " ) ;
PrintAndLogEx ( NORMAL , " p EPURSE " ) ;
PrintAndLogEx ( NORMAL , " m macs " ) ;
PrintAndLogEx ( NORMAL , " r raw " ) ;
PrintAndLogEx ( NORMAL , " e elite " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass lookup u 9655a400f8ff12e0 p f0ffffffffffffff m 0000000089cb984b f default_iclass_keys.dic " ) ;
PrintAndLogEx ( NORMAL , " hf iclass lookup u 9655a400f8ff12e0 p f0ffffffffffffff m 0000000089cb984b f default_iclass_keys.dic e " ) ;
2018-01-31 23:50:41 +08:00
return 0 ;
}
2018-02-05 00:19:08 +08:00
int usage_hf_iclass_permutekey ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Permute function from 'heart of darkness' paper. " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass permute [h] <r|f> <bytes> " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " h This help " ) ;
PrintAndLogEx ( NORMAL , " r reverse permuted key " ) ;
PrintAndLogEx ( NORMAL , " f permute key " ) ;
PrintAndLogEx ( NORMAL , " <bytes> input bytes " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , " hf iclass permute r 0123456789abcdef " ) ;
2018-02-05 00:19:08 +08:00
return 0 ;
}
2017-08-18 16:23:46 +08:00
2015-10-08 05:00:46 +08:00
int xorbits_8 ( uint8_t val ) {
2015-02-15 04:17:08 +08:00
uint8_t res = val ^ ( val > > 1 ) ; //1st pass
res = res ^ ( res > > 1 ) ; // 2nd pass
res = res ^ ( res > > 2 ) ; // 3rd pass
res = res ^ ( res > > 4 ) ; // 4th pass
return res & 1 ;
2014-04-24 20:13:33 +08:00
}
2015-10-08 05:00:46 +08:00
int CmdHFiClassList ( const char * Cmd ) {
2018-02-21 14:47:33 +08:00
//PrintAndLogEx(NORMAL, "Deprecated command, use 'hf list iclass' instead");
2018-03-19 01:00:41 +08:00
CmdTraceList ( " iclass " ) ;
2014-04-24 20:13:33 +08:00
return 0 ;
}
2017-08-26 18:57:18 +08:00
int CmdHFiClassSniff ( const char * Cmd ) {
2017-01-11 05:21:16 +08:00
char cmdp = param_getchar ( Cmd , 0 ) ;
2017-08-26 18:57:18 +08:00
if ( cmdp = = ' h ' | | cmdp = = ' H ' ) return usage_hf_iclass_sniff ( ) ;
2015-02-15 04:17:08 +08:00
UsbCommand c = { CMD_SNOOP_ICLASS } ;
SendCommand ( & c ) ;
return 0 ;
2011-05-18 20:33:32 +08:00
}
2015-10-08 05:00:46 +08:00
int CmdHFiClassSim ( const char * Cmd ) {
2015-02-15 04:17:08 +08:00
2018-02-04 19:25:55 +08:00
char cmdp = param_getchar ( Cmd , 0 ) ;
if ( strlen ( Cmd ) < 1 | | cmdp = = ' H ' | | cmdp = = ' h ' ) return usage_hf_iclass_sim ( ) ;
2016-01-20 02:31:34 +08:00
2017-08-19 15:49:41 +08:00
uint8_t simType = 0 ;
uint8_t CSN [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2015-02-19 17:48:33 +08:00
simType = param_get8ex ( Cmd , 0 , 0 , 10 ) ;
2015-02-15 04:17:08 +08:00
2017-08-19 15:49:41 +08:00
if ( simType = = 0 ) {
2015-02-15 04:17:08 +08:00
if ( param_gethex ( Cmd , 1 , CSN , 16 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " A CSN should consist of 16 HEX symbols " ) ;
2015-02-18 05:07:40 +08:00
return usage_hf_iclass_sim ( ) ;
2015-02-15 04:17:08 +08:00
}
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " --simtype:%02x csn:%s " , simType , sprint_hex ( CSN , 8 ) ) ;
2015-02-15 04:17:08 +08:00
}
2016-01-20 02:31:34 +08:00
2017-09-12 14:45:38 +08:00
if ( simType > 4 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Undefined simptype %d " , simType ) ;
2015-02-18 05:07:40 +08:00
return usage_hf_iclass_sim ( ) ;
2015-02-15 04:17:08 +08:00
}
2012-06-28 21:38:40 +08:00
2017-08-19 15:49:41 +08:00
uint8_t numberOfCSNs = 0 ;
2017-09-12 14:45:38 +08:00
2017-08-19 22:13:36 +08:00
/*
2017-08-18 16:23:46 +08:00
// pre-defined 8 CSN by Holiman
uint8_t csns [ 8 * NUM_CSNS ] = {
2017-08-19 22:13:36 +08:00
0x00 , 0x0B , 0x0F , 0xFF , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x13 , 0x94 , 0x7E , 0x76 , 0xFF , 0x12 , 0xE0 ,
0x2A , 0x99 , 0xAC , 0x79 , 0xEC , 0xFF , 0x12 , 0xE0 ,
0x17 , 0x12 , 0x01 , 0xFD , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0xCD , 0x56 , 0x01 , 0x7C , 0x6F , 0xFF , 0x12 , 0xE0 ,
0x4B , 0x5E , 0x0B , 0x72 , 0xEF , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x73 , 0xD8 , 0x75 , 0x58 , 0xFF , 0x12 , 0xE0 ,
0x0C , 0x90 , 0x32 , 0xF3 , 0x5D , 0xFF , 0x12 , 0xE0
} ;
*/
/*
2017-09-07 22:14:33 +08:00
pre - defined 9 CSN by iceman
2017-08-19 22:13:36 +08:00
only one csn depend on several others .
six depends only on the first csn , ( 0 , 1 , 0x45 )
*/
uint8_t csns [ 8 * NUM_CSNS ] = {
0x01 , 0x0A , 0x0F , 0xFF , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x0C , 0x06 , 0x0C , 0xFE , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x10 , 0x97 , 0x83 , 0x7B , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x13 , 0x97 , 0x82 , 0x7A , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x07 , 0x0E , 0x0D , 0xF9 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x14 , 0x96 , 0x84 , 0x76 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x17 , 0x96 , 0x85 , 0x71 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
2017-09-07 22:14:33 +08:00
0xCE , 0xC5 , 0x0F , 0x77 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0xD2 , 0x5A , 0x82 , 0xF8 , 0xF7 , 0xFF , 0x12 , 0xE0
//0x04, 0x08, 0x9F, 0x78, 0x6E, 0xFF, 0x12, 0xE0
2017-08-18 16:23:46 +08:00
} ;
2017-08-19 22:13:36 +08:00
/*
2017-08-18 16:23:46 +08:00
// pre-defined 15 CSN by Carl55
2017-08-19 22:13:36 +08:00
// remember to change the define NUM_CSNS to match.
2015-02-15 04:17:08 +08:00
uint8_t csns [ 8 * NUM_CSNS ] = {
2017-08-19 22:13:36 +08:00
0x00 , 0x0B , 0x0F , 0xFF , 0xF7 , 0xFF , 0x12 , 0xE0 ,
2015-02-15 04:17:08 +08:00
0x00 , 0x04 , 0x0E , 0x08 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x09 , 0x0D , 0x05 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x0A , 0x0C , 0x06 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x0F , 0x0B , 0x03 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x08 , 0x0A , 0x0C , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x0D , 0x09 , 0x09 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x0E , 0x08 , 0x0A , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x03 , 0x07 , 0x17 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x3C , 0x06 , 0xE0 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x01 , 0x05 , 0x1D , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x02 , 0x04 , 0x1E , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x07 , 0x03 , 0x1B , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0x00 , 0x00 , 0x02 , 0x24 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
2017-08-18 16:23:46 +08:00
0x00 , 0x05 , 0x01 , 0x21 , 0xF7 , 0xFF , 0x12 , 0xE0
} ;
*/
2017-09-12 14:45:38 +08:00
/* DUMPFILE FORMAT:
*
* < 8 - byte CSN > < 8 - byte CC > < 4 byte NR > < 4 byte MAC > . . . .
* So , it should wind up as
* 8 * 24 bytes .
*
* The returndata from the pm3 is on the following format
* < 4 byte NR > < 4 byte MAC >
* CC are all zeroes , CSN is the same as was sent in
* */
2018-02-01 23:10:24 +08:00
uint8_t tries = 0 ;
2017-09-12 14:45:38 +08:00
switch ( simType ) {
case 2 : {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( INFO , " Starting iCLASS sim 2 attack (elite mode) " ) ;
PrintAndLogEx ( INFO , " press keyboard to cancel " ) ;
2017-09-12 14:45:38 +08:00
UsbCommand c = { CMD_SIMULATE_TAG_ICLASS , { simType , NUM_CSNS } } ;
UsbCommand resp = { 0 } ;
2018-02-04 07:52:29 +08:00
memcpy ( c . d . asBytes , csns , 8 * NUM_CSNS ) ;
2017-09-12 14:45:38 +08:00
clearCommandBuffer ( ) ;
SendCommand ( & c ) ;
2018-02-01 22:55:12 +08:00
2018-02-01 23:10:24 +08:00
while ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
tries + + ;
2018-02-01 22:55:12 +08:00
if ( ukbhit ( ) ) {
int gc = getchar ( ) ; ( void ) gc ;
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n aborted via keyboard. " ) ;
2018-02-01 22:55:12 +08:00
return 0 ;
}
2018-02-01 23:10:24 +08:00
if ( tries > 20 ) {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n timeout while waiting for reply. " ) ;
2018-02-01 22:55:12 +08:00
return 0 ;
}
}
2018-02-02 00:53:28 +08:00
uint8_t num_mac = resp . arg [ 1 ] ;
bool success = ( NUM_CSNS = = num_mac ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " [%c] %d out of %d MAC obtained [%s] " , ( success ) ? ' + ' : ' ! ' , num_mac , NUM_CSNS , ( success ) ? " OK " : " FAIL " ) ;
2012-06-28 21:38:40 +08:00
2018-02-02 00:53:28 +08:00
if ( num_mac = = 0 )
2017-09-14 19:05:46 +08:00
break ;
2017-09-18 21:31:53 +08:00
size_t datalen = NUM_CSNS * 24 ;
2017-09-12 14:45:38 +08:00
void * dump = malloc ( datalen ) ;
if ( ! dump ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
2017-09-12 14:45:38 +08:00
return 2 ;
}
2018-02-04 07:52:29 +08:00
memset ( dump , 0 , datalen ) ; //<-- Need zeroes for the EPURSE - field (offical)
2017-09-12 14:45:38 +08:00
uint8_t i = 0 ;
for ( i = 0 ; i < NUM_CSNS ; i + + ) {
2017-09-18 21:31:53 +08:00
//copy CSN
memcpy ( dump + i * 24 , csns + i * 8 , 8 ) ;
2018-02-04 07:52:29 +08:00
//copy epurse
memcpy ( dump + i * 24 + 8 , resp . d . asBytes + i * 16 , 8 ) ;
// NR_MAC (eight bytes from the response) ( 8b csn + 8b epurse == 16)
memcpy ( dump + i * 24 + 16 , resp . d . asBytes + i * 16 + 8 , 8 ) ;
2017-09-12 14:45:38 +08:00
}
/** Now, save to dumpfile **/
saveFile ( " iclass_mac_attack " , " bin " , dump , datalen ) ;
free ( dump ) ;
break ;
2014-06-17 03:27:12 +08:00
}
2017-12-24 17:59:24 +08:00
case 4 : {
2018-02-02 04:39:34 +08:00
// reader in key roll mode, when it has two keys it alternates when trying to verify.
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( INFO , " Starting iCLASS sim 4 attack (elite mode, reader in key roll mode) " ) ;
PrintAndLogEx ( INFO , " press keyboard to cancel " ) ;
2017-09-12 14:45:38 +08:00
UsbCommand c = { CMD_SIMULATE_TAG_ICLASS , { simType , NUM_CSNS } } ;
UsbCommand resp = { 0 } ;
memcpy ( c . d . asBytes , csns , 8 * NUM_CSNS ) ;
clearCommandBuffer ( ) ;
SendCommand ( & c ) ;
2018-02-01 22:55:12 +08:00
2018-02-01 23:10:24 +08:00
while ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
tries + + ;
2018-02-01 22:55:12 +08:00
if ( ukbhit ( ) ) {
int gc = getchar ( ) ; ( void ) gc ;
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n aborted via keyboard. " ) ;
2018-02-01 22:55:12 +08:00
return 0 ;
}
2018-02-01 23:10:24 +08:00
if ( tries > 20 ) {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n timeout while waiting for reply. " ) ;
2018-02-01 22:55:12 +08:00
return 0 ;
}
2017-09-12 14:45:38 +08:00
}
2018-02-02 01:01:44 +08:00
uint8_t num_mac = resp . arg [ 1 ] ;
2018-02-02 00:53:28 +08:00
bool success = ( ( NUM_CSNS * 2 ) = = num_mac ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " [%c] %d out of %d MAC obtained [%s] " , ( success ) ? ' + ' : ' ! ' , num_mac , NUM_CSNS * 2 , ( success ) ? " OK " : " FAIL " ) ;
2017-09-12 14:45:38 +08:00
2018-02-02 00:53:28 +08:00
if ( num_mac = = 0 )
2017-09-14 19:05:46 +08:00
break ;
2017-09-18 21:31:53 +08:00
size_t datalen = NUM_CSNS * 24 ;
2017-09-12 14:45:38 +08:00
void * dump = malloc ( datalen ) ;
if ( ! dump ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
2017-09-12 14:45:38 +08:00
return 2 ;
}
2017-09-18 21:31:53 +08:00
# define MAC_ITEM_SIZE 24
2017-09-12 14:45:38 +08:00
//KEYROLL 1
//Need zeroes for the CC-field
memset ( dump , 0 , datalen ) ;
for ( uint8_t i = 0 ; i < NUM_CSNS ; i + + ) {
2018-02-04 07:52:29 +08:00
// copy CSN
2017-09-18 21:31:53 +08:00
memcpy ( dump + i * MAC_ITEM_SIZE , csns + i * 8 , 8 ) ; //CSN
2018-02-04 07:52:29 +08:00
// copy EPURSE
memcpy ( dump + i * MAC_ITEM_SIZE + 8 , resp . d . asBytes + i * 16 , 8 ) ;
// copy NR_MAC (eight bytes from the response) ( 8b csn + 8b epurse == 16)
memcpy ( dump + i * MAC_ITEM_SIZE + 16 , resp . d . asBytes + i * 16 + 8 , 8 ) ;
2017-09-12 14:45:38 +08:00
}
saveFile ( " iclass_mac_attack_keyroll_A " , " bin " , dump , datalen ) ;
//KEYROLL 2
memset ( dump , 0 , datalen ) ;
2017-09-18 21:31:53 +08:00
uint8_t resp_index = 0 ;
for ( uint8_t i = 0 ; i < NUM_CSNS ; i + + ) {
2018-02-04 07:52:29 +08:00
resp_index = ( i + NUM_CSNS ) * 16 ;
2017-09-18 21:31:53 +08:00
// Copy CSN
memcpy ( dump + i * MAC_ITEM_SIZE , csns + i * 8 , 8 ) ;
2018-02-04 07:52:29 +08:00
// copy EPURSE
memcpy ( dump + i * MAC_ITEM_SIZE + 8 , resp . d . asBytes + resp_index , 8 ) ;
// copy NR_MAC (eight bytes from the response) ( 8b csn + 8 epurse == 16)
memcpy ( dump + i * MAC_ITEM_SIZE + 16 , resp . d . asBytes + resp_index + 8 , 8 ) ;
2017-09-18 21:31:53 +08:00
resp_index + + ;
2017-09-12 14:45:38 +08:00
}
2018-02-02 00:58:50 +08:00
saveFile ( " iclass_mac_attack_keyroll_B " , " bin " , dump , datalen ) ;
2017-09-12 14:45:38 +08:00
free ( dump ) ;
break ;
}
case 1 :
case 3 :
default : {
UsbCommand c = { CMD_SIMULATE_TAG_ICLASS , { simType , numberOfCSNs } } ;
memcpy ( c . d . asBytes , CSN , 8 ) ;
clearCommandBuffer ( ) ;
SendCommand ( & c ) ;
break ;
}
2014-06-17 03:27:12 +08:00
}
2015-02-15 04:17:08 +08:00
return 0 ;
2012-06-28 21:38:40 +08:00
}
2015-10-08 05:00:46 +08:00
int HFiClassReader ( const char * Cmd , bool loop , bool verbose ) {
2015-05-13 15:13:42 +08:00
bool tagFound = false ;
2018-02-08 19:18:40 +08:00
uint32_t flags = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC | FLAG_ICLASS_READER_AIA |
FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_ONLY_ONCE |
FLAG_ICLASS_READER_ONE_TRY ;
UsbCommand c = { CMD_READER_ICLASS , { flags , 0 , 0 } } ;
2015-10-08 05:00:46 +08:00
// loop in client not device - else on windows have a communication error
2015-02-15 04:17:08 +08:00
UsbCommand resp ;
2017-07-07 18:38:49 +08:00
while ( ! ukbhit ( ) ) {
2017-07-30 15:17:48 +08:00
2016-01-20 02:31:34 +08:00
clearCommandBuffer ( ) ;
2015-10-08 05:00:46 +08:00
SendCommand ( & c ) ;
2015-05-13 04:45:48 +08:00
if ( WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
2016-01-20 02:31:34 +08:00
uint8_t readStatus = resp . arg [ 0 ] & 0xff ;
2015-05-13 04:45:48 +08:00
uint8_t * data = resp . d . asBytes ;
2015-02-15 04:17:08 +08:00
2018-02-21 14:47:33 +08:00
if ( verbose ) PrintAndLogEx ( NORMAL , " Readstatus:%02x " , readStatus ) ;
2017-07-07 18:38:49 +08:00
// no tag found or button pressed
2018-02-08 19:15:47 +08:00
if ( ( readStatus = = 0 & & ! loop ) | | readStatus = = 0xFF ) {
2017-07-07 18:38:49 +08:00
// abort
2018-02-08 19:39:35 +08:00
if ( verbose ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " Quitting... " ) ;
2018-02-08 19:34:13 +08:00
DropField ( ) ;
return 0 ;
}
2015-02-15 04:17:08 +08:00
}
2015-05-13 15:13:42 +08:00
if ( readStatus & FLAG_ICLASS_READER_CSN ) {
2018-07-01 00:34:58 +08:00
PrintAndLogEx ( NORMAL , " CSN: %s " , sprint_hex ( data , 8 ) ) ;
2015-05-13 15:13:42 +08:00
tagFound = true ;
}
2018-02-08 19:15:47 +08:00
if ( readStatus & FLAG_ICLASS_READER_CC ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " CC: %s " , sprint_hex ( data + 16 , 8 ) ) ;
2017-07-30 15:17:48 +08:00
}
2018-02-08 19:15:47 +08:00
if ( readStatus & FLAG_ICLASS_READER_CONF ) {
2017-07-30 15:17:48 +08:00
printIclassDumpInfo ( data ) ;
}
2017-12-21 19:42:32 +08:00
if ( readStatus & FLAG_ICLASS_READER_AIA ) {
bool legacy = ( memcmp ( ( uint8_t * ) ( data + 8 * 5 ) , " \xff \xff \xff \xff \xff \xff \xff \xff " , 8 ) = = 0 ) ;
2018-09-15 18:58:50 +08:00
bool se_enabled = ( memcmp ( ( uint8_t * ) ( data + 8 * 5 ) , " \xff \xff \xff \x00 \x06 \xff \xff \xff " , 8 ) = = 0 ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " App IA: %s " , sprint_hex ( data + 8 * 5 , 8 ) ) ;
2018-07-01 00:34:58 +08:00
if ( legacy )
2018-09-15 18:58:50 +08:00
PrintAndLogEx ( SUCCESS , " : Possible iClass (legacy credential tag) " ) ;
else if ( se_enabled )
PrintAndLogEx ( SUCCESS , " : Possible iClass (SE credential tag) " ) ;
2018-07-01 00:34:58 +08:00
else
PrintAndLogEx ( WARNING , " : Possible iClass (NOT legacy tag) " ) ;
2017-07-30 15:17:48 +08:00
}
2018-02-08 19:34:13 +08:00
if ( tagFound & & ! loop ) {
DropField ( ) ;
return 1 ;
}
2015-02-15 04:17:08 +08:00
} else {
2018-02-08 19:39:35 +08:00
if ( verbose )
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
2015-02-15 04:17:08 +08:00
}
2015-05-13 15:13:42 +08:00
if ( ! loop ) break ;
2015-02-15 04:17:08 +08:00
}
2018-02-08 19:34:13 +08:00
DropField ( ) ;
2015-02-15 04:17:08 +08:00
return 0 ;
2015-05-13 15:13:42 +08:00
}
2017-01-11 05:21:16 +08:00
int CmdHFiClassReader ( const char * Cmd ) {
char cmdp = param_getchar ( Cmd , 0 ) ;
if ( cmdp = = ' h ' | | cmdp = = ' H ' ) return usage_hf_iclass_reader ( ) ;
2017-07-15 02:54:11 +08:00
bool findone = ( cmdp = = ' 1 ' ) ? false : true ;
2017-01-11 05:21:16 +08:00
return HFiClassReader ( Cmd , findone , true ) ;
2014-04-15 18:47:01 +08:00
}
2015-10-08 05:00:46 +08:00
int CmdHFiClassReader_Replay ( const char * Cmd ) {
2018-02-04 19:25:55 +08:00
char cmdp = param_getchar ( Cmd , 0 ) ;
if ( strlen ( Cmd ) < 1 | | cmdp = = ' H ' | | cmdp = = ' h ' ) return usage_hf_iclass_replay ( ) ;
2015-02-15 04:17:08 +08:00
uint8_t readerType = 0 ;
2018-02-04 19:25:55 +08:00
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
2015-02-15 04:17:08 +08:00
if ( param_gethex ( Cmd , 0 , MAC , 8 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " MAC must include 8 HEX symbols " ) ;
2015-02-15 04:17:08 +08:00
return 1 ;
}
2014-04-15 18:47:01 +08:00
2015-02-15 04:17:08 +08:00
UsbCommand c = { CMD_READER_ICLASS_REPLAY , { readerType } } ;
memcpy ( c . d . asBytes , MAC , 4 ) ;
2016-01-20 02:31:34 +08:00
clearCommandBuffer ( ) ;
2015-02-15 04:17:08 +08:00
SendCommand ( & c ) ;
return 0 ;
2012-06-28 21:38:40 +08:00
}
2015-10-08 05:00:46 +08:00
int iclassEmlSetMem ( uint8_t * data , int blockNum , int blocksCount ) {
UsbCommand c = { CMD_MIFARE_EML_MEMSET , { blockNum , blocksCount , 0 } } ;
memcpy ( c . d . asBytes , data , blocksCount * 16 ) ;
2016-01-20 02:31:34 +08:00
clearCommandBuffer ( ) ;
2015-02-15 04:17:08 +08:00
SendCommand ( & c ) ;
return 0 ;
2012-06-28 21:38:40 +08:00
}
2015-10-08 05:00:46 +08:00
int CmdHFiClassELoad ( const char * Cmd ) {
2015-02-15 04:15:53 +08:00
2018-01-02 20:02:55 +08:00
char ctmp = param_getchar ( Cmd , 0 ) ;
if ( strlen ( Cmd ) < 1 | | ctmp = = ' h ' | | ctmp = = ' H ' ) return usage_hf_iclass_eload ( ) ;
if ( ctmp ! = ' f ' & & ctmp ! = ' F ' ) return usage_hf_iclass_eload ( ) ;
2015-02-15 04:15:53 +08:00
//File handling and reading
FILE * f ;
char filename [ FILE_PATH_SIZE ] ;
2018-01-02 20:02:55 +08:00
if ( param_getstr ( Cmd , 1 , filename , FILE_PATH_SIZE ) > = FILE_PATH_SIZE ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " Filename too long " ) ;
2018-01-02 20:02:55 +08:00
return 1 ;
2015-02-15 04:15:53 +08:00
}
2018-01-02 20:02:55 +08:00
f = fopen ( filename , " rb " ) ;
if ( ! f ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " File: %s: not found or locked. " , filename ) ;
2015-02-15 04:15:53 +08:00
return 1 ;
}
2018-01-02 20:02:55 +08:00
// get filesize in order to malloc memory
2015-02-15 04:15:53 +08:00
fseek ( f , 0 , SEEK_END ) ;
long fsize = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
2016-01-13 05:43:28 +08:00
if ( fsize < 0 ) {
2018-02-22 22:59:55 +08:00
PrintAndLogDevice ( WARNING , " error, when getting filesize " ) ;
2016-01-13 05:47:48 +08:00
fclose ( f ) ;
2016-01-13 05:43:28 +08:00
return 1 ;
}
2018-05-04 01:42:16 +08:00
uint8_t * dump = calloc ( fsize , sizeof ( uint8_t ) ) ;
2018-01-02 20:02:55 +08:00
if ( ! dump ) {
2018-02-22 22:59:55 +08:00
PrintAndLogDevice ( WARNING , " error, cannot allocate memory " ) ;
2018-01-02 20:02:55 +08:00
fclose ( f ) ;
return 1 ;
}
2015-02-15 04:15:53 +08:00
size_t bytes_read = fread ( dump , 1 , fsize , f ) ;
fclose ( f ) ;
2015-02-19 17:48:33 +08:00
printIclassDumpInfo ( dump ) ;
2015-02-15 04:15:53 +08:00
//Validate
2016-01-20 02:31:34 +08:00
if ( bytes_read < fsize ) {
2018-02-22 22:59:55 +08:00
PrintAndLogDevice ( WARNING , " error, could only read %d bytes (should be %d) " , bytes_read , fsize ) ;
2015-02-15 04:15:53 +08:00
free ( dump ) ;
return 1 ;
}
//Send to device
uint32_t bytes_sent = 0 ;
uint32_t bytes_remaining = bytes_read ;
2018-01-02 20:02:55 +08:00
while ( bytes_remaining > 0 ) {
2015-02-15 04:15:53 +08:00
uint32_t bytes_in_packet = MIN ( USB_CMD_DATA_SIZE , bytes_remaining ) ;
2018-01-02 20:02:55 +08:00
UsbCommand c = { CMD_ICLASS_EML_MEMSET , { bytes_sent , bytes_in_packet , 0 } } ;
2018-05-03 17:00:15 +08:00
memcpy ( c . d . asBytes , dump + bytes_sent , bytes_in_packet ) ;
2016-01-20 02:31:34 +08:00
clearCommandBuffer ( ) ;
2015-02-15 04:15:53 +08:00
SendCommand ( & c ) ;
bytes_remaining - = bytes_in_packet ;
bytes_sent + = bytes_in_packet ;
}
free ( dump ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " sent %d bytes of data to device emulator memory " , bytes_sent ) ;
2015-02-15 04:15:53 +08:00
return 0 ;
}
2015-10-08 05:00:46 +08:00
static int readKeyfile ( const char * filename , size_t len , uint8_t * buffer ) {
2015-02-18 05:07:40 +08:00
FILE * f = fopen ( filename , " rb " ) ;
2018-01-09 23:31:54 +08:00
if ( ! f ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Failed to read from file '%s' " , filename ) ;
2015-02-18 05:07:40 +08:00
return 1 ;
}
fseek ( f , 0 , SEEK_END ) ;
long fsize = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
size_t bytes_read = fread ( buffer , 1 , len , f ) ;
fclose ( f ) ;
2016-01-20 02:31:34 +08:00
2018-01-09 23:31:54 +08:00
if ( fsize ! = len ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Warning, file size is %d, expected %d " , fsize , len ) ;
2015-02-18 05:07:40 +08:00
return 1 ;
}
2016-01-20 02:31:34 +08:00
2018-01-09 23:31:54 +08:00
if ( bytes_read ! = len ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Warning, could only read %d bytes, expected %d " , bytes_read , len ) ;
2015-02-18 05:07:40 +08:00
return 1 ;
}
return 0 ;
}
2015-10-08 05:00:46 +08:00
int CmdHFiClassDecrypt ( const char * Cmd ) {
2017-01-11 05:21:16 +08:00
char opt = param_getchar ( Cmd , 0 ) ;
if ( strlen ( Cmd ) < 1 | | opt = = ' h ' | | opt = = ' H ' ) return usage_hf_iclass_decrypt ( ) ;
2015-02-18 05:07:40 +08:00
uint8_t key [ 16 ] = { 0 } ;
2018-02-04 19:25:55 +08:00
if ( readKeyfile ( " iclass_decryptionkey.bin " , 16 , key ) ) return usage_hf_iclass_decrypt ( ) ;
2017-01-11 01:23:05 +08:00
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " decryption key loaded from file " ) ;
2015-02-18 05:07:40 +08:00
//Open the tagdump-file
FILE * f ;
char filename [ FILE_PATH_SIZE ] ;
2017-11-12 06:23:01 +08:00
if ( opt = = ' f ' & & param_getstr ( Cmd , 1 , filename , sizeof ( filename ) ) > 0 ) {
2017-01-12 06:01:15 +08:00
f = fopen ( filename , " rb " ) ;
if ( ! f ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " could not find file %s " , filename ) ;
2016-01-13 05:57:23 +08:00
return 1 ;
2016-01-20 02:31:34 +08:00
}
2016-01-13 05:57:23 +08:00
} else {
2015-02-18 05:07:40 +08:00
return usage_hf_iclass_decrypt ( ) ;
2016-01-13 05:57:23 +08:00
}
2015-02-18 05:07:40 +08:00
fseek ( f , 0 , SEEK_END ) ;
long fsize = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
2017-01-11 01:23:05 +08:00
if ( fsize < 0 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " error, when getting filesize " ) ;
2017-01-11 01:23:05 +08:00
fclose ( f ) ;
return 2 ;
}
2018-05-04 01:42:16 +08:00
uint8_t * decrypted = calloc ( fsize , sizeof ( uint8_t ) ) ;
if ( ! decrypted ) {
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
fclose ( f ) ;
return 1 ;
}
2017-01-11 01:23:05 +08:00
size_t bytes_read = fread ( decrypted , 1 , fsize , f ) ;
fclose ( f ) ;
if ( bytes_read = = 0 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " file reading error " ) ;
2017-01-11 01:23:05 +08:00
free ( decrypted ) ;
return 3 ;
}
2015-02-18 05:07:40 +08:00
2017-01-11 01:23:05 +08:00
picopass_hdr * hdr = ( picopass_hdr * ) decrypted ;
uint8_t mem = hdr - > conf . mem_config ;
uint8_t chip = hdr - > conf . chip_config ;
uint8_t applimit = hdr - > conf . app_limit ;
uint8_t kb = 2 ;
uint8_t app_areas = 2 ;
uint8_t max_blk = 31 ;
getMemConfig ( mem , chip , & max_blk , & app_areas , & kb ) ;
2015-02-18 05:07:40 +08:00
//Use the first block (CSN) for filename
2017-01-11 01:23:05 +08:00
char outfilename [ FILE_PATH_SIZE ] = { 0 } ;
snprintf ( outfilename , FILE_PATH_SIZE , " iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x-decrypted " ,
hdr - > csn [ 0 ] , hdr - > csn [ 1 ] , hdr - > csn [ 2 ] , hdr - > csn [ 3 ] ,
hdr - > csn [ 4 ] , hdr - > csn [ 5 ] , hdr - > csn [ 6 ] , hdr - > csn [ 7 ] ) ;
2015-02-18 05:07:40 +08:00
2017-01-11 01:23:05 +08:00
// tripledes
des3_context ctx = { DES_DECRYPT , { 0 } } ;
des3_set2key_dec ( & ctx , key ) ;
uint8_t enc_dump [ 8 ] = { 0 } ;
uint8_t empty [ 8 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } ;
for ( uint16_t blocknum = 0 ; blocknum < applimit ; + + blocknum ) {
uint8_t idx = blocknum * 8 ;
memcpy ( enc_dump , decrypted + idx , 8 ) ;
// block 7 or higher, and not empty 0xFF
if ( blocknum > 6 & & memcmp ( enc_dump , empty , 8 ) ! = 0 ) {
des3_crypt_ecb ( & ctx , enc_dump , decrypted + idx ) ;
2015-02-18 05:07:40 +08:00
}
}
2017-01-11 01:23:05 +08:00
saveFile ( outfilename , " bin " , decrypted , fsize ) ;
2018-02-04 19:25:55 +08:00
free ( decrypted ) ;
2017-01-11 01:23:05 +08:00
printIclassDumpContents ( decrypted , 1 , ( fsize / 8 ) , fsize ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
static int iClassEncryptBlkData ( uint8_t * blkData ) {
uint8_t key [ 16 ] = { 0 } ;
2018-02-02 01:24:38 +08:00
if ( readKeyfile ( " iclass_decryptionkey.bin " , 16 , key ) ) {
2015-10-08 05:00:46 +08:00
usage_hf_iclass_encrypt ( ) ;
return 1 ;
}
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " decryption file found " ) ;
2015-10-08 05:00:46 +08:00
uint8_t encryptedData [ 16 ] ;
uint8_t * encrypted = encryptedData ;
des3_context ctx = { DES_DECRYPT , { 0 } } ;
des3_set2key_enc ( & ctx , key ) ;
des3_crypt_ecb ( & ctx , blkData , encrypted ) ;
memcpy ( blkData , encrypted , 8 ) ;
return 1 ;
}
2015-02-15 04:17:08 +08:00
2015-10-08 05:00:46 +08:00
int CmdHFiClassEncryptBlk ( const char * Cmd ) {
uint8_t blkData [ 8 ] = { 0 } ;
char opt = param_getchar ( Cmd , 0 ) ;
2016-01-20 02:31:34 +08:00
if ( strlen ( Cmd ) < 1 | | opt = = ' h ' | | opt = = ' H ' ) return usage_hf_iclass_encrypt ( ) ;
2015-10-08 05:00:46 +08:00
//get the bytes to encrypt
2016-01-20 02:31:34 +08:00
if ( param_gethex ( Cmd , 0 , blkData , 16 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " BlockData must include 16 HEX symbols " ) ;
2015-02-15 04:17:08 +08:00
return 0 ;
}
2015-10-08 05:00:46 +08:00
if ( ! iClassEncryptBlkData ( blkData ) ) return 0 ;
2015-02-15 04:17:08 +08:00
2015-10-08 05:00:46 +08:00
printvar ( " encrypted block " , blkData , 8 ) ;
return 1 ;
}
void Calc_wb_mac ( uint8_t blockno , uint8_t * data , uint8_t * div_key , uint8_t MAC [ 4 ] ) {
2018-02-02 01:24:38 +08:00
uint8_t wb [ 9 ] ;
wb [ 0 ] = blockno ;
memcpy ( wb + 1 , data , 8 ) ;
doMAC_N ( wb , sizeof ( wb ) , div_key , MAC ) ;
2015-10-08 05:00:46 +08:00
}
static bool select_only ( uint8_t * CSN , uint8_t * CCNR , bool use_credit_key , bool verbose ) {
UsbCommand resp ;
UsbCommand c = { CMD_READER_ICLASS , { 0 } } ;
c . arg [ 0 ] = FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_CC | FLAG_ICLASS_READER_ONE_TRY ;
2017-12-15 22:37:00 +08:00
2015-10-08 05:00:46 +08:00
if ( use_credit_key )
c . arg [ 0 ] | = FLAG_ICLASS_READER_CEDITKEY ;
clearCommandBuffer ( ) ;
SendCommand ( & c ) ;
2017-12-21 17:13:40 +08:00
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4000 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
2015-10-08 05:00:46 +08:00
return false ;
2015-02-15 04:17:08 +08:00
}
2015-10-08 05:00:46 +08:00
uint8_t isOK = resp . arg [ 0 ] & 0xff ;
uint8_t * data = resp . d . asBytes ;
2017-12-15 22:37:00 +08:00
memcpy ( CSN , data , 8 ) ;
2016-01-20 02:31:34 +08:00
2017-12-15 22:37:00 +08:00
if ( CCNR ! = NULL )
memcpy ( CCNR , data + 16 , 8 ) ;
2016-01-20 02:31:34 +08:00
2018-02-04 19:25:55 +08:00
if ( isOK > 0 & & verbose ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " CSN | %s " , sprint_hex ( CSN , 8 ) ) ;
PrintAndLogEx ( SUCCESS , " CCNR | %s " , sprint_hex ( CCNR , 8 ) ) ;
2018-02-04 07:52:29 +08:00
}
2016-01-20 02:31:34 +08:00
2017-12-15 22:37:00 +08:00
if ( isOK < = 1 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " failed to obtain CC! Tag-select is aborting... (%d) " , isOK ) ;
2015-10-08 05:00:46 +08:00
return false ;
2015-02-15 04:17:08 +08:00
}
2015-10-08 05:00:46 +08:00
return true ;
}
2015-12-22 02:44:47 +08:00
static bool select_and_auth ( uint8_t * KEY , uint8_t * MAC , uint8_t * div_key , bool use_credit_key , bool elite , bool rawkey , bool verbose ) {
2017-12-13 17:40:20 +08:00
uint8_t CSN [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t CCNR [ 12 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2015-10-08 05:00:46 +08:00
2018-02-04 19:25:55 +08:00
if ( ! select_only ( CSN , CCNR , use_credit_key , verbose ) ) {
2018-02-21 14:47:33 +08:00
if ( verbose ) PrintAndLogEx ( FAILED , " selecting tag failed " ) ;
2015-10-08 05:00:46 +08:00
return false ;
2018-02-04 19:25:55 +08:00
}
2015-10-08 05:00:46 +08:00
//get div_key
2017-12-13 17:18:38 +08:00
if ( rawkey )
2015-12-22 02:44:47 +08:00
memcpy ( div_key , KEY , 8 ) ;
else
2016-01-20 02:31:34 +08:00
HFiClassCalcDivKey ( CSN , KEY , div_key , elite ) ;
2018-02-21 14:47:33 +08:00
if ( verbose ) PrintAndLogEx ( SUCCESS , " authing with %s: %s " , rawkey ? " raw key " : " diversified key " , sprint_hex ( div_key , 8 ) ) ;
2015-10-08 05:00:46 +08:00
doMAC ( CCNR , div_key , MAC ) ;
UsbCommand resp ;
2017-12-21 17:13:40 +08:00
UsbCommand d = { CMD_ICLASS_AUTHENTICATION , { 0 , 0 , 0 } } ;
2015-10-08 05:00:46 +08:00
memcpy ( d . d . asBytes , MAC , 4 ) ;
clearCommandBuffer ( ) ;
SendCommand ( & d ) ;
2017-12-21 17:13:40 +08:00
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4000 ) ) {
2018-02-21 14:47:33 +08:00
if ( verbose ) PrintAndLogEx ( FAILED , " auth command execute timeout " ) ;
2015-10-08 05:00:46 +08:00
return false ;
}
2017-12-21 17:13:40 +08:00
uint8_t isOK = resp . arg [ 0 ] & 0xFF ;
2015-10-08 05:00:46 +08:00
if ( ! isOK ) {
2018-02-21 14:47:33 +08:00
if ( verbose ) PrintAndLogEx ( FAILED , " authentication error " ) ;
2015-10-08 05:00:46 +08:00
return false ;
2015-02-15 04:17:08 +08:00
}
2015-10-08 05:00:46 +08:00
return true ;
}
2015-02-15 04:17:08 +08:00
2015-10-08 05:00:46 +08:00
int CmdHFiClassReader_Dump ( const char * Cmd ) {
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t c_div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t blockno = 0 ;
uint8_t numblks = 0 ;
uint8_t maxBlk = 31 ;
uint8_t app_areas = 1 ;
uint8_t kb = 2 ;
uint8_t KEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t CreditKEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
uint8_t fileNameLen = 0 ;
2018-02-04 19:25:55 +08:00
char filename [ FILE_PATH_SIZE ] = { 0 } ;
2015-10-08 05:00:46 +08:00
char tempStr [ 50 ] = { 0 } ;
bool have_debit_key = false ;
bool have_credit_key = false ;
bool use_credit_key = false ;
bool elite = false ;
2015-12-22 02:44:47 +08:00
bool rawkey = false ;
2015-10-08 05:00:46 +08:00
bool errors = false ;
2018-02-04 19:25:55 +08:00
bool verbose = false ;
2015-10-08 05:00:46 +08:00
uint8_t cmdp = 0 ;
2018-02-08 17:49:44 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( param_getchar ( Cmd , cmdp ) ) {
2015-10-08 05:00:46 +08:00
case ' h ' :
case ' H ' :
return usage_hf_iclass_dump ( ) ;
case ' c ' :
case ' C ' :
have_credit_key = true ;
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , CreditKEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
2016-01-13 06:05:10 +08:00
if ( keyNbr < ICLASS_KEYS_MAX ) {
2015-10-08 05:00:46 +08:00
memcpy ( CreditKEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' e ' :
case ' E ' :
elite = true ;
cmdp + + ;
break ;
case ' f ' :
case ' F ' :
2017-11-12 06:23:01 +08:00
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
2015-10-08 05:00:46 +08:00
if ( fileNameLen < 1 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " no filename found after f " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' k ' :
case ' K ' :
have_debit_key = true ;
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
2016-01-13 06:05:10 +08:00
if ( keyNbr < ICLASS_KEYS_MAX ) {
2015-10-08 05:00:46 +08:00
memcpy ( KEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
2015-12-22 02:44:47 +08:00
case ' r ' :
case ' R ' :
rawkey = true ;
cmdp + + ;
break ;
2018-02-04 19:25:55 +08:00
case ' v ' :
case ' V ' :
verbose = true ;
cmdp + + ;
break ;
2015-10-08 05:00:46 +08:00
default :
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
break ;
}
}
2017-07-15 02:54:11 +08:00
if ( errors | | cmdp < 2 ) return usage_hf_iclass_dump ( ) ;
2015-10-08 05:00:46 +08:00
// if no debit key given try credit key on AA1 (not for iclass but for some picopass this will work)
if ( ! have_debit_key & & have_credit_key ) use_credit_key = true ;
2018-02-08 19:15:47 +08:00
uint32_t flags = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC |
FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_ONLY_ONCE |
FLAG_ICLASS_READER_ONE_TRY ;
2015-10-08 05:00:46 +08:00
//get config and first 3 blocks
2018-02-08 19:15:47 +08:00
UsbCommand c = { CMD_READER_ICLASS , { flags , 0 , 0 } } ;
2015-02-15 04:17:08 +08:00
UsbCommand resp ;
2015-10-08 05:00:46 +08:00
uint8_t tag_data [ 255 * 8 ] ;
2015-02-15 04:17:08 +08:00
2015-10-08 05:00:46 +08:00
clearCommandBuffer ( ) ;
SendCommand ( & c ) ;
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
2017-11-12 06:23:01 +08:00
DropField ( ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
2018-02-08 18:03:11 +08:00
DropField ( ) ;
2015-10-08 05:00:46 +08:00
uint8_t readStatus = resp . arg [ 0 ] & 0xff ;
2018-02-08 18:03:11 +08:00
uint8_t * data = resp . d . asBytes ;
2015-02-15 04:17:08 +08:00
2018-02-08 17:49:44 +08:00
if ( readStatus = = 0 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " no tag found " ) ;
2016-01-20 02:31:34 +08:00
return 0 ;
}
2016-12-09 21:38:51 +08:00
if ( readStatus & ( FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_CC ) ) {
2015-10-08 05:00:46 +08:00
memcpy ( tag_data , data , 8 * 3 ) ;
2016-12-09 21:38:51 +08:00
blockno + = 2 ; // 2 to force re-read of block 2 later. (seems to respond differently..)
2015-10-08 05:00:46 +08:00
numblks = data [ 8 ] ;
getMemConfig ( data [ 13 ] , data [ 12 ] , & maxBlk , & app_areas , & kb ) ;
// large memory - not able to dump pages currently
if ( numblks > maxBlk ) numblks = maxBlk ;
}
2018-02-08 18:03:11 +08:00
2015-10-08 05:00:46 +08:00
// authenticate debit key and get div_key - later store in dump block 3
2018-02-04 19:25:55 +08:00
if ( ! select_and_auth ( KEY , MAC , div_key , use_credit_key , elite , rawkey , verbose ) ) {
2015-10-08 05:00:46 +08:00
//try twice - for some reason it sometimes fails the first time...
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " retry to select card " ) ;
2018-02-04 19:25:55 +08:00
if ( ! select_and_auth ( KEY , MAC , div_key , use_credit_key , elite , rawkey , verbose ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " failed authenticating with debit key " ) ;
2017-11-12 06:23:01 +08:00
DropField ( ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
2015-02-15 04:17:08 +08:00
}
2015-10-08 05:00:46 +08:00
// begin dump
UsbCommand w = { CMD_ICLASS_DUMP , { blockno , numblks - blockno + 1 } } ;
clearCommandBuffer ( ) ;
SendCommand ( & w ) ;
2018-02-08 19:15:47 +08:00
while ( true ) {
2018-03-01 21:10:52 +08:00
printf ( " . " ) ; fflush ( stdout ) ;
2018-02-08 19:15:47 +08:00
if ( ukbhit ( ) ) {
int gc = getchar ( ) ; ( void ) gc ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " \n [!] aborted via keyboard! \n " ) ;
2018-02-08 19:15:47 +08:00
DropField ( ) ;
return 0 ;
}
if ( WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) )
break ;
2015-10-08 05:00:46 +08:00
}
2018-02-08 18:03:11 +08:00
// dump cmd switch off at device when finised.
2018-02-08 17:49:44 +08:00
2015-10-08 05:00:46 +08:00
uint32_t blocksRead = resp . arg [ 1 ] ;
uint8_t isOK = resp . arg [ 0 ] & 0xff ;
if ( ! isOK & & ! blocksRead ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " read block failed " ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
2018-02-08 17:49:44 +08:00
2015-10-08 05:00:46 +08:00
uint32_t startindex = resp . arg [ 2 ] ;
2018-02-08 17:49:44 +08:00
if ( blocksRead * 8 > sizeof ( tag_data ) - ( blockno * 8 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " data exceeded buffer size! " ) ;
2015-10-08 05:00:46 +08:00
blocksRead = ( sizeof ( tag_data ) / 8 ) - blockno ;
}
2018-02-08 17:49:44 +08:00
2015-10-08 05:00:46 +08:00
// response ok - now get bigbuf content of the dump
2018-05-03 01:51:10 +08:00
if ( ! GetFromDevice ( BIG_BUF , tag_data + ( blockno * 8 ) , blocksRead * 8 , startindex , NULL , 2500 , false ) ) {
2018-04-27 22:48:59 +08:00
PrintAndLogEx ( WARNING , " command execution time out " ) ;
return 0 ;
}
2015-10-08 05:00:46 +08:00
size_t gotBytes = blocksRead * 8 + blockno * 8 ;
// try AA2
if ( have_credit_key ) {
//turn off hf field before authenticating with different key
2017-11-12 06:23:01 +08:00
DropField ( ) ;
2015-10-08 05:00:46 +08:00
memset ( MAC , 0 , 4 ) ;
// AA2 authenticate credit key and git c_div_key - later store in dump block 4
2018-02-04 19:25:55 +08:00
if ( ! select_and_auth ( CreditKEY , MAC , c_div_key , true , elite , rawkey , verbose ) ) {
2015-10-08 05:00:46 +08:00
//try twice - for some reason it sometimes fails the first time...
2018-02-04 19:25:55 +08:00
if ( ! select_and_auth ( CreditKEY , MAC , c_div_key , true , elite , rawkey , verbose ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " failed authenticating with credit key " ) ;
2017-11-12 06:23:01 +08:00
DropField ( ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
}
// do we still need to read more block? (aa2 enabled?)
if ( maxBlk > blockno + numblks + 1 ) {
// setup dump and start
w . arg [ 0 ] = blockno + blocksRead ;
w . arg [ 1 ] = maxBlk - ( blockno + blocksRead ) ;
clearCommandBuffer ( ) ;
SendCommand ( & w ) ;
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " command execute timeout 2 " ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
uint8_t isOK = resp . arg [ 0 ] & 0xff ;
blocksRead = resp . arg [ 1 ] ;
if ( ! isOK & & ! blocksRead ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " read block failed 2 " ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
2015-02-15 04:17:08 +08:00
2015-10-08 05:00:46 +08:00
startindex = resp . arg [ 2 ] ;
2018-02-04 19:25:55 +08:00
if ( blocksRead * 8 > sizeof ( tag_data ) - gotBytes ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " data exceeded buffer size! " ) ;
2015-10-08 05:00:46 +08:00
blocksRead = ( sizeof ( tag_data ) - gotBytes ) / 8 ;
}
// get dumped data from bigbuf
2018-05-03 01:51:10 +08:00
if ( ! GetFromDevice ( BIG_BUF , tag_data + gotBytes , blocksRead * 8 , startindex , NULL , 2500 , false ) ) {
2018-04-27 22:48:59 +08:00
PrintAndLogEx ( WARNING , " command execution time out " ) ;
return 0 ;
}
2015-02-15 04:17:08 +08:00
2018-02-04 19:25:55 +08:00
gotBytes + = blocksRead * 8 ;
2015-10-08 05:00:46 +08:00
}
}
2015-02-15 04:17:08 +08:00
2018-02-08 17:49:44 +08:00
DropField ( ) ;
2015-10-08 05:00:46 +08:00
// add diversified keys to dump
if ( have_debit_key ) memcpy ( tag_data + ( 3 * 8 ) , div_key , 8 ) ;
if ( have_credit_key ) memcpy ( tag_data + ( 4 * 8 ) , c_div_key , 8 ) ;
2018-02-08 17:49:44 +08:00
2015-10-08 05:00:46 +08:00
// print the dump
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " ------+--+-------------------------+ \n " ) ;
PrintAndLogEx ( NORMAL , " CSN |00| %s| \n " , sprint_hex ( tag_data , 8 ) ) ;
2016-12-09 21:38:51 +08:00
printIclassDumpContents ( tag_data , 1 , ( gotBytes / 8 ) , gotBytes ) ;
2015-10-08 05:00:46 +08:00
if ( filename [ 0 ] = = 0 ) {
snprintf ( filename , FILE_PATH_SIZE , " iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x " ,
tag_data [ 0 ] , tag_data [ 1 ] , tag_data [ 2 ] , tag_data [ 3 ] ,
tag_data [ 4 ] , tag_data [ 5 ] , tag_data [ 6 ] , tag_data [ 7 ] ) ;
}
2015-02-15 04:17:08 +08:00
2015-10-08 05:00:46 +08:00
// save the dump to .bin file
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " saving dump file - %d blocks read " , gotBytes / 8 ) ;
2015-10-08 05:00:46 +08:00
saveFile ( filename , " bin " , tag_data , gotBytes ) ;
return 1 ;
}
2015-02-15 04:17:08 +08:00
2015-12-22 02:44:47 +08:00
static int WriteBlock ( uint8_t blockno , uint8_t * bldata , uint8_t * KEY , bool use_credit_key , bool elite , bool rawkey , bool verbose ) {
2018-02-04 19:25:55 +08:00
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2015-12-22 02:44:47 +08:00
if ( ! select_and_auth ( KEY , MAC , div_key , use_credit_key , elite , rawkey , verbose ) )
2015-10-08 05:00:46 +08:00
return 0 ;
UsbCommand resp ;
Calc_wb_mac ( blockno , bldata , div_key , MAC ) ;
UsbCommand w = { CMD_ICLASS_WRITEBLOCK , { blockno } } ;
memcpy ( w . d . asBytes , bldata , 8 ) ;
memcpy ( w . d . asBytes + 8 , MAC , 4 ) ;
clearCommandBuffer ( ) ;
SendCommand ( & w ) ;
2018-02-04 19:25:55 +08:00
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
2018-02-21 14:47:33 +08:00
if ( verbose ) PrintAndLogEx ( WARNING , " Write Command execute timeout " ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
uint8_t isOK = resp . arg [ 0 ] & 0xff ;
2018-02-04 19:25:55 +08:00
if ( isOK )
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " Write block successful " ) ;
2018-02-04 19:25:55 +08:00
else
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Write block failed " ) ;
2018-02-04 19:25:55 +08:00
return isOK ;
2015-10-08 05:00:46 +08:00
}
int CmdHFiClass_WriteBlock ( const char * Cmd ) {
2018-02-04 19:25:55 +08:00
uint8_t blockno = 0 ;
uint8_t bldata [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
uint8_t KEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2015-10-08 05:00:46 +08:00
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
char tempStr [ 50 ] = { 0 } ;
bool use_credit_key = false ;
bool elite = false ;
2017-12-21 17:13:40 +08:00
bool rawkey = false ;
2015-10-08 05:00:46 +08:00
bool errors = false ;
2018-02-04 19:25:55 +08:00
bool verbose = false ;
2015-10-08 05:00:46 +08:00
uint8_t cmdp = 0 ;
2017-07-15 02:54:11 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( param_getchar ( Cmd , cmdp ) ) {
2015-10-08 05:00:46 +08:00
case ' h ' :
case ' H ' :
return usage_hf_iclass_writeblock ( ) ;
case ' b ' :
case ' B ' :
if ( param_gethex ( Cmd , cmdp + 1 , & blockno , 2 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Block No must include 2 HEX symbols \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' c ' :
case ' C ' :
use_credit_key = true ;
cmdp + + ;
break ;
case ' d ' :
case ' D ' :
2017-12-21 17:13:40 +08:00
if ( param_gethex ( Cmd , cmdp + 1 , bldata , 16 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Data must include 16 HEX symbols \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' e ' :
case ' E ' :
elite = true ;
cmdp + + ;
break ;
case ' k ' :
case ' K ' :
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
2016-01-13 06:05:10 +08:00
if ( keyNbr < ICLASS_KEYS_MAX ) {
2015-10-08 05:00:46 +08:00
memcpy ( KEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
2015-12-22 02:44:47 +08:00
case ' r ' :
case ' R ' :
rawkey = true ;
cmdp + + ;
break ;
2018-02-04 19:25:55 +08:00
case ' v ' :
case ' V ' :
verbose = true ;
cmdp + + ;
break ;
2015-10-08 05:00:46 +08:00
default :
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
break ;
}
}
2017-07-15 02:54:11 +08:00
if ( errors | | cmdp < 6 ) return usage_hf_iclass_writeblock ( ) ;
2015-10-08 05:00:46 +08:00
2018-02-04 19:25:55 +08:00
int ans = WriteBlock ( blockno , bldata , KEY , use_credit_key , elite , rawkey , verbose ) ;
2017-11-12 06:23:01 +08:00
DropField ( ) ;
2015-10-08 05:00:46 +08:00
return ans ;
}
int CmdHFiClassCloneTag ( const char * Cmd ) {
2016-01-13 06:10:38 +08:00
char filename [ FILE_PATH_SIZE ] = { 0x00 } ;
2018-02-04 19:25:55 +08:00
char tempStr [ 50 ] = { 0 } ;
uint8_t KEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2015-10-08 05:00:46 +08:00
uint8_t keyNbr = 0 ;
uint8_t fileNameLen = 0 ;
uint8_t startblock = 0 ;
uint8_t endblock = 0 ;
uint8_t dataLen = 0 ;
bool use_credit_key = false ;
bool elite = false ;
2015-12-22 02:44:47 +08:00
bool rawkey = false ;
2015-10-08 05:00:46 +08:00
bool errors = false ;
2018-02-04 19:25:55 +08:00
bool verbose = false ;
2015-10-08 05:00:46 +08:00
uint8_t cmdp = 0 ;
2017-07-15 02:54:11 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( param_getchar ( Cmd , cmdp ) ) {
2015-10-08 05:00:46 +08:00
case ' h ' :
case ' H ' :
return usage_hf_iclass_clone ( ) ;
case ' b ' :
case ' B ' :
if ( param_gethex ( Cmd , cmdp + 1 , & startblock , 2 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " start block No must include 2 HEX symbols \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' c ' :
case ' C ' :
use_credit_key = true ;
cmdp + + ;
break ;
case ' e ' :
case ' E ' :
elite = true ;
cmdp + + ;
break ;
case ' f ' :
case ' F ' :
2017-11-12 06:23:01 +08:00
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
2015-10-08 05:00:46 +08:00
if ( fileNameLen < 1 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " No filename found after f " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' k ' :
case ' K ' :
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
2016-01-13 06:05:10 +08:00
if ( keyNbr < ICLASS_KEYS_MAX ) {
2015-10-08 05:00:46 +08:00
memcpy ( KEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' l ' :
case ' L ' :
if ( param_gethex ( Cmd , cmdp + 1 , & endblock , 2 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " start Block No must include 2 HEX symbols \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
2015-12-22 02:44:47 +08:00
case ' r ' :
case ' R ' :
rawkey = true ;
cmdp + + ;
break ;
2018-02-04 19:25:55 +08:00
case ' v ' :
case ' V ' :
verbose = true ;
cmdp + + ;
break ;
2015-10-08 05:00:46 +08:00
default :
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
break ;
}
}
2017-07-15 02:54:11 +08:00
if ( errors | | cmdp < 8 ) return usage_hf_iclass_clone ( ) ;
2015-10-08 05:00:46 +08:00
FILE * f ;
iclass_block_t tag_data [ USB_CMD_DATA_SIZE / 12 ] ;
if ( ( endblock - startblock + 1 ) * 12 > USB_CMD_DATA_SIZE ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Trying to write too many blocks at once. Max: %d " , USB_CMD_DATA_SIZE / 8 ) ;
2015-10-08 05:00:46 +08:00
}
// file handling and reading
f = fopen ( filename , " rb " ) ;
if ( ! f ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " failed to read file '%s' " , filename ) ;
2015-10-08 05:00:46 +08:00
return 1 ;
}
if ( startblock < 5 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " you cannot write key blocks this way. yet... make your start block > 4 " ) ;
2016-01-13 06:06:53 +08:00
fclose ( f ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
// now read data from the file from block 6 --- 19
// ok we will use this struct [data 8 bytes][MAC 4 bytes] for each block calculate all mac number for each data
// then copy to usbcommand->asbytes; the max is 32 - 6 = 24 block 12 bytes each block 288 bytes then we can only accept to clone 21 blocks at the time,
// else we have to create a share memory
int i ;
2018-02-04 19:25:55 +08:00
fseek ( f , startblock * 8 , SEEK_SET ) ;
size_t bytes_read = fread ( tag_data , sizeof ( iclass_block_t ) , endblock - startblock + 1 , f ) ;
2016-01-20 02:52:01 +08:00
if ( bytes_read = = 0 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " file reading error. " ) ;
2015-12-09 22:29:18 +08:00
fclose ( f ) ;
return 2 ;
}
2015-10-08 05:00:46 +08:00
2018-02-04 19:25:55 +08:00
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2015-10-08 05:00:46 +08:00
2018-02-04 19:25:55 +08:00
if ( ! select_and_auth ( KEY , MAC , div_key , use_credit_key , elite , rawkey , verbose ) )
2015-10-08 05:00:46 +08:00
return 0 ;
UsbCommand w = { CMD_ICLASS_CLONE , { startblock , endblock } } ;
uint8_t * ptr ;
// calculate all mac for every the block we will write
for ( i = startblock ; i < = endblock ; i + + ) {
Calc_wb_mac ( i , tag_data [ i - startblock ] . d , div_key , MAC ) ;
// usb command d start pointer = d + (i - 6) * 12
// memcpy(pointer,tag_data[i - 6],8) 8 bytes
// memcpy(pointer + 8,mac,sizoof(mac) 4 bytes;
// next one
ptr = w . d . asBytes + ( i - startblock ) * 12 ;
memcpy ( ptr , & ( tag_data [ i - startblock ] . d [ 0 ] ) , 8 ) ;
memcpy ( ptr + 8 , MAC , 4 ) ;
}
uint8_t p [ 12 ] ;
for ( i = 0 ; i < = endblock - startblock ; i + + ) {
memcpy ( p , w . d . asBytes + ( i * 12 ) , 12 ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " Block |%02x| " , i + startblock ) ;
PrintAndLogEx ( NORMAL , " %02x%02x%02x%02x%02x%02x%02x%02x | " , p [ 0 ] , p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , p [ 5 ] , p [ 6 ] , p [ 7 ] ) ;
PrintAndLogEx ( NORMAL , " MAC |%02x%02x%02x%02x| \n " , p [ 8 ] , p [ 9 ] , p [ 10 ] , p [ 11 ] ) ;
2015-10-08 05:00:46 +08:00
}
UsbCommand resp ;
2016-01-20 02:31:34 +08:00
clearCommandBuffer ( ) ;
2015-10-08 05:00:46 +08:00
SendCommand ( & w ) ;
2018-02-04 19:25:55 +08:00
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
return 1 ;
}
2017-07-30 15:17:48 +08:00
static int ReadBlock ( uint8_t * KEY , uint8_t blockno , uint8_t keyType , bool elite , bool rawkey , bool verbose , bool auth ) {
2017-12-21 19:42:32 +08:00
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2015-10-08 05:00:46 +08:00
2017-07-05 18:05:53 +08:00
// block 0,1 should always be able to read, and block 5 on some cards.
2017-07-30 15:17:48 +08:00
if ( auth | | blockno > = 2 ) {
2017-12-21 17:13:40 +08:00
if ( ! select_and_auth ( KEY , MAC , div_key , ( keyType = = 0x18 ) , elite , rawkey , verbose ) )
2017-07-05 18:05:53 +08:00
return 0 ;
} else {
2017-12-21 17:13:40 +08:00
uint8_t CSN [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t CCNR [ 12 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
if ( ! select_only ( CSN , CCNR , ( keyType = = 0x18 ) , verbose ) )
2017-07-30 15:17:48 +08:00
return 0 ;
2017-07-05 18:05:53 +08:00
}
2017-07-30 15:17:48 +08:00
2015-10-08 05:00:46 +08:00
UsbCommand resp ;
2017-12-21 17:13:40 +08:00
UsbCommand c = { CMD_ICLASS_READBLOCK , { blockno } } ;
2015-10-08 05:00:46 +08:00
clearCommandBuffer ( ) ;
2017-12-21 17:13:40 +08:00
SendCommand ( & c ) ;
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Command execute timeout " ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
2015-02-15 04:17:08 +08:00
}
2016-01-20 02:31:34 +08:00
2015-10-08 05:00:46 +08:00
uint8_t isOK = resp . arg [ 0 ] & 0xff ;
if ( ! isOK ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " read block failed " ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
//data read is stored in: resp.d.asBytes[0-15]
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " block %02X: %s \n " , blockno , sprint_hex ( resp . d . asBytes , 8 ) ) ;
2015-10-08 05:00:46 +08:00
return 1 ;
}
int CmdHFiClass_ReadBlock ( const char * Cmd ) {
2017-12-21 17:13:40 +08:00
uint8_t blockno = 0 ;
2015-10-08 05:00:46 +08:00
uint8_t keyType = 0x88 ; //debit key
2017-12-15 22:37:00 +08:00
uint8_t KEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2015-10-08 05:00:46 +08:00
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
char tempStr [ 50 ] = { 0 } ;
bool elite = false ;
2015-12-22 02:44:47 +08:00
bool rawkey = false ;
2015-10-08 05:00:46 +08:00
bool errors = false ;
2017-07-30 15:17:48 +08:00
bool auth = false ;
2018-02-04 19:25:55 +08:00
bool verbose = false ;
2015-10-08 05:00:46 +08:00
uint8_t cmdp = 0 ;
2017-12-21 17:13:40 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( param_getchar ( Cmd , cmdp ) ) {
2015-10-08 05:00:46 +08:00
case ' h ' :
case ' H ' :
return usage_hf_iclass_readblock ( ) ;
case ' b ' :
case ' B ' :
if ( param_gethex ( Cmd , cmdp + 1 , & blockno , 2 ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Block No must include 2 HEX symbols \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' c ' :
case ' C ' :
keyType = 0x18 ;
cmdp + + ;
break ;
case ' e ' :
case ' E ' :
elite = true ;
cmdp + + ;
break ;
case ' k ' :
case ' K ' :
2017-07-30 15:17:48 +08:00
auth = true ;
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
2016-01-13 06:05:10 +08:00
if ( keyNbr < ICLASS_KEYS_MAX ) {
2015-10-08 05:00:46 +08:00
memcpy ( KEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
2015-12-22 02:44:47 +08:00
case ' r ' :
case ' R ' :
rawkey = true ;
cmdp + + ;
break ;
2018-02-04 19:25:55 +08:00
case ' v ' :
case ' V ' :
verbose = true ;
cmdp + + ;
break ;
2015-10-08 05:00:46 +08:00
default :
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
break ;
}
}
2017-07-15 02:54:11 +08:00
if ( errors | | cmdp < 4 ) return usage_hf_iclass_readblock ( ) ;
2015-10-08 05:00:46 +08:00
2017-07-30 15:17:48 +08:00
if ( ! auth )
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " warning: no authentication used with read, only a few specific blocks can be read accurately without authentication. " ) ;
2018-02-04 19:25:55 +08:00
return ReadBlock ( KEY , blockno , keyType , elite , rawkey , verbose , auth ) ;
2015-10-08 05:00:46 +08:00
}
int CmdHFiClass_loclass ( const char * Cmd ) {
2015-01-05 16:16:06 +08:00
char opt = param_getchar ( Cmd , 0 ) ;
2017-08-18 16:23:46 +08:00
if ( strlen ( Cmd ) < 1 | | opt = = ' h ' )
usage_hf_iclass_loclass ( ) ;
2017-07-15 02:54:11 +08:00
char fileName [ FILE_PATH_SIZE ] = { 0 } ;
2017-08-18 16:23:46 +08:00
if ( opt = = ' f ' ) {
2017-11-12 06:23:01 +08:00
if ( param_getstr ( Cmd , 1 , fileName , sizeof ( fileName ) ) > 0 ) {
2015-02-15 04:17:08 +08:00
return bruteforceFileNoKeys ( fileName ) ;
2016-01-20 02:31:34 +08:00
} else {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " You must specify a filename " ) ;
2018-02-02 01:24:38 +08:00
return 0 ;
2015-02-15 04:17:08 +08:00
}
2015-01-05 16:16:06 +08:00
}
2017-08-18 16:23:46 +08:00
else if ( opt = = ' t ' ) {
2015-01-05 16:16:06 +08:00
int errors = testCipherUtils ( ) ;
errors + = testMAC ( ) ;
errors + = doKeyTests ( 0 ) ;
errors + = testElite ( ) ;
2018-02-22 22:59:55 +08:00
if ( errors ) PrintAndLogDevice ( WARNING , " There were errors!!! " ) ;
2015-01-05 16:16:06 +08:00
return errors ;
}
return 0 ;
}
2014-04-15 18:47:01 +08:00
2015-10-08 05:00:46 +08:00
void printIclassDumpContents ( uint8_t * iclass_dump , uint8_t startblock , uint8_t endblock , size_t filesize ) {
uint8_t mem_config ;
memcpy ( & mem_config , iclass_dump + 13 , 1 ) ;
uint8_t maxmemcount ;
2016-12-09 21:38:51 +08:00
2015-10-08 05:00:46 +08:00
uint8_t filemaxblock = filesize / 8 ;
2016-01-20 02:31:34 +08:00
2015-10-08 05:00:46 +08:00
if ( mem_config & 0x80 )
maxmemcount = 255 ;
else
maxmemcount = 31 ;
if ( startblock = = 0 )
startblock = 6 ;
2016-01-20 02:31:34 +08:00
2015-10-08 05:00:46 +08:00
if ( ( endblock > maxmemcount ) | | ( endblock = = 0 ) )
endblock = maxmemcount ;
2016-01-20 02:31:34 +08:00
2016-12-09 21:38:51 +08:00
// remember endblock needs to relate to zero-index arrays.
if ( endblock > filemaxblock - 1 )
endblock = filemaxblock - 1 ;
//PrintAndLog ("startblock: %d, endblock: %d, filesize: %d, maxmemcount: %d, filemaxblock: %d",startblock, endblock,filesize, maxmemcount, filemaxblock);
2016-01-20 02:31:34 +08:00
2015-10-08 05:00:46 +08:00
int i = startblock ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " ------+--+-------------------------+ \n " ) ;
2015-10-08 05:00:46 +08:00
while ( i < = endblock ) {
2016-12-09 21:38:51 +08:00
uint8_t * blk = iclass_dump + ( i * 8 ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " |%02X| %s \n " , i , sprint_hex_ascii ( blk , 8 ) ) ;
2015-10-08 05:00:46 +08:00
i + + ;
}
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " ------+--+-------------------------+ \n " ) ;
2015-10-08 05:00:46 +08:00
}
int CmdHFiClassReadTagFile ( const char * Cmd ) {
int startblock = 0 ;
int endblock = 0 ;
char tempnum [ 5 ] ;
FILE * f ;
char filename [ FILE_PATH_SIZE ] ;
2017-11-12 06:23:01 +08:00
if ( param_getstr ( Cmd , 0 , filename , sizeof ( filename ) ) < 1 )
2015-10-08 05:00:46 +08:00
return usage_hf_iclass_readtagfile ( ) ;
2016-01-20 02:31:34 +08:00
2017-11-12 06:23:01 +08:00
if ( param_getstr ( Cmd , 1 , tempnum , sizeof ( tempnum ) ) < 1 )
2015-10-08 05:00:46 +08:00
startblock = 0 ;
else
sscanf ( tempnum , " %d " , & startblock ) ;
2017-11-12 06:23:01 +08:00
if ( param_getstr ( Cmd , 2 , tempnum , sizeof ( tempnum ) ) < 1 )
2015-10-08 05:00:46 +08:00
endblock = 0 ;
else
sscanf ( tempnum , " %d " , & endblock ) ;
2016-01-20 02:31:34 +08:00
2015-10-08 05:00:46 +08:00
// file handling and reading
f = fopen ( filename , " rb " ) ;
if ( ! f ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Failed to read from file '%s' " , filename ) ;
2015-10-08 05:00:46 +08:00
return 1 ;
}
fseek ( f , 0 , SEEK_END ) ;
long fsize = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
2016-01-13 05:49:29 +08:00
if ( fsize < 0 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Error, when getting filesize " ) ;
2016-01-13 05:49:29 +08:00
fclose ( f ) ;
return 1 ;
}
2015-10-08 05:00:46 +08:00
2018-05-04 01:42:16 +08:00
uint8_t * dump = calloc ( fsize , sizeof ( uint8_t ) ) ;
if ( ! dump ) {
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
fclose ( f ) ;
return 1 ;
}
2015-10-08 05:00:46 +08:00
size_t bytes_read = fread ( dump , 1 , fsize , f ) ;
fclose ( f ) ;
2016-01-20 02:31:34 +08:00
2015-10-08 05:00:46 +08:00
uint8_t * csn = dump ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " ------+--+-------------------------+ \n " ) ;
PrintAndLogEx ( NORMAL , " CSN |00| %s| \n " , sprint_hex ( csn , 8 ) ) ;
2016-12-09 21:38:51 +08:00
printIclassDumpContents ( dump , startblock , endblock , bytes_read ) ;
2015-10-08 05:00:46 +08:00
free ( dump ) ;
return 0 ;
}
2017-12-21 17:13:40 +08:00
void HFiClassCalcDivKey ( uint8_t * CSN , uint8_t * KEY , uint8_t * div_key , bool elite ) {
2015-10-08 05:00:46 +08:00
uint8_t keytable [ 128 ] = { 0 } ;
uint8_t key_index [ 8 ] = { 0 } ;
if ( elite ) {
uint8_t key_sel [ 8 ] = { 0 } ;
uint8_t key_sel_p [ 8 ] = { 0 } ;
hash2 ( KEY , keytable ) ;
hash1 ( CSN , key_index ) ;
for ( uint8_t i = 0 ; i < 8 ; i + + )
key_sel [ i ] = keytable [ key_index [ i ] ] & 0xFF ;
//Permute from iclass format to standard format
permutekey_rev ( key_sel , key_sel_p ) ;
diversifyKey ( CSN , key_sel_p , div_key ) ;
} else {
diversifyKey ( CSN , KEY , div_key ) ;
}
}
//when told CSN, oldkey, newkey, if new key is elite (elite), and if old key was elite (oldElite)
//calculate and return xor_div_key (ready for a key write command)
//print all div_keys if verbose
static void HFiClassCalcNewKey ( uint8_t * CSN , uint8_t * OLDKEY , uint8_t * NEWKEY , uint8_t * xor_div_key , bool elite , bool oldElite , bool verbose ) {
uint8_t old_div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t new_div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
//get old div key
HFiClassCalcDivKey ( CSN , OLDKEY , old_div_key , oldElite ) ;
//get new div key
HFiClassCalcDivKey ( CSN , NEWKEY , new_div_key , elite ) ;
for ( uint8_t i = 0 ; i < sizeof ( old_div_key ) ; i + + ) {
xor_div_key [ i ] = old_div_key [ i ] ^ new_div_key [ i ] ;
}
if ( verbose ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " Old div key : %s \n " , sprint_hex ( old_div_key , 8 ) ) ;
PrintAndLogEx ( SUCCESS , " New div key : %s \n " , sprint_hex ( new_div_key , 8 ) ) ;
PrintAndLogEx ( SUCCESS , " Xor div key : %s \n " , sprint_hex ( xor_div_key , 8 ) ) ;
2015-10-08 05:00:46 +08:00
}
}
int CmdHFiClassCalcNewKey ( const char * Cmd ) {
uint8_t OLDKEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t NEWKEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t xor_div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t CSN [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t CCNR [ 12 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
char tempStr [ 50 ] = { 0 } ;
bool givenCSN = false ;
bool oldElite = false ;
bool elite = false ;
bool errors = false ;
uint8_t cmdp = 0 ;
2017-07-15 02:54:11 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( param_getchar ( Cmd , cmdp ) ) {
2015-10-08 05:00:46 +08:00
case ' h ' :
case ' H ' :
return usage_hf_iclass_calc_newkey ( ) ;
case ' e ' :
case ' E ' :
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 2 )
oldElite = true ;
elite = true ;
cmdp + + ;
break ;
case ' n ' :
case ' N ' :
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , NEWKEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
2016-01-13 06:05:10 +08:00
if ( keyNbr < ICLASS_KEYS_MAX ) {
2015-10-08 05:00:46 +08:00
memcpy ( NEWKEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: NewKey Nbr is invalid \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: NewKey is incorrect length \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' o ' :
case ' O ' :
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , OLDKEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
2016-01-13 06:05:10 +08:00
if ( keyNbr < ICLASS_KEYS_MAX ) {
2015-10-08 05:00:46 +08:00
memcpy ( OLDKEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' s ' :
case ' S ' :
givenCSN = true ;
if ( param_gethex ( Cmd , cmdp + 1 , CSN , 16 ) )
return usage_hf_iclass_calc_newkey ( ) ;
cmdp + = 2 ;
break ;
default :
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
break ;
}
}
2017-07-15 02:54:11 +08:00
if ( errors | | cmdp < 4 ) return usage_hf_iclass_calc_newkey ( ) ;
2015-10-08 05:00:46 +08:00
if ( ! givenCSN )
if ( ! select_only ( CSN , CCNR , false , true ) )
return 0 ;
HFiClassCalcNewKey ( CSN , OLDKEY , NEWKEY , xor_div_key , elite , oldElite , true ) ;
return 0 ;
}
static int loadKeys ( char * filename ) {
FILE * f ;
f = fopen ( filename , " rb " ) ;
if ( ! f ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Failed to read from file '%s' " , filename ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
fseek ( f , 0 , SEEK_END ) ;
long fsize = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
2016-01-13 05:47:48 +08:00
if ( fsize < 0 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Error, when getting filesize " ) ;
2016-01-13 05:47:48 +08:00
fclose ( f ) ;
return 1 ;
}
2018-05-04 01:42:16 +08:00
uint8_t * dump = calloc ( fsize , sizeof ( uint8_t ) ) ;
if ( ! dump ) {
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
fclose ( f ) ;
return 1 ;
}
2015-10-08 05:00:46 +08:00
size_t bytes_read = fread ( dump , 1 , fsize , f ) ;
fclose ( f ) ;
if ( bytes_read > ICLASS_KEYS_MAX * 8 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " File is too long to load - bytes: %u " , bytes_read ) ;
2015-10-08 05:00:46 +08:00
free ( dump ) ;
return 0 ;
}
uint8_t i = 0 ;
2016-12-09 01:02:48 +08:00
for ( ; i < bytes_read / 8 ; i + + )
2015-10-08 05:00:46 +08:00
memcpy ( iClass_Key_Table [ i ] , dump + ( i * 8 ) , 8 ) ;
2016-12-09 01:02:48 +08:00
2015-10-08 05:00:46 +08:00
free ( dump ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " %u keys loaded " , i ) ;
2015-10-08 05:00:46 +08:00
return 1 ;
}
static int saveKeys ( char * filename ) {
FILE * f ;
f = fopen ( filename , " wb " ) ;
2017-01-12 06:01:15 +08:00
if ( ! f ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " [!] error opening file %s \n " , filename ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
for ( uint8_t i = 0 ; i < ICLASS_KEYS_MAX ; i + + ) {
if ( fwrite ( iClass_Key_Table [ i ] , 8 , 1 , f ) ! = 1 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " save key failed to write to file: %s " , filename ) ;
2015-10-08 05:00:46 +08:00
break ;
}
}
fclose ( f ) ;
return 0 ;
}
static int printKeys ( void ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2016-12-09 01:02:48 +08:00
for ( uint8_t i = 0 ; i < ICLASS_KEYS_MAX ; i + + )
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " %u: %s " , i , sprint_hex ( iClass_Key_Table [ i ] , 8 ) ) ;
PrintAndLogEx ( NORMAL , " " ) ;
2015-10-08 05:00:46 +08:00
return 0 ;
}
int CmdHFiClassManageKeys ( const char * Cmd ) {
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
uint8_t KEY [ 8 ] = { 0 } ;
char filename [ FILE_PATH_SIZE ] ;
uint8_t fileNameLen = 0 ;
bool errors = false ;
uint8_t operation = 0 ;
char tempStr [ 20 ] ;
uint8_t cmdp = 0 ;
2017-07-15 02:54:11 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( param_getchar ( Cmd , cmdp ) ) {
2015-10-08 05:00:46 +08:00
case ' h ' :
case ' H ' :
return usage_hf_iclass_managekeys ( ) ;
case ' f ' :
case ' F ' :
2017-11-12 06:23:01 +08:00
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
2015-10-08 05:00:46 +08:00
if ( fileNameLen < 1 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " No filename found after f " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' n ' :
case ' N ' :
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
2017-07-30 15:17:48 +08:00
if ( keyNbr > = ICLASS_KEYS_MAX ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Invalid block number " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' k ' :
case ' K ' :
operation + = 3 ; //set key
2017-11-12 06:23:01 +08:00
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
2015-10-08 05:00:46 +08:00
if ( dataLen = = 16 ) { //ul-c or ev1/ntag key length
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Key is incorrect length \n " ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' p ' :
case ' P ' :
operation + = 4 ; //print keys in memory
cmdp + + ;
break ;
case ' l ' :
case ' L ' :
operation + = 5 ; //load keys from file
cmdp + + ;
break ;
case ' s ' :
case ' S ' :
operation + = 6 ; //save keys to file
cmdp + + ;
break ;
default :
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2015-10-08 05:00:46 +08:00
errors = true ;
break ;
}
}
2017-07-15 02:54:11 +08:00
if ( errors ) return usage_hf_iclass_managekeys ( ) ;
2015-10-08 05:00:46 +08:00
if ( operation = = 0 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " no operation specified (load, save, or print) \n " ) ;
2015-10-08 05:00:46 +08:00
return usage_hf_iclass_managekeys ( ) ;
}
if ( operation > 6 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Too many operations specified \n " ) ;
2015-10-08 05:00:46 +08:00
return usage_hf_iclass_managekeys ( ) ;
}
if ( operation > 4 & & fileNameLen = = 0 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " You must enter a filename when loading or saving \n " ) ;
2015-10-08 05:00:46 +08:00
return usage_hf_iclass_managekeys ( ) ;
}
switch ( operation ) {
case 3 : memcpy ( iClass_Key_Table [ keyNbr ] , KEY , 8 ) ; return 1 ;
case 4 : return printKeys ( ) ;
case 5 : return loadKeys ( filename ) ;
case 6 : return saveKeys ( filename ) ;
break ;
}
return 0 ;
}
2017-12-13 17:18:38 +08:00
int CmdHFiClassCheckKeys ( const char * Cmd ) {
2018-07-01 04:47:07 +08:00
// empty string
if ( strlen ( Cmd ) = = 0 ) return usage_hf_iclass_chk ( ) ;
2017-12-21 17:13:40 +08:00
uint8_t CSN [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t CCNR [ 12 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2017-12-13 17:18:38 +08:00
// elite key, raw key, standard key
2017-12-17 02:13:10 +08:00
bool use_elite = false ;
2018-07-01 04:47:07 +08:00
bool use_raw = false ;
bool use_credit_key = false ;
2017-12-13 17:18:38 +08:00
bool found_debit = false ;
2017-12-24 17:59:24 +08:00
//bool found_credit = false;
2017-12-21 17:13:40 +08:00
bool got_csn = false ;
2017-12-17 02:13:10 +08:00
bool errors = false ;
uint8_t cmdp = 0x00 ;
2017-12-24 17:59:24 +08:00
2017-12-13 17:18:38 +08:00
char filename [ FILE_PATH_SIZE ] = { 0 } ;
2017-12-17 02:13:10 +08:00
uint8_t fileNameLen = 0 ;
2017-12-24 17:59:24 +08:00
uint8_t * keyBlock = NULL ;
2018-01-02 18:17:31 +08:00
iclass_premac_t * pre = NULL ;
2017-12-24 17:59:24 +08:00
int keycnt = 0 ;
2018-01-31 23:50:41 +08:00
2017-12-21 17:13:40 +08:00
// time
uint64_t t1 = msclock ( ) ;
2017-12-17 02:13:10 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
2018-07-01 04:47:07 +08:00
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2017-12-17 02:13:10 +08:00
case ' h ' :
return usage_hf_iclass_chk ( ) ;
case ' f ' :
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
if ( fileNameLen < 1 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " no filename found after f " ) ;
2017-12-17 02:13:10 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' e ' :
use_elite = true ;
cmdp + + ;
break ;
2018-07-01 04:47:07 +08:00
case ' c ' :
use_credit_key = true ;
cmdp + + ;
break ;
2017-12-17 02:13:10 +08:00
case ' r ' :
use_raw = true ;
cmdp + + ;
break ;
default :
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2017-12-17 02:13:10 +08:00
errors = true ;
break ;
}
2017-12-13 17:18:38 +08:00
}
2017-12-17 02:13:10 +08:00
if ( errors ) return usage_hf_iclass_chk ( ) ;
2017-12-13 17:18:38 +08:00
2017-12-24 17:59:24 +08:00
// Get CSN / UID and CCNR
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " Reading tag CSN " ) ;
2017-12-21 17:13:40 +08:00
for ( uint8_t i = 0 ; i < 10 & & ! got_csn ; i + + ) {
if ( select_only ( CSN , CCNR , false , false ) ) {
got_csn = true ;
2017-12-21 21:30:24 +08:00
} else {
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( WARNING , " one more try \n " ) ;
2017-12-21 17:13:40 +08:00
}
}
if ( ! got_csn ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " can't select card, aborting... " ) ;
2017-12-21 17:13:40 +08:00
return 1 ;
}
2017-12-13 17:18:38 +08:00
2017-12-24 17:59:24 +08:00
// load keys into keyblock
2018-01-02 18:17:31 +08:00
int res = LoadDictionaryKeyFile ( filename , & keyBlock , & keycnt ) ;
2017-12-24 17:59:24 +08:00
if ( res > 0 ) {
free ( keyBlock ) ;
return 1 ;
}
2018-01-02 18:17:31 +08:00
pre = calloc ( keycnt , sizeof ( iclass_premac_t ) ) ;
2017-12-21 17:13:40 +08:00
if ( ! pre ) {
free ( keyBlock ) ;
return 1 ;
}
2017-12-24 17:59:24 +08:00
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " Generating diversified keys, MAC " ) ;
2018-07-01 17:56:08 +08:00
if ( use_elite )
PrintAndLogEx ( SUCCESS , " Using elite algo " ) ;
if ( use_raw )
PrintAndLogEx ( SUCCESS , " Using raw mode " ) ;
2018-07-04 19:08:59 +08:00
PrintAndLogEx ( SUCCESS , " Searching for %s key " , ( use_credit_key ) ? " CREDIT " : " DEBIT " ) ;
2018-07-01 17:56:08 +08:00
PrintAndLogEx ( SUCCESS , " Tag info " ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " CSN | %s " , sprint_hex ( CSN , sizeof ( CSN ) ) ) ;
PrintAndLogEx ( SUCCESS , " CCNR | %s " , sprint_hex ( CCNR , sizeof ( CCNR ) ) ) ;
2017-12-24 17:59:24 +08:00
res = GenerateMacFromKeyFile ( CSN , CCNR , use_raw , use_elite , keyBlock , keycnt , pre ) ;
if ( res > 0 ) {
free ( keyBlock ) ;
free ( pre ) ;
return 1 ;
2017-12-21 17:13:40 +08:00
}
2017-12-24 17:59:24 +08:00
2018-11-06 05:30:46 +08:00
//PrintPreCalcMac(keyBlock, keycnt, pre);
2017-12-21 17:13:40 +08:00
// max 42 keys inside USB_COMMAND. 512/4 = 103 mac
uint32_t chunksize = keycnt > ( USB_CMD_DATA_SIZE / 4 ) ? ( USB_CMD_DATA_SIZE / 4 ) : keycnt ;
bool lastChunk = false ;
// main keychunk loop
for ( uint32_t i = 0 ; i < keycnt ; i + = chunksize ) {
uint64_t t2 = msclock ( ) ;
2018-01-06 00:41:11 +08:00
uint8_t timeout = 0 ;
2017-12-21 21:30:24 +08:00
2017-12-21 17:13:40 +08:00
if ( ukbhit ( ) ) {
int gc = getchar ( ) ; ( void ) gc ;
2018-07-01 04:47:07 +08:00
PrintAndLogEx ( NORMAL , " \n [!] Aborted via keyboard! \n " ) ;
2017-12-21 17:13:40 +08:00
goto out ;
}
uint32_t keys = ( ( keycnt - i ) > chunksize ) ? chunksize : keycnt - i ;
// last chunk?
if ( keys = = keycnt - i )
lastChunk = true ;
UsbCommand c = { CMD_ICLASS_CHECK_KEYS , { ( lastChunk < < 8 ) , keys , 0 } } ;
2018-07-01 04:47:07 +08:00
// bit 16
// - 1 indicates credit key
// - 0 indicates debit key (default)
c . arg [ 0 ] | = ( use_credit_key < < 16 ) ;
2018-02-08 16:30:34 +08:00
memcpy ( c . d . asBytes , pre + i , 4 * keys ) ;
2017-12-21 17:13:40 +08:00
clearCommandBuffer ( ) ;
SendCommand ( & c ) ;
UsbCommand resp ;
while ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
timeout + + ;
2018-03-06 03:07:29 +08:00
printf ( " . " ) ; fflush ( stdout ) ;
2017-12-21 17:13:40 +08:00
if ( timeout > 120 ) {
2018-07-01 04:47:07 +08:00
PrintAndLogEx ( WARNING , " \n No response from Proxmark. Aborting... " ) ;
2017-12-21 17:13:40 +08:00
goto out ;
2017-12-13 17:18:38 +08:00
}
2017-12-21 17:13:40 +08:00
}
uint8_t found = resp . arg [ 1 ] & 0xFF ;
uint8_t isOK = resp . arg [ 0 ] & 0xFF ;
t2 = msclock ( ) - t2 ;
switch ( isOK ) {
case 1 : {
found_debit = true ;
2017-12-15 22:37:00 +08:00
2018-07-01 04:47:07 +08:00
PrintAndLogEx ( NORMAL , " \n [-] Chunk [%d/%d]: %.1fs [%s] found key %s (index %u) "
2017-12-21 21:30:24 +08:00
, i
, keycnt
2017-12-21 17:13:40 +08:00
, ( float ) ( t2 / 1000.0 )
2018-07-01 04:47:07 +08:00
, ( use_credit_key ) ? " credit " : " debit "
2017-12-21 17:13:40 +08:00
, sprint_hex ( keyBlock + ( i + found ) * 8 , 8 )
, found
) ;
break ;
2017-12-13 17:18:38 +08:00
}
2017-12-21 17:13:40 +08:00
case 0 : {
2018-07-01 04:47:07 +08:00
PrintAndLogEx ( NORMAL , " \n [-] Chunk [%d/%d] : %.1fs [%s] "
, i
, keycnt
, ( float ) ( t2 / 1000.0 )
, ( use_credit_key ) ? " credit " : " debit "
) ;
2017-12-13 17:18:38 +08:00
break ;
2017-12-21 17:13:40 +08:00
}
case 99 : {
}
default : break ;
}
2017-12-13 17:18:38 +08:00
2017-12-21 17:13:40 +08:00
// both keys found.
2017-12-24 17:59:24 +08:00
if ( found_debit ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " All keys found, exiting " ) ;
2017-12-21 17:13:40 +08:00
break ;
}
} // end chunks of keys
out :
2017-12-13 17:18:38 +08:00
t1 = msclock ( ) - t1 ;
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( SUCCESS , " \n Time in iclass checkkeys: %.0f seconds \n " , ( float ) t1 / 1000.0 ) ;
2017-12-13 17:18:38 +08:00
DropField ( ) ;
2017-12-21 17:13:40 +08:00
free ( pre ) ;
2017-12-13 17:18:38 +08:00
free ( keyBlock ) ;
return 0 ;
}
2018-01-02 18:17:31 +08:00
2017-12-24 17:59:24 +08:00
static int cmp_uint32 ( const void * a , const void * b ) {
2018-01-02 18:17:31 +08:00
const iclass_prekey_t * x = ( const iclass_prekey_t * ) a ;
const iclass_prekey_t * y = ( const iclass_prekey_t * ) b ;
uint32_t mx = bytes_to_num ( ( uint8_t * ) x - > mac , 4 ) ;
uint32_t my = bytes_to_num ( ( uint8_t * ) y - > mac , 4 ) ;
if ( mx < my )
2017-12-24 17:59:24 +08:00
return - 1 ;
else
2018-01-02 18:17:31 +08:00
return mx > my ;
2017-12-24 17:59:24 +08:00
}
2018-01-02 18:17:31 +08:00
// this method tries to identify in which configuration mode a iClass / iClass SE reader is in.
// Standard or Elite / HighSecurity mode. It uses a default key dictionary list in order to work.
int CmdHFiClassLookUp ( const char * Cmd ) {
2017-12-24 17:59:24 +08:00
2018-01-02 18:17:31 +08:00
uint8_t CSN [ 8 ] ;
uint8_t EPURSE [ 8 ] ;
uint8_t MACS [ 8 ] ;
2018-02-04 07:52:29 +08:00
uint8_t CCNR [ 12 ] ;
2018-01-02 18:17:31 +08:00
uint8_t MAC_TAG [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
2017-12-24 17:59:24 +08:00
// elite key, raw key, standard key
bool use_elite = false ;
bool use_raw = false ;
bool errors = false ;
uint8_t cmdp = 0x00 ;
char filename [ FILE_PATH_SIZE ] = { 0 } ;
uint8_t fileNameLen = 0 ;
uint8_t * keyBlock = NULL ;
2018-01-02 18:17:31 +08:00
iclass_prekey_t * prekey = NULL ;
int keycnt = 0 , len = 0 ;
2017-12-24 17:59:24 +08:00
2018-01-31 23:50:41 +08:00
// if empty string
if ( strlen ( Cmd ) = = 0 ) errors = true ;
2017-12-24 17:59:24 +08:00
// time
uint64_t t1 = msclock ( ) ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
2018-07-01 04:47:07 +08:00
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2017-12-24 17:59:24 +08:00
case ' h ' :
2018-01-31 23:50:41 +08:00
return usage_hf_iclass_lookup ( ) ;
2017-12-24 17:59:24 +08:00
case ' f ' :
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
if ( fileNameLen < 1 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " No filename found after f " ) ;
2017-12-24 17:59:24 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
2018-01-02 18:17:31 +08:00
case ' u ' :
param_gethex_ex ( Cmd , cmdp + 1 , CSN , & len ) ;
if ( len > > 1 ! = sizeof ( CSN ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Wrong CSN length, expected %d got [%d] " , sizeof ( CSN ) , len > > 1 ) ;
2018-01-02 18:17:31 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' m ' :
param_gethex_ex ( Cmd , cmdp + 1 , MACS , & len ) ;
if ( len > > 1 ! = sizeof ( MACS ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Wrong MACS length, expected %d got [%d] " , sizeof ( MACS ) , len > > 1 ) ;
2018-01-02 18:17:31 +08:00
errors = true ;
} else {
memcpy ( MAC_TAG , MACS + 4 , 4 ) ;
}
cmdp + = 2 ;
break ;
case ' p ' :
param_gethex_ex ( Cmd , cmdp + 1 , EPURSE , & len ) ;
if ( len > > 1 ! = sizeof ( EPURSE ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " Wrong EPURSE length, expected %d got [%d] " , sizeof ( EPURSE ) , len > > 1 ) ;
2018-01-02 18:17:31 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
break ;
2017-12-24 17:59:24 +08:00
case ' e ' :
use_elite = true ;
cmdp + + ;
break ;
case ' r ' :
use_raw = true ;
cmdp + + ;
break ;
default :
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2017-12-24 17:59:24 +08:00
errors = true ;
break ;
}
}
2018-01-31 23:50:41 +08:00
if ( errors ) return usage_hf_iclass_lookup ( ) ;
2018-01-02 18:17:31 +08:00
2018-02-04 07:52:29 +08:00
// stupid copy.. CCNR is a combo of epurse and reader nonce
memcpy ( CCNR , EPURSE , 8 ) ;
memcpy ( CCNR + 8 , MACS , 4 ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " CSN | %s " , sprint_hex ( CSN , sizeof ( CSN ) ) ) ;
PrintAndLogEx ( SUCCESS , " Epurse | %s " , sprint_hex ( EPURSE , sizeof ( EPURSE ) ) ) ;
PrintAndLogEx ( SUCCESS , " MACS | %s " , sprint_hex ( MACS , sizeof ( MACS ) ) ) ;
PrintAndLogEx ( SUCCESS , " CCNR | %s " , sprint_hex ( CCNR , sizeof ( CCNR ) ) ) ;
PrintAndLogEx ( SUCCESS , " MAC_TAG | %s " , sprint_hex ( MAC_TAG , sizeof ( MAC_TAG ) ) ) ;
2018-01-02 18:17:31 +08:00
int res = LoadDictionaryKeyFile ( filename , & keyBlock , & keycnt ) ;
2017-12-24 17:59:24 +08:00
if ( res > 0 ) {
free ( keyBlock ) ;
return 1 ;
}
2018-01-02 18:17:31 +08:00
//iclass_prekey_t
prekey = calloc ( keycnt , sizeof ( iclass_prekey_t ) ) ;
if ( ! prekey ) {
2017-12-24 17:59:24 +08:00
free ( keyBlock ) ;
return 1 ;
}
2018-01-02 18:17:31 +08:00
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " Generating diversified keys and MAC " ) ;
2018-02-04 07:52:29 +08:00
res = GenerateFromKeyFile ( CSN , CCNR , use_raw , use_elite , keyBlock , keycnt , prekey ) ;
2017-12-24 17:59:24 +08:00
if ( res > 0 ) {
free ( keyBlock ) ;
2018-01-02 18:17:31 +08:00
free ( prekey ) ;
2017-12-24 17:59:24 +08:00
return 1 ;
}
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " Sorting " ) ;
2018-01-02 18:17:31 +08:00
// sort mac list.
qsort ( prekey , keycnt , sizeof ( iclass_prekey_t ) , cmp_uint32 ) ;
//PrintPreCalc(prekey, keycnt);
2017-12-24 17:59:24 +08:00
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( FAILED , " Searching " ) ;
2018-01-02 18:17:31 +08:00
iclass_prekey_t * item ;
iclass_prekey_t lookup ;
memcpy ( lookup . mac , MAC_TAG , 4 ) ;
2017-12-24 17:59:24 +08:00
2018-07-01 04:47:07 +08:00
// binsearch
2018-01-02 18:17:31 +08:00
item = ( iclass_prekey_t * ) bsearch ( & lookup , prekey , keycnt , sizeof ( iclass_prekey_t ) , cmp_uint32 ) ;
2018-07-01 04:47:07 +08:00
if ( item ! = NULL )
2018-04-27 17:22:30 +08:00
PrintAndLogEx ( SUCCESS , " \n [debit] found key %s " , sprint_hex ( item - > key , 8 ) ) ;
2017-12-24 17:59:24 +08:00
t1 = msclock ( ) - t1 ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " \n Time in iclass : %.0f seconds \n " , ( float ) t1 / 1000.0 ) ;
2017-12-24 17:59:24 +08:00
DropField ( ) ;
2018-01-02 18:17:31 +08:00
free ( prekey ) ;
2017-12-24 17:59:24 +08:00
free ( keyBlock ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2017-12-24 17:59:24 +08:00
return 0 ;
}
2018-01-02 18:17:31 +08:00
int LoadDictionaryKeyFile ( char * filename , uint8_t * * keys , int * keycnt ) {
2017-12-24 17:59:24 +08:00
char buf [ 17 ] ;
FILE * f ;
uint8_t * p ;
int keyitems = 0 ;
if ( ! ( f = fopen ( filename , " r " ) ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( ERR , " file: %s: not found or locked. " , filename ) ;
2018-02-08 17:32:14 +08:00
return 1 ;
2017-12-24 17:59:24 +08:00
}
while ( fgets ( buf , sizeof ( buf ) , f ) ) {
if ( strlen ( buf ) < 16 | | buf [ 15 ] = = ' \n ' )
continue ;
//goto next line
while ( fgetc ( f ) ! = ' \n ' & & ! feof ( f ) ) { } ;
//The line start with # is comment, skip
if ( buf [ 0 ] = = ' # ' ) continue ;
// doesn't this only test first char only?
if ( ! isxdigit ( buf [ 0 ] ) ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( ERR , " file content error. '%s' must include 16 HEX symbols " , buf ) ;
2017-12-24 17:59:24 +08:00
continue ;
}
// null terminator (skip the rest of the line)
buf [ 16 ] = 0 ;
p = realloc ( * keys , 8 * ( keyitems + = 64 ) ) ;
if ( ! p ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , _RED_ ( [ ! ] ) " cannot allocate memory for default keys " ) ;
2017-12-24 17:59:24 +08:00
fclose ( f ) ;
return 2 ;
}
* keys = p ;
memset ( * keys + 8 * ( * keycnt ) , 0 , 8 ) ;
num_to_bytes ( strtoull ( buf , NULL , 16 ) , 8 , * keys + 8 * ( * keycnt ) ) ;
( * keycnt ) + + ;
memset ( buf , 0 , sizeof ( buf ) ) ;
}
fclose ( f ) ;
2018-07-01 04:47:07 +08:00
PrintAndLogEx ( NORMAL , _BLUE_ ( [ + ] ) " Loaded " _GREEN_ ( % 2 d ) " keys from %s " , * keycnt , filename ) ;
2017-12-24 17:59:24 +08:00
return 0 ;
}
// precalc diversified keys and their MAC
2018-01-02 18:17:31 +08:00
int GenerateMacFromKeyFile ( uint8_t * CSN , uint8_t * CCNR , bool use_raw , bool use_elite , uint8_t * keys , int keycnt , iclass_premac_t * list ) {
2017-12-24 17:59:24 +08:00
uint8_t key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
for ( int i = 0 ; i < keycnt ; i + + ) {
memcpy ( key , keys + 8 * i , 8 ) ;
if ( use_raw )
memcpy ( div_key , key , 8 ) ;
else
HFiClassCalcDivKey ( CSN , key , div_key , use_elite ) ;
2018-01-02 18:17:31 +08:00
doMAC ( CCNR , div_key , list [ i ] . mac ) ;
}
return 0 ;
}
int GenerateFromKeyFile ( uint8_t * CSN , uint8_t * CCNR , bool use_raw , bool use_elite , uint8_t * keys , int keycnt , iclass_prekey_t * list ) {
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
for ( int i = 0 ; i < keycnt ; i + + ) {
memcpy ( list [ i ] . key , keys + 8 * i , 8 ) ;
// generate diversifed key
if ( use_raw )
memcpy ( div_key , list [ i ] . key , 8 ) ;
else
HFiClassCalcDivKey ( CSN , list [ i ] . key , div_key , use_elite ) ;
// generate MAC
doMAC ( CCNR , div_key , list [ i ] . mac ) ;
2017-12-24 17:59:24 +08:00
}
return 0 ;
}
// print diversified keys
2018-01-02 18:17:31 +08:00
void PrintPreCalcMac ( uint8_t * keys , int keycnt , iclass_premac_t * pre_list ) {
2017-12-24 17:59:24 +08:00
2018-01-02 18:17:31 +08:00
iclass_prekey_t * b = calloc ( keycnt , sizeof ( iclass_prekey_t ) ) ;
if ( ! b )
return ;
for ( int i = 0 ; i < keycnt ; i + + ) {
memcpy ( b [ i ] . key , keys + 8 * i , 8 ) ;
memcpy ( b [ i ] . mac , pre_list [ i ] . mac , 4 ) ;
}
PrintPreCalc ( b , keycnt ) ;
free ( b ) ;
}
void PrintPreCalc ( iclass_prekey_t * list , int itemcnt ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " -----+------------------+--------- " ) ;
PrintAndLogEx ( NORMAL , " #key | key | mac " ) ;
PrintAndLogEx ( NORMAL , " -----+------------------+--------- " ) ;
2018-01-02 18:17:31 +08:00
for ( int i = 0 ; i < itemcnt ; i + + ) {
2017-12-24 17:59:24 +08:00
if ( i < 10 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " [%2d] | %016 " PRIx64 " | %08 " PRIx32 , i , bytes_to_num ( list [ i ] . key , 8 ) , bytes_to_num ( list [ i ] . mac , 4 ) ) ;
2017-12-24 17:59:24 +08:00
} else if ( i = = 10 ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " ... skip printing the rest " ) ;
2017-12-24 17:59:24 +08:00
}
}
}
2017-12-13 17:18:38 +08:00
2018-02-05 00:19:08 +08:00
static void permute ( uint8_t * data , uint8_t len , uint8_t * output ) {
# define KEY_SIZE 8
if ( len > KEY_SIZE ) {
for ( uint8_t m = 0 ; m < len ; m + = KEY_SIZE ) {
permute ( data + m , KEY_SIZE , output + m ) ;
}
return ;
}
if ( len ! = KEY_SIZE ) {
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( NORMAL , " [!] wrong key size \n " ) ;
2018-02-05 00:19:08 +08:00
return ;
}
uint8_t i , j , p , mask ;
for ( i = 0 ; i < KEY_SIZE ; + + i ) {
p = 0 ;
mask = 0x80 > > i ;
for ( j = 0 ; j < KEY_SIZE ; + + j ) {
p > > = 1 ;
if ( data [ j ] & mask )
p | = 0x80 ;
}
output [ i ] = p ;
}
}
static void permute_rev ( uint8_t * data , uint8_t len , uint8_t * output ) {
permute ( data , len , output ) ;
permute ( output , len , data ) ;
permute ( data , len , output ) ;
}
static void simple_crc ( uint8_t * data , uint8_t len , uint8_t * output ) {
uint8_t crc = 0 ;
for ( uint8_t i = 0 ; i < len ; + + i ) {
// seventh byte contains the crc.
if ( ( i & 0x7 ) = = 0x7 ) {
output [ i ] = crc ^ 0xFF ;
crc = 0 ;
} else {
output [ i ] = data [ i ] ;
crc ^ = data [ i ] ;
}
}
}
// DES doesn't use the MSB.
static void shave ( uint8_t * data , uint8_t len ) {
for ( uint8_t i = 0 ; i < len ; + + i )
data [ i ] & = 0xFE ;
}
static void generate_rev ( uint8_t * data , uint8_t len ) {
uint8_t * key = calloc ( len , 1 ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " input permuted key | %s \n " , sprint_hex ( data , len ) ) ;
2018-02-05 00:19:08 +08:00
permute_rev ( data , len , key ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " unpermuted key | %s \n " , sprint_hex ( key , len ) ) ;
2018-02-05 00:19:08 +08:00
shave ( key , len ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " key | %s \n " , sprint_hex ( key , len ) ) ;
2018-02-05 00:19:08 +08:00
free ( key ) ;
}
static void generate ( uint8_t * data , uint8_t len ) {
uint8_t * key = calloc ( len , 1 ) ;
uint8_t * pkey = calloc ( len , 1 ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " input key | %s \n " , sprint_hex ( data , len ) ) ;
2018-02-05 00:19:08 +08:00
permute ( data , len , pkey ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " permuted key | %s \n " , sprint_hex ( pkey , len ) ) ;
2018-02-05 00:19:08 +08:00
simple_crc ( pkey , len , key ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " CRC'ed key | %s \n " , sprint_hex ( key , len ) ) ;
2018-02-05 00:19:08 +08:00
free ( key ) ;
free ( pkey ) ;
}
int CmdHFiClassPermuteKey ( const char * Cmd ) {
uint8_t key [ 8 ] = { 0 } ;
uint8_t key_std_format [ 8 ] = { 0 } ;
uint8_t key_iclass_format [ 8 ] = { 0 } ;
uint8_t data [ 16 ] = { 0 } ;
bool isReverse = false ;
int len = 0 ;
char cmdp = param_getchar ( Cmd , 0 ) ;
if ( strlen ( Cmd ) = = 0 | | cmdp = = ' h ' | | cmdp = = ' H ' ) return usage_hf_iclass_permutekey ( ) ;
isReverse = ( cmdp = = ' r ' | | cmdp = = ' R ' ) ;
param_gethex_ex ( Cmd , 1 , data , & len ) ;
if ( len % 2 ) return usage_hf_iclass_permutekey ( ) ;
len > > = 1 ;
memcpy ( key , data , 8 ) ;
if ( isReverse ) {
generate_rev ( data , len ) ;
permutekey_rev ( key , key_std_format ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " holiman iclass key | %s \n " , sprint_hex ( key_std_format , 8 ) ) ;
2018-02-05 00:19:08 +08:00
}
else {
generate ( data , len ) ;
permutekey ( key , key_iclass_format ) ;
2018-02-21 14:47:33 +08:00
PrintAndLogEx ( SUCCESS , " holiman std key | %s \n " , sprint_hex ( key_iclass_format , 8 ) ) ;
2018-02-05 00:19:08 +08:00
}
return 0 ;
}
2016-01-20 02:31:34 +08:00
static command_t CommandTable [ ] = {
2018-01-31 23:50:41 +08:00
{ " help " , CmdHelp , 1 , " This help " } ,
{ " calcnewkey " , CmdHFiClassCalcNewKey , 1 , " [options..] Calc Diversified keys (blocks 3 & 4) to write new keys " } ,
{ " chk " , CmdHFiClassCheckKeys , 1 , " Check keys " } ,
{ " clone " , CmdHFiClassCloneTag , 0 , " [options..] Authenticate and Clone from iClass bin file " } ,
{ " decrypt " , CmdHFiClassDecrypt , 1 , " [f <fname>] Decrypt tagdump " } ,
{ " dump " , CmdHFiClassReader_Dump , 0 , " [options..] Authenticate and Dump iClass tag's AA1 " } ,
{ " eload " , CmdHFiClassELoad , 0 , " [f <fname>] (experimental) Load data into iClass emulator memory " } ,
{ " encryptblk " , CmdHFiClassEncryptBlk , 1 , " <BlockData> Encrypt given block data " } ,
{ " list " , CmdHFiClassList , 0 , " (Deprecated) List iClass history " } ,
{ " loclass " , CmdHFiClass_loclass , 1 , " [options..] Use loclass to perform bruteforce of reader attack dump " } ,
{ " lookup " , CmdHFiClassLookUp , 0 , " [options..] Uses authentication trace to check for key in dictionary file " } ,
{ " managekeys " , CmdHFiClassManageKeys , 1 , " [options..] Manage the keys to use with iClass " } ,
2018-02-05 00:19:08 +08:00
{ " permutekey " , CmdHFiClassPermuteKey , 0 , " Permute function from 'heart of darkness' paper " } ,
2018-01-31 23:50:41 +08:00
{ " readblk " , CmdHFiClass_ReadBlock , 0 , " [options..] Authenticate and Read iClass block " } ,
{ " reader " , CmdHFiClassReader , 0 , " Act like an iClass reader " } ,
{ " readtagfile " , CmdHFiClassReadTagFile , 1 , " [options..] Display Content from tagfile " } ,
{ " replay " , CmdHFiClassReader_Replay , 0 , " <mac> Read an iClass tag via Reply Attack " } ,
{ " sim " , CmdHFiClassSim , 0 , " [options..] Simulate iClass tag " } ,
{ " sniff " , CmdHFiClassSniff , 0 , " Eavesdrop iClass communication " } ,
{ " writeblk " , CmdHFiClass_WriteBlock , 0 , " [options..] Authenticate and Write iClass block " } ,
2015-01-05 16:16:06 +08:00
{ NULL , NULL , 0 , NULL }
2011-05-18 20:33:32 +08:00
} ;
2016-01-20 02:31:34 +08:00
int CmdHFiClass ( const char * Cmd ) {
clearCommandBuffer ( ) ;
2015-02-15 04:17:08 +08:00
CmdsParse ( CommandTable , Cmd ) ;
return 0 ;
2011-05-18 20:33:32 +08:00
}
2016-01-20 02:31:34 +08:00
int CmdHelp ( const char * Cmd ) {
2015-02-15 04:17:08 +08:00
CmdsHelp ( CommandTable ) ;
return 0 ;
2011-05-18 20:33:32 +08:00
}