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
2020-02-27 23:35:17 +08:00
// Copyright (C) 2020 Iceman
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"
2019-08-08 22:57:33 +08:00
# include <ctype.h>
# include "cmdparser.h" // command_t
# include "commonutil.h" // ARRAYLEN
# include "cmdtrace.h"
# include "util_posix.h"
# include "comms.h"
# include "mbedtls/des.h"
# include "loclass/cipherutils.h"
# include "loclass/cipher.h"
# include "loclass/ikeys.h"
# include "loclass/elite_crack.h"
2019-08-21 20:41:33 +08:00
# include "fileutils.h"
2019-08-08 22:57:33 +08:00
# include "protocols.h"
2020-02-27 23:35:17 +08:00
# include "cardhelper.h"
2020-02-04 07:41:57 +08:00
# include "wiegand_formats.h"
# include "wiegand_formatutils.h"
2019-08-08 22:57:33 +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
2019-09-08 23:37:14 +08:00
# define ICLASS_AUTH_RETRY 10
2020-02-27 23:35:17 +08:00
# define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin"
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 ] = {
2019-09-05 22:07:25 +08:00
{ 0xAE , 0xA6 , 0x84 , 0xA6 , 0xDA , 0xB2 , 0x32 , 0x78 } ,
{ 0x76 , 0x65 , 0x54 , 0x43 , 0x32 , 0x21 , 0x10 , 0x00 } ,
{ 0x5B , 0x7C , 0x62 , 0xC4 , 0x91 , 0xc1 , 0x1b , 0x39 } ,
{ 0xF0 , 0xE1 , 0xD2 , 0xC3 , 0xB4 , 0xA5 , 0x96 , 0x87 } ,
2019-03-10 07:00:59 +08:00
{ 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 }
2015-10-08 05:00:46 +08:00
} ;
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_sim ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Simulate a iclass legacy/standard tag \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass sim [h] <option> [CSN] \n " ) ;
PrintAndLogEx ( NORMAL , " Options " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
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 " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass sim 0 031FEC8AF7FF12E0 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- execute loclass attack online part " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass sim 2 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- simulate full iClass 2k tag " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass eload f hf-iclass-AA162D30F8FF12F1-dump.bin " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass sim 3 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_eload ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Loads iclass tag-dump into emulator memory on device \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass eload [h] f <filename> \n " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " Options " ) ;
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
PrintAndLogEx ( NORMAL , " f <filename> : filename of dump " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass eload f hf-iclass-AA162D30F8FF12F1-dump.bin " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_decrypt ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " 3DES decrypt data \n " ) ;
PrintAndLogEx ( NORMAL , " This is naive implementation, it tries to decrypt every block after block 6. " ) ;
2019-03-10 06:35:06 +08:00
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 " ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " in the resources directory. The file should be 16 bytes binary data \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass decrypt d <enc data> f <tagdump> k <transport key> \n " ) ;
PrintAndLogEx ( NORMAL , " Options " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
PrintAndLogEx ( NORMAL , " d <encrypted blk> : 16 bytes hex " ) ;
PrintAndLogEx ( NORMAL , " f <filename> : filename of dump " ) ;
PrintAndLogEx ( NORMAL , " k <transport key> : 16 bytes hex " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass decrypt f hf-iclass-AA162D30F8FF12F1-dump.bin " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass decrypt f hf-iclass-AA162D30F8FF12F1-dump.bin k 000102030405060708090a0b0c0d0e0f " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass decrypt d 1122334455667788 k 000102030405060708090a0b0c0d0e0f " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_encrypt ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " 3DES encrypt data \n " ) ;
PrintAndLogEx ( NORMAL , " OBS! In order to use this function, the file " _YELLOW_ ( " 'iclass_decryptionkey.bin' " ) " must reside " ) ;
PrintAndLogEx ( NORMAL , " in the resources directory. The file should be 16 bytes binary data \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass encrypt d <blockdata> k <transport key> \n " ) ;
PrintAndLogEx ( NORMAL , " Options " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
PrintAndLogEx ( NORMAL , " d <block data> : 16 bytes hex " ) ;
PrintAndLogEx ( NORMAL , " k <transport key> : 16 bytes hex " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass encrypt d 0102030405060708 " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass encrypt d 0102030405060708 k 00112233445566778899AABBCCDDEEFF " ) ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_dump ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Dump all memory from a iClass tag \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass dump f <fileName> k <key> c <creditkey> [e|r|v] \n " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
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 " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass dump k 001122334455667B " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass dump k AAAAAAAAAAAAAAAA c 001122334455667B " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass dump k AAAAAAAAAAAAAAAA e " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass dump k 0 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_clone ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Restore data from dumpfile onto a iClass tag \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass clone f <tagfile.bin> b <first block> l <last block> k <KEY> c e|r \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
2019-08-24 11:08:03 +08:00
PrintAndLogEx ( NORMAL , " f <filename> : specify a filename to clone from " ) ;
2020-03-27 18:56:47 +08:00
PrintAndLogEx ( NORMAL , " b <block> : The first block to clone as 2 hex symbols " ) ;
2020-04-03 15:11:12 +08:00
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 " ) ;
2019-08-24 11:08:03 +08:00
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 " ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " r : If 'r' is specified, no computations applied to key (raw) " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass clone f hf-iclass-AA162D30F8FF12F1-dump.bin b 06 l 1A k 1122334455667788 e " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass clone f hf-iclass-AA162D30F8FF12F1-dump b 05 l 19 k 0 " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass clone f hf-iclass-AA162D30F8FF12F1-dump b 06 l 19 k 0 e " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_writeblock ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Write data to a iClass tag \n " ) ;
2020-03-27 18:56:47 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass wrbl b <block> d <data> k <key> [c|e|r|v] \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
2020-03-27 18:56:47 +08:00
PrintAndLogEx ( NORMAL , " b <block> : The block number as 2 hex symbols " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " d <data> : set the Data to write as 16 hex symbols " ) ;
2020-03-27 18:56:47 +08:00
PrintAndLogEx ( NORMAL , " k <key> : access Key as 16 hex symbols or 1 hex to select key from memory " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " c : credit key assumed \n " ) ;
PrintAndLogEx ( NORMAL , " e : elite computations applied to key " ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " r : raw, no computations applied to key (raw) " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " v : verbose output " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass wrbl b 0A d AAAAAAAAAAAAAAAA k 001122334455667B " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass wrbl b 1B d AAAAAAAAAAAAAAAA k 001122334455667B c " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass wrbl b 1B d AAAAAAAAAAAAAAAA k 0 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_readblock ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Read a iClass block from tag \n " ) ;
2019-10-15 04:32:58 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass rdbl b <block> k <key> [c|e|r|v] \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
2019-03-10 06:35:06 +08:00
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: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass rdbl b 06 k 0011223344556677 " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass rdbl b 1B k 0011223344556677 c " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass rdbl b 0A k 0 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_readtagfile ( ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Print a iClass tag-dump file \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iClass readtagfile [f <filename>] [s <startblock>] [e <endblock>] [v] \n " ) ;
2020-04-03 14:36:55 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " f <filename> filename of dump " ) ;
PrintAndLogEx ( NORMAL , " s <startblock> print from this block (default block6) " ) ;
PrintAndLogEx ( NORMAL , " e <endblock> end printing at this block (default 0, ALL) " ) ;
PrintAndLogEx ( NORMAL , " v verbose output " ) ;
2020-04-03 14:36:55 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass readtagfile f hf-iclass-AA162D30F8FF12F1-dump.bin " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass readtagfile s 1 f hf-iclass-AA162D30F8FF12F1-dump.bin " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_calc_newkey ( void ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Calculate new key for updating \n " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass calc_newkey o <old key> n <new key> s [csn] e \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
PrintAndLogEx ( NORMAL , " o <old key> : *specify a key as 16 hex symbols or a key number as 1 symbol " ) ;
PrintAndLogEx ( NORMAL , " n <new key> : *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 " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- e key to e key given csn " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass calcnewkey o 1122334455667788 n 2233445566778899 s deadbeafdeadbeaf ee " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- std key to e key read csn " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass calcnewkey o 1122334455667788 n 2233445566778899 e " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- std to std read csn " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass calcnewkey o 1122334455667788 n 2233445566778899 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " \n NOTE: * = required " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ; ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_managekeys ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Manage iClass Keys in client memory: \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass managekeys n [keynbr] k [key] f [filename] s l p \n " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
2019-08-24 11:08:03 +08:00
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 " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- set key " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass managekeys n 0 k 1122334455667788 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- save key file " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass managekeys f mykeys.bin s " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- load key file " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass managekeys f mykeys.bin l " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " -- print keys " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass managekeys p " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 01:23:05 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_reader ( void ) {
2019-07-11 19:01:34 +08:00
PrintAndLogEx ( NORMAL , " Act as a Iclass reader. Look for iClass tags until Enter or the pm3 button is pressed \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass reader [h] [1] \n " ) ;
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " 1 read only 1 tag " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass reader 1 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 05:21:16 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_replay ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Replay a collected mac message \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass replay [h] <mac> \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " <mac> Mac bytes to replay (8 hexsymbols) " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass replay 00112233 " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 05:21:16 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_sniff ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Sniff the communication between reader and tag \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass sniff [h] \n " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass sniff " ) ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-01-11 05:21:16 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_loclass ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Execute the offline part of loclass attack " ) ;
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 \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass loclass [h] [t [l]] [f <filename>] \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " t Perform self-test " ) ;
PrintAndLogEx ( NORMAL , " t l Perform self-test, including long ones " ) ;
PrintAndLogEx ( NORMAL , " f <filename> Bruteforce iclass dumpfile " ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass loclass f iclass-dump.bin " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass loclass t " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2017-08-18 16:23:46 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_chk ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass chk [h|e|r] [f (*.dic)] \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " f <filename> Dictionary file with default iclass keys " ) ;
PrintAndLogEx ( NORMAL , " r raw " ) ;
PrintAndLogEx ( NORMAL , " e elite " ) ;
PrintAndLogEx ( NORMAL , " c credit key (if not use, default is debit) " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass chk f dictionaries/iclass_default_keys.dic " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass chk f dictionaries/iclass_default_keys.dic e " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ; ;
2017-12-13 17:18:38 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_lookup ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Lookup keys takes some sniffed trace data and tries to verify what key was used against a dictionary file \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass lookup [h|e|r] [f (*.dic)] [u <csn>] [p <epurse>] [m <macs>] \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
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 " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass lookup u 9655a400f8ff12e0 p f0ffffffffffffff m 0000000089cb984b f dictionaries/iclass_default_keys.dic " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass lookup u 9655a400f8ff12e0 p f0ffffffffffffff m 0000000089cb984b f dictionaries/iclass_default_keys.dic e " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2018-01-31 23:50:41 +08:00
}
2019-04-10 19:06:05 +08:00
static int usage_hf_iclass_permutekey ( void ) {
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " Permute function from 'heart of darkness' paper. \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass permute [h] <r|f> <bytes> \n " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " Options: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , " h Show this help " ) ;
PrintAndLogEx ( NORMAL , " r reverse permuted key " ) ;
PrintAndLogEx ( NORMAL , " f permute key " ) ;
PrintAndLogEx ( NORMAL , " <bytes> input bytes " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
2020-04-03 15:23:34 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass permute r 0123456789abcdef " ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2018-02-05 00:19:08 +08:00
}
2017-08-18 16:23:46 +08:00
2019-08-08 22:57:33 +08:00
// iclass / picopass chip config structures and shared routines
typedef struct {
uint8_t app_limit ; //[8]
uint8_t otp [ 2 ] ; //[9-10]
uint8_t block_writelock ; //[11]
uint8_t chip_config ; //[12]
uint8_t mem_config ; //[13]
uint8_t eas ; //[14]
uint8_t fuses ; //[15]
} picopass_conf_block ;
typedef struct {
uint8_t csn [ 8 ] ;
picopass_conf_block conf ;
uint8_t epurse [ 8 ] ;
uint8_t key_d [ 8 ] ;
uint8_t key_c [ 8 ] ;
uint8_t app_issuer_area [ 8 ] ;
} picopass_hdr ;
2020-02-27 23:35:17 +08:00
typedef enum {
None = 0 ,
DES ,
RFU ,
TRIPLEDES
} BLOCK79ENCRYPTION ;
2019-08-08 22:57:33 +08:00
static uint8_t isset ( uint8_t val , uint8_t mask ) {
return ( val & mask ) ;
}
static uint8_t notset ( uint8_t val , uint8_t mask ) {
return ! ( val & mask ) ;
}
static void fuse_config ( const picopass_hdr * hdr ) {
uint8_t fuses = hdr - > conf . fuses ;
if ( isset ( fuses , FUSE_FPERS ) )
2019-08-17 21:59:11 +08:00
PrintAndLogEx ( SUCCESS , " Mode: Personalization [Programmable] " ) ;
2019-08-08 22:57:33 +08:00
else
2019-12-01 00:06:03 +08:00
PrintAndLogEx ( SUCCESS , " Mode: Application [Locked] " ) ;
2019-08-08 22:57:33 +08:00
if ( isset ( fuses , FUSE_CODING1 ) ) {
2019-12-01 00:06:03 +08:00
PrintAndLogEx ( SUCCESS , " Coding: RFU " ) ;
2019-08-08 22:57:33 +08:00
} else {
if ( isset ( fuses , FUSE_CODING0 ) )
2019-12-01 00:06:03 +08:00
PrintAndLogEx ( SUCCESS , " Coding: ISO 14443-2 B/ISO 15693 " ) ;
2019-08-08 22:57:33 +08:00
else
2019-12-01 00:06:03 +08:00
PrintAndLogEx ( SUCCESS , " Coding: ISO 14443B only " ) ;
2019-08-08 22:57:33 +08:00
}
// 1 1
2019-08-17 21:59:11 +08:00
if ( isset ( fuses , FUSE_CRYPT1 ) & & isset ( fuses , FUSE_CRYPT0 ) ) PrintAndLogEx ( SUCCESS , " Crypt: Secured page, keys not locked " ) ;
2019-08-08 22:57:33 +08:00
// 1 0
2019-08-17 21:59:11 +08:00
if ( isset ( fuses , FUSE_CRYPT1 ) & & notset ( fuses , FUSE_CRYPT0 ) ) PrintAndLogEx ( NORMAL , " Crypt: Secured page, keys locked " ) ;
2019-08-08 22:57:33 +08:00
// 0 1
2019-08-17 21:59:11 +08:00
if ( notset ( fuses , FUSE_CRYPT1 ) & & isset ( fuses , FUSE_CRYPT0 ) ) PrintAndLogEx ( SUCCESS , " Crypt: Non secured page " ) ;
2019-08-08 22:57:33 +08:00
// 0 0
2019-08-17 21:59:11 +08:00
if ( notset ( fuses , FUSE_CRYPT1 ) & & notset ( fuses , FUSE_CRYPT0 ) ) PrintAndLogEx ( NORMAL , " Crypt: No auth possible. Read only if RA is enabled " ) ;
2019-08-08 22:57:33 +08:00
if ( isset ( fuses , FUSE_RA ) )
2019-12-01 00:06:03 +08:00
PrintAndLogEx ( SUCCESS , " RA: Read access enabled " ) ;
2019-08-08 22:57:33 +08:00
else
2019-08-17 21:59:11 +08:00
PrintAndLogEx ( WARNING , " RA: Read access not enabled " ) ;
2019-08-08 22:57:33 +08:00
}
static void getMemConfig ( uint8_t mem_cfg , uint8_t chip_cfg , uint8_t * max_blk , uint8_t * app_areas , uint8_t * kb ) {
// mem-bit 5, mem-bit 7, chip-bit 4: defines chip type
uint8_t k16 = isset ( mem_cfg , 0x80 ) ;
//uint8_t k2 = isset(mem_cfg, 0x08);
uint8_t book = isset ( mem_cfg , 0x20 ) ;
if ( isset ( chip_cfg , 0x10 ) & & ! k16 & & ! book ) {
* kb = 2 ;
* app_areas = 2 ;
* max_blk = 31 ;
} else if ( isset ( chip_cfg , 0x10 ) & & k16 & & ! book ) {
* kb = 16 ;
* app_areas = 2 ;
* max_blk = 255 ; //16kb
} else if ( notset ( chip_cfg , 0x10 ) & & ! k16 & & ! book ) {
* kb = 16 ;
* app_areas = 16 ;
* max_blk = 255 ; //16kb
} else if ( isset ( chip_cfg , 0x10 ) & & k16 & & book ) {
* kb = 32 ;
* app_areas = 3 ;
* max_blk = 255 ; //16kb
} else if ( notset ( chip_cfg , 0x10 ) & & ! k16 & & book ) {
* kb = 32 ;
* app_areas = 17 ;
* max_blk = 255 ; //16kb
} else {
* kb = 32 ;
* app_areas = 2 ;
* max_blk = 255 ;
}
}
static void mem_app_config ( const picopass_hdr * hdr ) {
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 ) ;
if ( applimit < 6 ) applimit = 26 ;
if ( kb = = 2 & & ( applimit > 0x1f ) ) applimit = 26 ;
PrintAndLogEx ( NORMAL , " Mem: %u KBits/%u App Areas (%u * 8 bytes) [%02X] " , kb , app_areas , max_blk , mem ) ;
2019-08-17 21:59:11 +08:00
PrintAndLogEx ( NORMAL , " AA1: blocks 06-%02X " , applimit ) ;
PrintAndLogEx ( NORMAL , " AA2: blocks %02X-%02X " , applimit + 1 , max_blk ) ;
PrintAndLogEx ( NORMAL , " OTP: 0x%02X%02X " , hdr - > conf . otp [ 1 ] , hdr - > conf . otp [ 0 ] ) ;
PrintAndLogEx ( NORMAL , " KeyAccess: " ) ;
2019-08-08 22:57:33 +08:00
uint8_t book = isset ( mem , 0x20 ) ;
if ( book ) {
2019-08-17 21:59:11 +08:00
PrintAndLogEx ( NORMAL , " Read A - Kd " ) ;
PrintAndLogEx ( NORMAL , " Read B - Kc " ) ;
PrintAndLogEx ( NORMAL , " Write A - Kd " ) ;
PrintAndLogEx ( NORMAL , " Write B - Kc " ) ;
PrintAndLogEx ( NORMAL , " Debit - Kd or Kc " ) ;
PrintAndLogEx ( NORMAL , " Credit - Kc " ) ;
2019-08-08 22:57:33 +08:00
} else {
2019-08-17 21:59:11 +08:00
PrintAndLogEx ( NORMAL , " Read A - Kd or Kc " ) ;
PrintAndLogEx ( NORMAL , " Read B - Kd or Kc " ) ;
PrintAndLogEx ( NORMAL , " Write A - Kc " ) ;
PrintAndLogEx ( NORMAL , " Write B - Kc " ) ;
PrintAndLogEx ( NORMAL , " Debit - Kd or Kc " ) ;
PrintAndLogEx ( NORMAL , " Credit - Kc " ) ;
2019-08-08 22:57:33 +08:00
}
}
2020-02-27 23:35:17 +08:00
2019-08-08 22:57:33 +08:00
static void print_picopass_info ( const picopass_hdr * hdr ) {
fuse_config ( hdr ) ;
mem_app_config ( hdr ) ;
}
2020-02-27 23:35:17 +08:00
2019-08-08 22:57:33 +08:00
static void printIclassDumpInfo ( uint8_t * iclass_dump ) {
print_picopass_info ( ( picopass_hdr * ) iclass_dump ) ;
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassList ( const char * Cmd ) {
2019-04-10 18:23:40 +08:00
( void ) Cmd ; // Cmd is not used so far
2019-03-10 06:35:06 +08:00
CmdTraceList ( " iclass " ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2014-04-24 20:13:33 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassSniff ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
char cmdp = tolower ( param_getchar ( Cmd , 0 ) ) ;
2019-03-10 07:00:59 +08:00
if ( cmdp = = ' h ' ) return usage_hf_iclass_sniff ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandNG ( CMD_HF_ICLASS_SNIFF , NULL , 0 ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2011-05-18 20:33:32 +08:00
}
2015-10-08 05:00:46 +08:00
2019-04-13 00:41:14 +08:00
static int CmdHFiClassSim ( const char * Cmd ) {
2015-02-15 04:17:08 +08:00
2019-03-10 06:35:06 +08:00
char cmdp = tolower ( param_getchar ( Cmd , 0 ) ) ;
if ( strlen ( Cmd ) < 1 | | cmdp = = ' h ' ) return usage_hf_iclass_sim ( ) ;
2016-01-20 02:31:34 +08:00
2019-03-10 06:35:06 +08:00
uint8_t simType = 0 ;
uint8_t CSN [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2017-08-19 15:49:41 +08:00
2019-03-10 06:35:06 +08:00
simType = param_get8ex ( Cmd , 0 , 0 , 10 ) ;
2015-02-15 04:17:08 +08:00
2019-03-10 06:35:06 +08:00
if ( simType = = 0 ) {
if ( param_gethex ( Cmd , 1 , CSN , 16 ) ) {
2019-08-17 21:59:11 +08:00
PrintAndLogEx ( ERR , " A CSN should consist of 16 HEX symbols " ) ;
2019-03-10 06:35:06 +08:00
return usage_hf_iclass_sim ( ) ;
}
2020-04-03 13:22:54 +08:00
PrintAndLogEx ( INFO , " simtype: %02x CSN: %s " , simType , sprint_hex ( CSN , 8 ) ) ;
2019-03-10 06:35:06 +08:00
}
2016-01-20 02:31:34 +08:00
2019-03-10 06:35:06 +08:00
if ( simType > 4 ) {
2019-08-17 21:59:11 +08:00
PrintAndLogEx ( ERR , " Undefined simptype %d " , simType ) ;
2019-03-10 06:35:06 +08:00
return usage_hf_iclass_sim ( ) ;
}
2012-06-28 21:38:40 +08:00
2020-02-27 23:35:17 +08:00
// remember to change the define NUM_CSNS to match.
// pre-defined 9 CSN by iceman
2019-03-10 07:00:59 +08:00
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 ,
0xCE , 0xC5 , 0x0F , 0x77 , 0xF7 , 0xFF , 0x12 , 0xE0 ,
0xD2 , 0x5A , 0x82 , 0xF8 , 0xF7 , 0xFF , 0x12 , 0xE0
//0x04, 0x08, 0x9F, 0x78, 0x6E, 0xFF, 0x12, 0xE0
} ;
/* 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
* */
2019-03-10 06:35:06 +08:00
uint8_t tries = 0 ;
2019-03-10 07:00:59 +08:00
switch ( simType ) {
2019-03-10 06:35:06 +08:00
case 2 : {
PrintAndLogEx ( INFO , " Starting iCLASS sim 2 attack (elite mode) " ) ;
2019-07-11 19:01:34 +08:00
PrintAndLogEx ( INFO , " press Enter to cancel " ) ;
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2020-04-03 13:22:54 +08:00
SendCommandMIX ( CMD_HF_ICLASS_SIMULATE , simType , NUM_CSNS , 0 , csns , 8 * NUM_CSNS ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
while ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
2019-03-10 06:35:06 +08:00
tries + + ;
2019-07-11 19:01:34 +08:00
if ( kbd_enter_pressed ( ) ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " \n aborted via keyboard. " ) ;
2019-08-29 13:47:17 +08:00
return PM3_EOPABORTED ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
if ( tries > 20 ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " \n timeout while waiting for reply. " ) ;
2019-08-29 13:47:17 +08:00
return PM3_ETIMEOUT ;
2019-03-10 06:35:06 +08:00
}
}
2019-04-18 05:44:48 +08:00
uint8_t num_mac = resp . oldarg [ 1 ] ;
2019-03-10 07:00:59 +08:00
bool success = ( NUM_CSNS = = num_mac ) ;
2020-04-03 13:22:54 +08:00
PrintAndLogEx ( ( success ) ? SUCCESS : WARNING , " [%c] %d out of %d MAC obtained [%s] " , ( success ) ? ' + ' : ' ! ' , num_mac , NUM_CSNS , ( success ) ? " OK " : " FAIL " ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
if ( num_mac = = 0 )
2019-03-10 06:35:06 +08:00
break ;
size_t datalen = NUM_CSNS * 24 ;
2019-04-05 06:08:36 +08:00
uint8_t * dump = calloc ( datalen , sizeof ( uint8_t ) ) ;
2019-03-10 07:00:59 +08:00
if ( ! dump ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
2019-08-29 13:47:17 +08:00
return PM3_EMALLOC ;
2019-03-10 06:35:06 +08:00
}
memset ( dump , 0 , datalen ) ; //<-- Need zeroes for the EPURSE - field (offical)
uint8_t i = 0 ;
for ( i = 0 ; i < NUM_CSNS ; i + + ) {
//copy CSN
2019-03-10 07:00:59 +08:00
memcpy ( dump + i * 24 , csns + i * 8 , 8 ) ;
2019-03-10 06:35:06 +08:00
//copy epurse
2019-04-18 05:44:48 +08:00
memcpy ( dump + i * 24 + 8 , resp . data . asBytes + i * 16 , 8 ) ;
2019-03-10 06:35:06 +08:00
// NR_MAC (eight bytes from the response) ( 8b csn + 8b epurse == 16)
2019-04-18 05:44:48 +08:00
memcpy ( dump + i * 24 + 16 , resp . data . asBytes + i * 16 + 8 , 8 ) ;
2019-03-10 06:35:06 +08:00
}
/** Now, save to dumpfile **/
2019-04-29 02:42:57 +08:00
saveFile ( " iclass_mac_attack " , " .bin " , dump , datalen ) ;
2019-03-10 06:35:06 +08:00
free ( dump ) ;
break ;
}
case 4 : {
// reader in key roll mode, when it has two keys it alternates when trying to verify.
PrintAndLogEx ( INFO , " Starting iCLASS sim 4 attack (elite mode, reader in key roll mode) " ) ;
2019-07-11 19:01:34 +08:00
PrintAndLogEx ( INFO , " press Enter to cancel " ) ;
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2020-04-03 13:22:54 +08:00
SendCommandMIX ( CMD_HF_ICLASS_SIMULATE , simType , NUM_CSNS , 0 , csns , 8 * NUM_CSNS ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
while ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
2019-03-10 06:35:06 +08:00
tries + + ;
2019-07-11 19:01:34 +08:00
if ( kbd_enter_pressed ( ) ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " \n aborted via keyboard. " ) ;
2019-08-29 13:47:17 +08:00
return PM3_EOPABORTED ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
if ( tries > 20 ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " \n timeout while waiting for reply. " ) ;
2019-08-29 13:47:17 +08:00
return PM3_ETIMEOUT ;
2019-03-10 06:35:06 +08:00
}
}
2019-04-18 05:44:48 +08:00
uint8_t num_mac = resp . oldarg [ 1 ] ;
2019-03-10 07:00:59 +08:00
bool success = ( ( NUM_CSNS * 2 ) = = num_mac ) ;
2020-04-03 13:22:54 +08:00
PrintAndLogEx ( ( success ) ? SUCCESS : WARNING , " [%c] %d out of %d MAC obtained [%s] " , ( success ) ? ' + ' : ' ! ' , num_mac , NUM_CSNS * 2 , ( success ) ? " OK " : " FAIL " ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
if ( num_mac = = 0 )
2019-03-10 06:35:06 +08:00
break ;
size_t datalen = NUM_CSNS * 24 ;
2019-04-05 06:08:36 +08:00
uint8_t * dump = calloc ( datalen , sizeof ( uint8_t ) ) ;
2019-03-10 07:00:59 +08:00
if ( ! dump ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
2019-08-29 13:47:17 +08:00
return PM3_EMALLOC ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
# define MAC_ITEM_SIZE 24
2019-03-10 06:35:06 +08:00
//KEYROLL 1
//Need zeroes for the CC-field
memset ( dump , 0 , datalen ) ;
for ( uint8_t i = 0 ; i < NUM_CSNS ; i + + ) {
// copy CSN
2019-03-10 07:00:59 +08:00
memcpy ( dump + i * MAC_ITEM_SIZE , csns + i * 8 , 8 ) ; //CSN
2019-03-10 06:35:06 +08:00
// copy EPURSE
2019-04-18 05:44:48 +08:00
memcpy ( dump + i * MAC_ITEM_SIZE + 8 , resp . data . asBytes + i * 16 , 8 ) ;
2019-03-10 06:35:06 +08:00
// copy NR_MAC (eight bytes from the response) ( 8b csn + 8b epurse == 16)
2019-04-18 05:44:48 +08:00
memcpy ( dump + i * MAC_ITEM_SIZE + 16 , resp . data . asBytes + i * 16 + 8 , 8 ) ;
2019-03-10 06:35:06 +08:00
}
2019-04-29 02:42:57 +08:00
saveFile ( " iclass_mac_attack_keyroll_A " , " .bin " , dump , datalen ) ;
2019-03-10 06:35:06 +08:00
//KEYROLL 2
memset ( dump , 0 , datalen ) ;
for ( uint8_t i = 0 ; i < NUM_CSNS ; i + + ) {
2019-06-08 00:41:39 +08:00
uint8_t resp_index = ( i + NUM_CSNS ) * 16 ;
2019-03-10 06:35:06 +08:00
// Copy CSN
2019-03-10 07:00:59 +08:00
memcpy ( dump + i * MAC_ITEM_SIZE , csns + i * 8 , 8 ) ;
2019-03-10 06:35:06 +08:00
// copy EPURSE
2019-04-18 05:44:48 +08:00
memcpy ( dump + i * MAC_ITEM_SIZE + 8 , resp . data . asBytes + resp_index , 8 ) ;
2019-03-10 06:35:06 +08:00
// copy NR_MAC (eight bytes from the response) ( 8b csn + 8 epurse == 16)
2019-04-18 05:44:48 +08:00
memcpy ( dump + i * MAC_ITEM_SIZE + 16 , resp . data . asBytes + resp_index + 8 , 8 ) ;
2019-03-10 06:35:06 +08:00
resp_index + + ;
}
2019-04-29 02:42:57 +08:00
saveFile ( " iclass_mac_attack_keyroll_B " , " .bin " , dump , datalen ) ;
2019-03-10 06:35:06 +08:00
free ( dump ) ;
break ;
}
case 1 :
case 3 :
default : {
2019-06-08 00:41:39 +08:00
uint8_t numberOfCSNs = 0 ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandOLD ( CMD_HF_ICLASS_SIMULATE , simType , numberOfCSNs , 0 , CSN , 8 ) ;
2019-03-10 06:35:06 +08:00
break ;
}
}
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2012-06-28 21:38:40 +08:00
}
2019-09-08 23:37:14 +08:00
static int CmdHFiClassInfo ( const char * Cmd ) {
2019-09-13 22:31:17 +08:00
return PM3_SUCCESS ;
2019-09-08 23:37:14 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassReader ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
char cmdp = tolower ( param_getchar ( Cmd , 0 ) ) ;
if ( cmdp = = ' h ' ) return usage_hf_iclass_reader ( ) ;
bool findone = ( cmdp = = ' 1 ' ) ? false : true ;
2019-04-13 00:41:14 +08:00
return readIclass ( findone , true ) ;
2014-04-15 18:47:01 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassReader_Replay ( const char * Cmd ) {
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
char cmdp = tolower ( param_getchar ( Cmd , 0 ) ) ;
2019-03-10 07:00:59 +08:00
if ( strlen ( Cmd ) < 1 | | cmdp = = ' h ' ) return usage_hf_iclass_replay ( ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
uint8_t readerType = 0 ;
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
if ( param_gethex ( Cmd , 0 , MAC , 8 ) ) {
PrintAndLogEx ( FAILED , " MAC must include 8 HEX symbols " ) ;
2020-04-03 13:22:54 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
2014-04-15 18:47:01 +08:00
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandMIX ( CMD_HF_ICLASS_REPLAY , readerType , 0 , 0 , MAC , 4 ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2012-06-28 21:38:40 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassELoad ( const char * Cmd ) {
2015-02-15 04:15:53 +08:00
2019-08-29 13:47:17 +08:00
DumpFileType_t dftype = BIN ;
char filename [ FILE_PATH_SIZE ] = { 0 } ;
bool errors = false ;
uint8_t cmdp = 0 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
case ' h ' :
return usage_hf_iclass_eload ( ) ;
case ' f ' :
if ( param_getstr ( Cmd , cmdp + 1 , filename , FILE_PATH_SIZE ) > = FILE_PATH_SIZE ) {
2019-08-30 16:45:52 +08:00
PrintAndLogEx ( FAILED , " Filename too long " ) ;
2019-08-29 13:47:17 +08:00
errors = true ;
break ;
}
cmdp + = 2 ;
break ;
case ' j ' :
dftype = JSON ;
2019-08-30 16:45:52 +08:00
cmdp + + ;
break ;
2019-08-29 13:47:17 +08:00
case ' e ' :
2019-08-30 16:45:52 +08:00
dftype = EML ;
cmdp + + ;
break ;
2019-08-29 13:47:17 +08:00
default :
PrintAndLogEx ( WARNING , " Unknown parameter '%c' " , param_getchar ( Cmd , cmdp ) ) ;
errors = true ;
break ;
}
2019-03-10 06:35:06 +08:00
}
2019-08-29 13:47:17 +08:00
//Validations
if ( errors | | cmdp = = 0 ) {
usage_hf_iclass_eload ( ) ;
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
2019-08-29 13:47:17 +08:00
uint8_t * dump = calloc ( 2048 , sizeof ( uint8_t ) ) ;
2019-03-10 06:35:06 +08:00
if ( ! dump ) {
2019-08-08 22:57:33 +08:00
PrintAndLogEx ( ERR , " error, cannot allocate memory " ) ;
2019-08-29 13:47:17 +08:00
return PM3_EMALLOC ;
2019-03-10 06:35:06 +08:00
}
2019-08-29 13:47:17 +08:00
size_t bytes_read = 2048 ;
int res = 0 ;
2019-03-10 06:35:06 +08:00
2019-08-30 16:45:52 +08:00
switch ( dftype ) {
2019-08-29 13:47:17 +08:00
case BIN : {
2019-08-31 21:25:36 +08:00
res = loadFile_safe ( filename , " .bin " , ( void * * ) & dump , & bytes_read ) ;
2019-08-29 13:47:17 +08:00
break ;
}
case EML : {
res = loadFileEML ( filename , dump , & bytes_read ) ;
break ;
}
case JSON : {
res = loadFileJSON ( filename , dump , 2048 , & bytes_read ) ;
break ;
}
default :
2019-08-30 16:45:52 +08:00
PrintAndLogEx ( ERR , " No dictionary loaded " ) ;
2019-10-06 07:18:04 +08:00
free ( dump ) ;
2019-08-30 16:45:52 +08:00
return PM3_ESOFT ;
2019-08-29 13:47:17 +08:00
}
2019-03-10 06:35:06 +08:00
2019-08-30 16:45:52 +08:00
if ( res ! = PM3_SUCCESS ) {
2019-03-10 06:35:06 +08:00
free ( dump ) ;
2019-08-29 13:47:17 +08:00
return PM3_EFILE ;
}
uint8_t * newdump = realloc ( dump , bytes_read ) ;
if ( newdump = = NULL ) {
free ( dump ) ;
return PM3_EMALLOC ;
} else {
dump = newdump ;
2019-03-10 06:35:06 +08:00
}
2019-04-30 18:14:46 +08:00
2019-08-29 13:47:17 +08:00
printIclassDumpInfo ( dump ) ;
2019-04-30 18:14:46 +08:00
// fast push mode
2019-04-30 20:19:26 +08:00
conn . block_after_ACK = true ;
2019-05-01 07:38:52 +08:00
2019-03-10 06:35:06 +08:00
//Send to device
uint32_t bytes_sent = 0 ;
uint32_t bytes_remaining = bytes_read ;
2019-03-10 07:00:59 +08:00
while ( bytes_remaining > 0 ) {
2019-05-01 03:10:11 +08:00
uint32_t bytes_in_packet = MIN ( PM3_CMD_DATA_SIZE , bytes_remaining ) ;
2019-05-09 01:16:37 +08:00
if ( bytes_in_packet = = bytes_remaining ) {
// Disable fast mode on last packet
conn . block_after_ACK = false ;
}
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandOLD ( CMD_HF_ICLASS_EML_MEMSET , bytes_sent , bytes_in_packet , 0 , dump + bytes_sent , bytes_in_packet ) ;
2019-03-10 06:35:06 +08:00
bytes_remaining - = bytes_in_packet ;
bytes_sent + = bytes_in_packet ;
}
free ( dump ) ;
2019-04-30 18:14:46 +08:00
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( SUCCESS , " sent %d bytes of data to device emulator memory " , bytes_sent ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-02-15 04:15:53 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassDecrypt ( const char * Cmd ) {
2019-08-30 16:45:52 +08:00
2019-08-29 20:17:39 +08:00
bool errors = false ;
bool have_key = false ;
2019-09-06 04:39:30 +08:00
bool have_data = false ;
bool have_file = false ;
2019-08-29 20:17:39 +08:00
uint8_t cmdp = 0 ;
2019-08-30 16:45:52 +08:00
2019-09-06 04:39:30 +08:00
uint8_t enc_data [ 8 ] = { 0 } ;
2020-02-27 23:35:17 +08:00
uint8_t dec_data [ 8 ] = { 0 } ;
2019-09-13 22:31:17 +08:00
2019-08-29 20:17:39 +08:00
size_t keylen = 0 ;
uint8_t key [ 32 ] = { 0 } ;
uint8_t * keyptr = NULL ;
2019-08-30 16:45:52 +08:00
2019-08-29 20:17:39 +08:00
size_t decryptedlen = 0 ;
uint8_t * decrypted = NULL ;
2019-03-10 06:35:06 +08:00
char filename [ FILE_PATH_SIZE ] ;
2019-08-29 20:17:39 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
case ' h ' :
return usage_hf_iclass_decrypt ( ) ;
2019-09-06 04:39:30 +08:00
case ' d ' :
if ( param_gethex ( Cmd , cmdp + 1 , enc_data , 16 ) ) {
2020-03-12 00:08:03 +08:00
PrintAndLogEx ( ERR , " Data must be 16 HEX symbols " ) ;
2019-09-06 04:39:30 +08:00
errors = true ;
break ;
2019-09-13 22:31:17 +08:00
}
2019-09-06 04:39:30 +08:00
have_data = true ;
cmdp + = 2 ;
break ;
2019-08-29 20:17:39 +08:00
case ' f ' :
2019-08-30 16:45:52 +08:00
if ( param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) = = 0 ) {
2020-03-12 00:08:03 +08:00
PrintAndLogEx ( WARNING , " No filename found after f " ) ;
2019-08-29 20:17:39 +08:00
errors = true ;
break ;
}
2019-03-10 06:35:06 +08:00
2019-08-30 16:45:52 +08:00
if ( loadFile_safe ( filename , " " , ( void * * ) & decrypted , & decryptedlen ) ! = PM3_SUCCESS ) {
2019-08-29 20:17:39 +08:00
errors = true ;
break ;
}
2019-09-06 04:39:30 +08:00
have_file = true ;
2019-08-29 20:17:39 +08:00
cmdp + = 2 ;
break ;
case ' k ' :
if ( param_gethex ( Cmd , cmdp + 1 , key , 32 ) ) {
PrintAndLogEx ( ERR , " Transport key must include 32 HEX symbols " ) ;
errors = true ;
}
have_key = true ;
cmdp + = 2 ;
break ;
default :
PrintAndLogEx ( WARNING , " Unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
errors = true ;
break ;
}
2019-03-10 06:35:06 +08:00
}
2019-08-29 20:17:39 +08:00
if ( errors | | cmdp < 1 ) return usage_hf_iclass_decrypt ( ) ;
2019-03-10 06:35:06 +08:00
2020-02-27 23:35:17 +08:00
bool use_sc = IsCryptoHelperPresent ( ) ;
if ( have_key = = false & & use_sc = = false ) {
2019-08-30 16:45:52 +08:00
int res = loadFile_safe ( ICLASS_DECRYPTION_BIN , " " , ( void * * ) & keyptr , & keylen ) ;
2020-02-27 23:35:17 +08:00
if ( res ! = PM3_SUCCESS ) {
PrintAndLogEx ( INFO , " Couldn't find any decryption methods " ) ;
2019-08-29 20:17:39 +08:00
return PM3_EINVARG ;
2020-02-27 23:35:17 +08:00
}
2019-08-29 20:17:39 +08:00
memcpy ( key , keyptr , sizeof ( key ) ) ;
2019-10-09 17:46:11 +08:00
free ( keyptr ) ;
2019-03-10 06:35:06 +08:00
}
// tripledes
mbedtls_des3_context ctx ;
2019-03-10 07:00:59 +08:00
mbedtls_des3_set2key_dec ( & ctx , key ) ;
2019-03-10 06:35:06 +08:00
2019-09-13 22:31:17 +08:00
if ( have_data ) {
2020-02-27 23:35:17 +08:00
if ( use_sc ) {
Decrypt ( enc_data , dec_data ) ;
} else {
mbedtls_des3_crypt_ecb ( & ctx , enc_data , dec_data ) ;
}
2019-09-06 04:39:30 +08:00
PrintAndLogEx ( SUCCESS , " Data: %s " , sprint_hex ( dec_data , sizeof ( dec_data ) ) ) ;
2019-09-06 15:00:01 +08:00
}
2019-09-06 04:39:30 +08:00
2019-09-13 22:31:17 +08:00
if ( have_file ) {
2019-09-06 04:39:30 +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 ) ;
2019-03-10 06:35:06 +08:00
2019-09-06 04:39:30 +08:00
uint8_t empty [ 8 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } ;
2020-03-11 00:10:05 +08:00
2020-02-27 23:35:17 +08:00
BLOCK79ENCRYPTION aa1_encryption = ( decrypted [ ( 6 * 8 ) + 7 ] & 0x03 ) ;
2019-03-10 06:35:06 +08:00
2020-03-12 00:08:03 +08:00
uint32_t limit = MIN ( applimit , decryptedlen / 8 ) ;
if ( decryptedlen / 8 ! = applimit ) {
2020-03-25 23:49:19 +08:00
PrintAndLogEx ( WARNING , " Actual file len " _YELLOW_ ( " %zu " ) " vs HID app-limit len " _YELLOW_ ( " %u " ) , decryptedlen , applimit * 8 ) ;
2020-03-12 00:08:03 +08:00
PrintAndLogEx ( INFO , " Setting limit to " _GREEN_ ( " %u " ) , limit * 8 ) ;
}
2020-03-12 04:00:19 +08:00
uint8_t numblocks4userid = GetNumberBlocksForUserId ( decrypted + ( 6 * 8 ) ) ;
2020-03-12 00:08:03 +08:00
for ( uint16_t blocknum = 0 ; blocknum < limit ; + + blocknum ) {
2019-09-06 04:39:30 +08:00
uint8_t idx = blocknum * 8 ;
memcpy ( enc_data , decrypted + idx , 8 ) ;
2020-03-12 04:00:19 +08:00
if ( aa1_encryption = = RFU | | aa1_encryption = = None )
continue ;
// Decrypted block 7,8,9 if configured.
if ( blocknum > 6 & & blocknum < = 6 + numblocks4userid & & memcmp ( enc_data , empty , 8 ) ! = 0 ) {
2020-02-27 23:35:17 +08:00
if ( use_sc ) {
Decrypt ( enc_data , decrypted + idx ) ;
} else {
mbedtls_des3_crypt_ecb ( & ctx , enc_data , decrypted + idx ) ;
}
2019-09-06 04:39:30 +08:00
}
2019-03-10 06:35:06 +08:00
}
2019-09-06 04:39:30 +08:00
//Use the first block (CSN) for filename
2020-03-12 00:08:03 +08:00
char * fptr = calloc ( 50 , sizeof ( uint8_t ) ) ;
if ( ! fptr ) {
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
free ( decrypted ) ;
return PM3_EMALLOC ;
}
2019-09-06 04:39:30 +08:00
strcat ( fptr , " hf-iclass- " ) ;
2020-03-22 03:58:20 +08:00
FillFileNameByUID ( fptr , hdr - > csn , " -dump-decrypted " , sizeof ( hdr - > csn ) ) ;
2019-08-30 16:45:52 +08:00
2019-09-06 04:39:30 +08:00
saveFile ( fptr , " .bin " , decrypted , decryptedlen ) ;
saveFileEML ( fptr , decrypted , decryptedlen , 8 ) ;
saveFileJSON ( fptr , jsfIclass , decrypted , decryptedlen ) ;
2019-08-29 20:17:39 +08:00
2020-03-12 00:08:03 +08:00
PrintAndLogEx ( INFO , " Following output skips CSN / block0 " ) ;
2019-09-06 04:39:30 +08:00
printIclassDumpContents ( decrypted , 1 , ( decryptedlen / 8 ) , decryptedlen ) ;
2020-03-11 00:10:05 +08:00
2020-02-27 23:35:17 +08:00
// decode block 6
2020-03-11 00:10:05 +08:00
if ( memcmp ( decrypted + ( 8 * 6 ) , empty , 8 ) ! = 0 ) {
2020-02-27 23:35:17 +08:00
if ( use_sc ) {
2020-03-11 00:10:05 +08:00
DecodeBlock6 ( decrypted + ( 8 * 6 ) ) ;
2020-02-27 23:35:17 +08:00
}
}
2020-03-11 00:10:05 +08:00
// decode block 7-8-9
if ( memcmp ( decrypted + ( 8 * 7 ) , empty , 8 ) ! = 0 ) {
2020-02-27 23:35:17 +08:00
//todo: remove preamble/sentinal
uint32_t top = 0 , mid , bot ;
2020-03-11 00:10:05 +08:00
mid = bytes_to_num ( decrypted + ( 8 * 7 ) , 4 ) ;
bot = bytes_to_num ( decrypted + ( 8 * 7 ) + 4 , 4 ) ;
PrintAndLogEx ( INFO , " Block 7 binary " ) ;
2020-02-27 23:35:17 +08:00
2020-03-11 00:10:05 +08:00
char hexstr [ 8 + 1 ] = { 0 } ;
hex_to_buffer ( ( uint8_t * ) hexstr , decrypted + ( 8 * 7 ) , 8 , sizeof ( hexstr ) - 1 , 0 , 0 , true ) ;
2020-02-27 23:35:17 +08:00
2020-03-11 00:10:05 +08:00
char binstr [ 8 * 8 + 1 ] = { 0 } ;
2020-02-27 23:35:17 +08:00
hextobinstring ( binstr , hexstr ) ;
2020-03-11 00:10:05 +08:00
uint8_t i = 0 ;
while ( i < strlen ( binstr ) & & binstr [ i + + ] = = ' 0 ' ) ;
2020-02-27 23:35:17 +08:00
PrintAndLogEx ( SUCCESS , " %s " , binstr + i ) ;
2020-03-11 00:10:05 +08:00
2020-02-27 23:35:17 +08:00
PrintAndLogEx ( INFO , " Wiegand decode " ) ;
wiegand_message_t packed = initialize_message_object ( top , mid , bot ) ;
HIDTryUnpack ( & packed , true ) ;
PrintAndLogEx ( INFO , " ----------------------------------------------------------------- " ) ;
} else {
PrintAndLogEx ( INFO , " No credential found. " ) ;
}
2020-03-11 00:10:05 +08:00
2019-09-06 04:39:30 +08:00
free ( decrypted ) ;
free ( fptr ) ;
}
2019-09-06 15:00:01 +08:00
mbedtls_des3_free ( & ctx ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-08-29 13:47:17 +08:00
static void iClassEncryptBlkData ( uint8_t * blk_data , uint8_t * key ) {
uint8_t encrypted_data [ 16 ] ;
uint8_t * encrypted = encrypted_data ;
2019-03-10 06:35:06 +08:00
mbedtls_des3_context ctx ;
2019-03-10 07:00:59 +08:00
mbedtls_des3_set2key_enc ( & ctx , key ) ;
2019-08-29 13:47:17 +08:00
mbedtls_des3_crypt_ecb ( & ctx , blk_data , encrypted ) ;
memcpy ( blk_data , encrypted , 8 ) ;
2019-09-06 15:00:01 +08:00
mbedtls_des3_free ( & ctx ) ;
2015-10-08 05:00:46 +08:00
}
2015-02-15 04:17:08 +08:00
2019-04-13 00:41:14 +08:00
static int CmdHFiClassEncryptBlk ( const char * Cmd ) {
2019-08-29 13:47:17 +08:00
bool errors = false ;
bool have_key = false ;
uint8_t blk_data [ 8 ] = { 0 } ;
uint8_t key [ 16 ] = { 0 } ;
2019-08-29 20:17:39 +08:00
uint8_t * keyptr = NULL ;
2019-08-29 13:47:17 +08:00
uint8_t cmdp = 0 ;
2015-10-08 05:00:46 +08:00
2019-08-29 13:47:17 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
case ' h ' :
return usage_hf_iclass_encrypt ( ) ;
case ' d ' :
2019-08-29 20:17:39 +08:00
if ( param_gethex ( Cmd , cmdp + 1 , blk_data , 16 ) ) {
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( ERR , " Block data must include 16 HEX symbols " ) ;
2019-08-29 20:17:39 +08:00
errors = true ;
2019-08-29 13:47:17 +08:00
}
cmdp + = 2 ;
break ;
case ' k ' :
2019-08-29 20:17:39 +08:00
if ( param_gethex ( Cmd , cmdp + 1 , key , 32 ) ) {
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( ERR , " Transport key must include 32 HEX symbols " ) ;
2019-08-29 20:17:39 +08:00
errors = true ;
2019-08-29 13:47:17 +08:00
}
have_key = true ;
cmdp + = 2 ;
break ;
default :
PrintAndLogEx ( WARNING , " Unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
errors = true ;
break ;
}
2019-03-10 06:35:06 +08:00
}
2015-02-15 04:17:08 +08:00
2019-08-29 13:47:17 +08:00
if ( errors | | cmdp < 1 ) return usage_hf_iclass_encrypt ( ) ;
2020-02-27 23:35:17 +08:00
bool use_sc = IsCryptoHelperPresent ( ) ;
if ( have_key = = false & & use_sc = = false ) {
2019-08-29 20:17:39 +08:00
size_t keylen = 0 ;
2019-08-30 16:45:52 +08:00
int res = loadFile_safe ( ICLASS_DECRYPTION_BIN , " " , ( void * * ) & keyptr , & keylen ) ;
2019-08-29 20:17:39 +08:00
if ( res ! = PM3_SUCCESS )
return PM3_EINVARG ;
2019-08-30 16:45:52 +08:00
2019-08-29 20:17:39 +08:00
memcpy ( key , keyptr , sizeof ( key ) ) ;
2019-10-06 07:18:04 +08:00
free ( keyptr ) ;
2019-08-29 13:47:17 +08:00
}
2020-02-27 23:35:17 +08:00
if ( use_sc ) {
Encrypt ( blk_data , blk_data ) ;
} else {
iClassEncryptBlkData ( blk_data , key ) ;
}
2019-08-29 13:47:17 +08:00
printvar ( " encrypted block " , blk_data , 8 ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-04-14 03:54:04 +08:00
static void Calc_wb_mac ( uint8_t blockno , uint8_t * data , uint8_t * div_key , uint8_t MAC [ 4 ] ) {
2019-03-10 06:35:06 +08:00
uint8_t wb [ 9 ] ;
wb [ 0 ] = blockno ;
2019-03-10 07:00:59 +08:00
memcpy ( wb + 1 , data , 8 ) ;
2019-03-10 06:35:06 +08:00
doMAC_N ( wb , sizeof ( wb ) , div_key , MAC ) ;
2015-10-08 05:00:46 +08:00
}
2019-03-10 18:20:22 +08:00
static bool select_only ( uint8_t * CSN , uint8_t * CCNR , bool use_credit_key , bool verbose ) {
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2019-04-19 23:03:39 +08:00
uint8_t flags = FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_CC | FLAG_ICLASS_READER_ONE_TRY ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
if ( use_credit_key )
2019-04-19 23:03:39 +08:00
flags | = FLAG_ICLASS_READER_CEDITKEY ;
2015-10-08 05:00:46 +08:00
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandMIX ( CMD_HF_ICLASS_READER , flags , 0 , 0 , NULL , 0 ) ;
2019-09-09 03:21:30 +08:00
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
return false ;
}
2015-02-15 04:17:08 +08:00
2019-04-18 05:44:48 +08:00
uint8_t isOK = resp . oldarg [ 0 ] & 0xff ;
uint8_t * data = resp . data . asBytes ;
2015-10-08 05:00:46 +08:00
2019-03-10 06:35:06 +08:00
memcpy ( CSN , data , 8 ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
if ( CCNR ! = NULL )
2019-03-10 07:00:59 +08:00
memcpy ( CCNR , data + 16 , 8 ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
if ( isOK > 0 & & verbose ) {
PrintAndLogEx ( SUCCESS , " CSN | %s " , sprint_hex ( CSN , 8 ) ) ;
PrintAndLogEx ( SUCCESS , " CCNR | %s " , sprint_hex ( CCNR , 8 ) ) ;
}
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
if ( isOK < = 1 ) {
2019-09-13 22:31:17 +08:00
if ( verbose )
2019-09-09 03:21:30 +08:00
PrintAndLogEx ( FAILED , " failed to obtain CC! Tag-select is aborting... (%d) " , isOK ) ;
2019-09-11 22:56:08 +08:00
2019-03-10 06:35:06 +08:00
return false ;
}
return true ;
2015-10-08 05:00:46 +08:00
}
2019-03-10 18:20:22 +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 ) {
2019-03-10 07:00:59 +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 } ;
2019-03-10 06:35:06 +08:00
if ( ! select_only ( CSN , CCNR , use_credit_key , verbose ) ) {
if ( verbose ) PrintAndLogEx ( FAILED , " selecting tag failed " ) ;
2019-09-11 22:56:08 +08:00
2019-09-12 04:51:13 +08:00
// DropField();
2019-03-10 06:35:06 +08:00
return false ;
}
//get div_key
if ( rawkey )
memcpy ( div_key , KEY , 8 ) ;
else
HFiClassCalcDivKey ( CSN , KEY , div_key , elite ) ;
2019-03-10 07:00:59 +08:00
if ( verbose ) PrintAndLogEx ( SUCCESS , " authing with %s: %s " , rawkey ? " raw key " : " diversified key " , sprint_hex ( div_key , 8 ) ) ;
2019-03-10 06:35:06 +08:00
doMAC ( CCNR , div_key , MAC ) ;
2019-09-09 03:21:30 +08:00
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-09-09 03:21:30 +08:00
SendCommandNG ( CMD_HF_ICLASS_AUTH , MAC , 4 ) ;
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_AUTH , & resp , 2000 ) = = 0 ) {
if ( verbose ) PrintAndLogEx ( WARNING , " Command execute timeout " ) ;
return false ;
}
2019-09-13 22:31:17 +08:00
if ( resp . status ! = PM3_SUCCESS ) {
2019-09-09 03:21:30 +08:00
if ( verbose ) PrintAndLogEx ( ERR , " failed to communicate with card " ) ;
2019-03-10 06:35:06 +08:00
return false ;
}
2019-09-09 03:21:30 +08:00
uint8_t isOK = resp . data . asBytes [ 0 ] ;
if ( isOK = = 0 ) {
2019-03-10 06:35:06 +08:00
if ( verbose ) PrintAndLogEx ( FAILED , " authentication error " ) ;
return false ;
}
2019-09-09 03:21:30 +08:00
2019-03-10 06:35:06 +08:00
return true ;
2015-10-08 05:00:46 +08:00
}
2015-02-15 04:17:08 +08:00
2019-04-13 00:41:14 +08:00
static int CmdHFiClassReader_Dump ( const char * Cmd ) {
2015-10-08 05:00:46 +08:00
2019-03-10 07:00:59 +08:00
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 } ;
2019-03-10 06:35:06 +08:00
uint8_t blockno = 0 ;
uint8_t numblks = 0 ;
uint8_t maxBlk = 31 ;
uint8_t app_areas = 1 ;
uint8_t kb = 2 ;
2019-03-10 07:00:59 +08:00
uint8_t KEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t CreditKEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2019-03-10 06:35:06 +08:00
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
uint8_t fileNameLen = 0 ;
char filename [ FILE_PATH_SIZE ] = { 0 } ;
char tempStr [ 50 ] = { 0 } ;
bool have_debit_key = false ;
bool have_credit_key = false ;
bool use_credit_key = false ;
bool elite = false ;
bool rawkey = false ;
bool errors = false ;
bool verbose = false ;
uint8_t cmdp = 0 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2019-03-10 07:00:59 +08:00
case ' h ' :
return usage_hf_iclass_dump ( ) ;
case ' c ' :
have_credit_key = true ;
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , CreditKEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
if ( keyNbr < ICLASS_KEYS_MAX ) {
memcpy ( CreditKEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
errors = true ;
}
2019-03-10 06:35:06 +08:00
} else {
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
}
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' e ' :
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " elite algo " ) ) ;
2019-03-10 07:00:59 +08:00
elite = true ;
cmdp + + ;
break ;
case ' f ' :
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
if ( fileNameLen < 1 ) {
PrintAndLogEx ( WARNING , " no filename found after f " ) ;
errors = true ;
}
cmdp + = 2 ;
break ;
case ' k ' :
have_debit_key = true ;
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
if ( keyNbr < ICLASS_KEYS_MAX ) {
memcpy ( KEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
errors = true ;
}
2019-03-10 06:35:06 +08:00
} else {
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
}
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' r ' :
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " raw mode " ) ) ;
2019-03-10 07:00:59 +08:00
rawkey = true ;
cmdp + + ;
break ;
case ' v ' :
verbose = true ;
cmdp + + ;
break ;
default :
PrintAndLogEx ( WARNING , " Unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
2019-03-10 07:00:59 +08:00
break ;
2019-03-10 06:35:06 +08:00
}
}
if ( errors | | cmdp < 2 ) return usage_hf_iclass_dump ( ) ;
// 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 ;
uint32_t flags = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC |
2019-03-10 07:00:59 +08:00
FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_ONLY_ONCE |
FLAG_ICLASS_READER_ONE_TRY ;
2019-03-10 06:35:06 +08:00
//get config and first 3 blocks
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2019-03-10 07:00:59 +08:00
uint8_t tag_data [ 255 * 8 ] ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandMIX ( CMD_HF_ICLASS_READER , flags , 0 , 0 , NULL , 0 ) ;
2019-03-10 06:35:06 +08:00
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
DropField ( ) ;
2019-09-09 05:19:06 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
DropField ( ) ;
2019-04-18 05:44:48 +08:00
uint8_t readStatus = resp . oldarg [ 0 ] & 0xff ;
uint8_t * data = resp . data . asBytes ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
if ( readStatus = = 0 ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( FAILED , " no tag found " ) ;
2019-09-09 05:19:06 +08:00
DropField ( ) ;
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
if ( readStatus & ( FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_CC ) ) {
memcpy ( tag_data , data , 8 * 3 ) ;
2019-03-10 06:35:06 +08:00
blockno + = 2 ; // 2 to force re-read of block 2 later. (seems to respond differently..)
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 ;
}
// authenticate debit key and get div_key - later store in dump block 3
2019-09-09 05:19:06 +08:00
int numberAuthRetries = ICLASS_AUTH_RETRY ;
do {
if ( select_and_auth ( KEY , MAC , div_key , use_credit_key , elite , rawkey , verbose ) )
break ;
} while ( numberAuthRetries - - ) ;
2019-09-13 22:31:17 +08:00
if ( numberAuthRetries < = 0 ) {
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( WARNING , " failed authenticating with debit key " ) ;
DropField ( ) ;
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
// begin dump
clearCommandBuffer ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandMIX ( CMD_HF_ICLASS_DUMP , blockno , numblks - blockno + 1 , 0 , NULL , 0 ) ;
2019-03-10 06:35:06 +08:00
while ( true ) {
2019-03-10 07:00:59 +08:00
printf ( " . " ) ;
fflush ( stdout ) ;
2019-07-11 19:01:34 +08:00
if ( kbd_enter_pressed ( ) ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " \n [!] aborted via keyboard! \n " ) ;
DropField ( ) ;
2020-03-12 01:38:07 +08:00
return PM3_EOPABORTED ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
if ( WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) )
2019-03-10 06:35:06 +08:00
break ;
}
// dump cmd switch off at device when finised.
2020-04-03 13:22:54 +08:00
2019-04-18 05:44:48 +08:00
uint32_t blocksRead = resp . oldarg [ 1 ] ;
uint8_t isOK = resp . oldarg [ 0 ] & 0xff ;
2019-03-10 06:35:06 +08:00
if ( ! isOK & & ! blocksRead ) {
PrintAndLogEx ( WARNING , " read block failed " ) ;
2019-09-09 05:23:06 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-04-18 05:44:48 +08:00
uint32_t startindex = resp . oldarg [ 2 ] ;
2019-03-10 07:00:59 +08:00
if ( blocksRead * 8 > sizeof ( tag_data ) - ( blockno * 8 ) ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( FAILED , " data exceeded buffer size! " ) ;
2019-03-10 07:00:59 +08:00
blocksRead = ( sizeof ( tag_data ) / 8 ) - blockno ;
2019-03-10 06:35:06 +08:00
}
// response ok - now get bigbuf content of the dump
2019-07-24 03:33:52 +08:00
if ( ! GetFromDevice ( BIG_BUF , tag_data + ( blockno * 8 ) , blocksRead * 8 , startindex , NULL , 0 , NULL , 2500 , false ) ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " command execution time out " ) ;
2019-09-09 05:19:06 +08:00
return PM3_ETIMEOUT ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
size_t gotBytes = blocksRead * 8 + blockno * 8 ;
2019-03-10 06:35:06 +08:00
// try AA2
if ( have_credit_key ) {
//turn off hf field before authenticating with different key
DropField ( ) ;
2019-09-09 05:19:06 +08:00
2019-03-10 07:00:59 +08:00
memset ( MAC , 0 , 4 ) ;
2019-09-09 05:19:06 +08:00
2019-03-10 06:35:06 +08:00
// AA2 authenticate credit key and git c_div_key - later store in dump block 4
2019-09-09 05:19:06 +08:00
numberAuthRetries = ICLASS_AUTH_RETRY ;
do {
if ( select_and_auth ( CreditKEY , MAC , c_div_key , true , elite , rawkey , verbose ) )
break ;
} while ( numberAuthRetries - - ) ;
2019-09-13 22:31:17 +08:00
if ( numberAuthRetries < = 0 ) {
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( WARNING , " failed authenticating with credit key " ) ;
DropField ( ) ;
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-09-09 05:19:06 +08:00
2019-03-10 06:35:06 +08:00
// do we still need to read more block? (aa2 enabled?)
2019-03-10 07:00:59 +08:00
if ( maxBlk > blockno + numblks + 1 ) {
2019-03-10 06:35:06 +08:00
// setup dump and start
clearCommandBuffer ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandMIX ( CMD_HF_ICLASS_DUMP , blockno + blocksRead , maxBlk - ( blockno + blocksRead ) , 0 , NULL , 0 ) ;
2019-03-10 06:35:06 +08:00
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
PrintAndLogEx ( WARNING , " command execute timeout 2 " ) ;
2019-09-09 05:19:06 +08:00
return PM3_ETIMEOUT ;
2019-03-10 06:35:06 +08:00
}
2019-04-18 05:44:48 +08:00
isOK = resp . oldarg [ 0 ] & 0xff ;
blocksRead = resp . oldarg [ 1 ] ;
2019-03-10 06:35:06 +08:00
if ( ! isOK & & ! blocksRead ) {
PrintAndLogEx ( WARNING , " read block failed 2 " ) ;
2019-09-09 05:19:06 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-04-18 05:44:48 +08:00
startindex = resp . oldarg [ 2 ] ;
2019-03-10 06:35:06 +08:00
if ( blocksRead * 8 > sizeof ( tag_data ) - gotBytes ) {
PrintAndLogEx ( FAILED , " data exceeded buffer size! " ) ;
2019-03-10 07:00:59 +08:00
blocksRead = ( sizeof ( tag_data ) - gotBytes ) / 8 ;
2019-03-10 06:35:06 +08:00
}
// get dumped data from bigbuf
2019-07-24 03:33:52 +08:00
if ( ! GetFromDevice ( BIG_BUF , tag_data + gotBytes , blocksRead * 8 , startindex , NULL , 0 , NULL , 2500 , false ) ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " command execution time out " ) ;
2019-09-09 05:19:06 +08:00
return PM3_ETIMEOUT ;
2019-03-10 06:35:06 +08:00
}
gotBytes + = blocksRead * 8 ;
}
}
DropField ( ) ;
// add diversified keys to dump
2019-09-09 05:19:06 +08:00
if ( have_debit_key )
memcpy ( tag_data + ( 3 * 8 ) , div_key , 8 ) ;
2019-09-13 22:31:17 +08:00
2019-09-09 05:19:06 +08:00
if ( have_credit_key )
memcpy ( tag_data + ( 4 * 8 ) , c_div_key , 8 ) ;
2019-03-10 06:35:06 +08:00
// print the dump
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-04-03 14:36:55 +08:00
PrintAndLogEx ( INFO , " ------+--+-------------------------+---------- " ) ;
PrintAndLogEx ( INFO , " CSN |00| " _GREEN_ ( " %s " ) " | " , sprint_hex ( tag_data , 8 ) ) ;
2019-03-10 07:00:59 +08:00
printIclassDumpContents ( tag_data , 1 , ( gotBytes / 8 ) , gotBytes ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
if ( filename [ 0 ] = = 0 ) {
2019-08-29 20:33:09 +08:00
//Use the first block (CSN) for filename
strcat ( filename , " hf-iclass- " ) ;
2020-03-22 03:58:20 +08:00
FillFileNameByUID ( filename , tag_data , " -dump " , 8 ) ;
2019-03-10 06:35:06 +08:00
}
// save the dump to .bin file
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( SUCCESS , " saving dump file - %zu blocks read " , gotBytes / 8 ) ;
2019-04-29 02:42:57 +08:00
saveFile ( filename , " .bin " , tag_data , gotBytes ) ;
2019-08-11 00:05:24 +08:00
saveFileEML ( filename , tag_data , gotBytes , 8 ) ;
2019-08-29 20:17:39 +08:00
saveFileJSON ( filename , jsfIclass , tag_data , gotBytes ) ;
2019-08-29 20:33:09 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2015-02-15 04:17:08 +08:00
2019-03-10 18:20:22 +08:00
static int WriteBlock ( uint8_t blockno , uint8_t * bldata , uint8_t * KEY , bool use_credit_key , bool elite , bool rawkey , bool verbose ) {
2019-03-10 06:35:06 +08:00
2019-09-13 22:31:17 +08:00
int numberAuthRetries = ICLASS_AUTH_RETRY ;
2019-09-09 03:21:30 +08:00
do {
2019-09-13 22:31:17 +08:00
2019-09-09 03:21:30 +08:00
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
if ( ! select_and_auth ( KEY , MAC , div_key , use_credit_key , elite , rawkey , verbose ) ) {
numberAuthRetries - - ;
2019-09-13 22:31:17 +08:00
DropField ( ) ;
2019-09-09 03:21:30 +08:00
continue ;
}
2019-03-10 06:35:06 +08:00
2019-09-09 03:21:30 +08:00
Calc_wb_mac ( blockno , bldata , div_key , MAC ) ;
2019-03-10 06:35:06 +08:00
2019-09-09 03:21:30 +08:00
struct p {
uint8_t blockno ;
uint8_t data [ 12 ] ;
} PACKED payload ;
payload . blockno = blockno ;
2019-09-13 22:31:17 +08:00
2019-09-09 03:21:30 +08:00
memcpy ( payload . data , bldata , 8 ) ;
memcpy ( payload . data + 8 , MAC , 4 ) ;
clearCommandBuffer ( ) ;
2019-09-13 22:31:17 +08:00
SendCommandNG ( CMD_HF_ICLASS_WRITEBL , ( uint8_t * ) & payload , sizeof ( payload ) ) ;
2019-09-09 03:21:30 +08:00
PacketResponseNG resp ;
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_WRITEBL , & resp , 4000 ) = = 0 ) {
if ( verbose ) PrintAndLogEx ( WARNING , " Command execute timeout " ) ;
DropField ( ) ;
return PM3_ETIMEOUT ;
}
2019-09-13 22:31:17 +08:00
if ( resp . status ! = PM3_SUCCESS ) {
2019-09-09 03:21:30 +08:00
if ( verbose ) PrintAndLogEx ( ERR , " failed to communicate with card " ) ;
DropField ( ) ;
return PM3_EWRONGANSVER ;
}
if ( resp . data . asBytes [ 0 ] = = 1 )
break ;
2019-09-13 22:31:17 +08:00
} while ( numberAuthRetries ) ;
2019-09-09 03:21:30 +08:00
DropField ( ) ;
2019-09-13 22:31:17 +08:00
if ( numberAuthRetries > 0 ) {
2019-09-09 03:21:30 +08:00
PrintAndLogEx ( SUCCESS , " Write block %02X successful \n " , blockno ) ;
} else {
2019-09-13 22:31:17 +08:00
PrintAndLogEx ( ERR , " failed to authenticate and write block " ) ;
2019-09-09 03:21:30 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-09-13 22:31:17 +08:00
2019-09-09 03:21:30 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClass_WriteBlock ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
uint8_t blockno = 0 ;
2019-03-10 07:00:59 +08:00
uint8_t bldata [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
uint8_t KEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2019-03-10 06:35:06 +08:00
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
char tempStr [ 50 ] = { 0 } ;
2019-09-09 03:21:30 +08:00
bool got_blockno = false ;
2019-03-10 06:35:06 +08:00
bool use_credit_key = false ;
bool elite = false ;
bool rawkey = false ;
bool errors = false ;
bool verbose = false ;
uint8_t cmdp = 0 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2019-03-10 07:00:59 +08:00
case ' h ' :
return usage_hf_iclass_writeblock ( ) ;
case ' b ' :
2019-09-09 03:21:30 +08:00
blockno = param_get8ex ( Cmd , cmdp + 1 , 07 , 16 ) ;
got_blockno = true ;
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' c ' :
2019-09-09 03:21:30 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " CREDIT " ) ) ;
2019-03-10 07:00:59 +08:00
use_credit_key = true ;
cmdp + + ;
break ;
case ' d ' :
if ( param_gethex ( Cmd , cmdp + 1 , bldata , 16 ) ) {
PrintAndLogEx ( WARNING , " Data must include 16 HEX symbols \n " ) ;
errors = true ;
}
cmdp + = 2 ;
break ;
case ' e ' :
2019-09-09 03:21:30 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " elite algo " ) ) ;
2019-03-10 07:00:59 +08:00
elite = true ;
cmdp + + ;
break ;
case ' k ' :
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
if ( keyNbr < ICLASS_KEYS_MAX ) {
2019-09-13 22:31:17 +08:00
PrintAndLogEx ( SUCCESS , " Using key[%d] %s " , keyNbr , sprint_hex ( iClass_Key_Table [ keyNbr ] , 8 ) ) ;
2019-03-10 07:00:59 +08:00
memcpy ( KEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
errors = true ;
}
2019-03-10 06:35:06 +08:00
} else {
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
}
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' r ' :
2019-09-09 03:21:30 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " raw mode " ) ) ;
2019-03-10 07:00:59 +08:00
rawkey = true ;
cmdp + + ;
break ;
case ' v ' :
verbose = true ;
cmdp + + ;
break ;
default :
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
2019-03-10 07:00:59 +08:00
break ;
2019-03-10 06:35:06 +08:00
}
}
2019-09-13 22:31:17 +08:00
if ( got_blockno = = false )
2019-09-09 03:21:30 +08:00
errors = true ;
2019-09-13 22:31:17 +08:00
2019-03-10 06:35:06 +08:00
if ( errors | | cmdp < 6 ) return usage_hf_iclass_writeblock ( ) ;
2019-09-09 03:21:30 +08:00
return WriteBlock ( blockno , bldata , KEY , use_credit_key , elite , rawkey , verbose ) ;
2015-10-08 05:00:46 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassCloneTag ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
char filename [ FILE_PATH_SIZE ] = { 0x00 } ;
char tempStr [ 50 ] = { 0 } ;
2019-03-10 07:00:59 +08:00
uint8_t KEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2019-03-10 06:35:06 +08:00
uint8_t keyNbr = 0 ;
uint8_t fileNameLen = 0 ;
uint8_t startblock = 0 ;
uint8_t endblock = 0 ;
uint8_t dataLen = 0 ;
2019-09-09 05:19:06 +08:00
bool got_startblk = false , got_endblk = false ;
2019-03-10 06:35:06 +08:00
bool use_credit_key = false ;
bool elite = false ;
bool rawkey = false ;
bool errors = false ;
bool verbose = false ;
uint8_t cmdp = 0 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2019-03-10 07:00:59 +08:00
case ' h ' :
return usage_hf_iclass_clone ( ) ;
case ' b ' :
2019-09-09 05:19:06 +08:00
startblock = param_get8ex ( Cmd , cmdp + 1 , 07 , 16 ) ;
got_startblk = true ;
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' c ' :
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " CREDIT " ) ) ;
2019-03-10 07:00:59 +08:00
use_credit_key = true ;
cmdp + + ;
break ;
case ' e ' :
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " elite algo " ) ) ;
2019-03-10 07:00:59 +08:00
elite = true ;
cmdp + + ;
break ;
case ' f ' :
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
if ( fileNameLen < 1 ) {
PrintAndLogEx ( WARNING , " No filename found after f " ) ;
errors = true ;
}
cmdp + = 2 ;
break ;
case ' k ' :
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
if ( keyNbr < ICLASS_KEYS_MAX ) {
2019-09-13 22:31:17 +08:00
PrintAndLogEx ( SUCCESS , " Using key[%d] %s " , keyNbr , sprint_hex ( iClass_Key_Table [ keyNbr ] , 8 ) ) ;
2019-03-10 07:00:59 +08:00
memcpy ( KEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
errors = true ;
}
2019-03-10 06:35:06 +08:00
} else {
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
}
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' l ' :
2019-09-09 05:19:06 +08:00
endblock = param_get8ex ( Cmd , cmdp + 1 , 07 , 16 ) ;
got_endblk = true ;
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' r ' :
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " raw mode " ) ) ;
2019-03-10 07:00:59 +08:00
rawkey = true ;
cmdp + + ;
break ;
case ' v ' :
verbose = true ;
cmdp + + ;
break ;
default :
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
2019-03-10 07:00:59 +08:00
break ;
2019-03-10 06:35:06 +08:00
}
}
2019-09-13 22:31:17 +08:00
if ( got_endblk = = false | | got_startblk = = false )
2019-09-09 05:19:06 +08:00
errors = true ;
2019-03-10 06:35:06 +08:00
if ( errors | | cmdp < 8 ) return usage_hf_iclass_clone ( ) ;
2019-09-09 05:19:06 +08:00
if ( startblock < 5 ) {
PrintAndLogEx ( WARNING , " you cannot write key blocks this way. yet... make your start block > 4 " ) ;
return PM3_EINVARG ;
}
2019-03-10 06:35:06 +08:00
2019-09-09 05:19:06 +08:00
int total_bytes = ( ( ( endblock - startblock ) + 1 ) * 12 ) ;
2019-09-13 22:31:17 +08:00
2019-09-09 05:19:06 +08:00
if ( total_bytes > PM3_CMD_DATA_SIZE - 2 ) {
2019-05-01 03:10:11 +08:00
PrintAndLogEx ( NORMAL , " Trying to write too many blocks at once. Max: %d " , PM3_CMD_DATA_SIZE / 8 ) ;
2019-09-09 05:19:06 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
2019-09-09 05:19:06 +08:00
2019-03-10 06:35:06 +08:00
// file handling and reading
2019-09-09 05:19:06 +08:00
FILE * f = fopen ( filename , " rb " ) ;
2019-03-10 07:00:59 +08:00
if ( ! f ) {
2019-05-15 16:24:06 +08:00
PrintAndLogEx ( FAILED , " File: " _YELLOW_ ( " %s " ) " : not found or locked. " , filename ) ;
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-09-09 05:19:06 +08:00
iclass_block_t tag_data [ PM3_CMD_DATA_SIZE / 12 ] ;
// read data from file from block 6 --- 19
// 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;
// max is 32 - 6 = 28 block. 28 x 12 bytes gives 336 bytes
2019-03-10 06:35:06 +08:00
int i ;
2019-03-10 07:00:59 +08:00
fseek ( f , startblock * 8 , SEEK_SET ) ;
size_t bytes_read = fread ( tag_data , sizeof ( iclass_block_t ) , endblock - startblock + 1 , f ) ;
2019-09-09 05:19:06 +08:00
fclose ( f ) ;
2019-09-13 22:31:17 +08:00
2019-03-10 07:00:59 +08:00
if ( bytes_read = = 0 ) {
2019-07-14 06:35:18 +08:00
PrintAndLogEx ( ERR , " file reading error. " ) ;
2019-09-09 05:19:06 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2019-09-13 22:31:17 +08:00
2019-09-09 05:19:06 +08:00
int numberAuthRetries = ICLASS_AUTH_RETRY ;
do {
if ( select_and_auth ( KEY , MAC , div_key , use_credit_key , elite , rawkey , verbose ) )
break ;
} while ( numberAuthRetries - - ) ;
2019-09-13 22:31:17 +08:00
if ( numberAuthRetries < = 0 ) {
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( ERR , " failed to authenticate " ) ;
DropField ( ) ;
return PM3_ESOFT ;
2019-03-14 17:01:07 +08:00
}
2019-03-10 06:35:06 +08:00
2019-09-09 05:19:06 +08:00
uint8_t data [ total_bytes ] ;
2019-03-10 06:35:06 +08:00
// calculate all mac for every the block we will write
2019-03-10 07:00:59 +08:00
for ( i = startblock ; i < = endblock ; i + + ) {
2019-09-13 22:31:17 +08:00
2019-03-10 07:00:59 +08:00
Calc_wb_mac ( i , tag_data [ i - startblock ] . d , div_key , MAC ) ;
2019-03-10 06:35:06 +08:00
// 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
2019-06-08 00:41:39 +08:00
uint8_t * ptr = data + ( i - startblock ) * 12 ;
2019-03-10 06:35:06 +08:00
memcpy ( ptr , & ( tag_data [ i - startblock ] . d [ 0 ] ) , 8 ) ;
2019-03-10 07:00:59 +08:00
memcpy ( ptr + 8 , MAC , 4 ) ;
2019-03-10 06:35:06 +08:00
}
2019-09-09 05:19:06 +08:00
2019-09-13 22:31:17 +08:00
if ( verbose ) {
PrintAndLogEx ( NORMAL , " ------+--------------------------+------------- " ) ;
PrintAndLogEx ( NORMAL , " block | data | mac " ) ;
PrintAndLogEx ( NORMAL , " ------+--------------------------+------------- " ) ;
2019-09-09 05:19:06 +08:00
uint8_t p [ 12 ] ;
for ( i = 0 ; i < = endblock - startblock ; i + + ) {
memcpy ( p , data + ( i * 12 ) , 12 ) ;
char * s = calloc ( 70 , sizeof ( uint8_t ) ) ;
sprintf ( s , " | %s " , sprint_hex ( p , 8 ) ) ;
sprintf ( s + strlen ( s ) , " | %s " , sprint_hex ( p + 8 , 4 ) ) ;
2019-09-13 22:31:17 +08:00
PrintAndLogEx ( NORMAL , " %02X %s " , i + startblock , s ) ;
2019-09-09 05:19:06 +08:00
free ( s ) ;
}
2019-03-10 06:35:06 +08:00
}
2019-03-14 19:30:32 +08:00
2019-09-09 05:19:06 +08:00
struct p {
uint8_t startblock ;
uint8_t endblock ;
uint8_t data [ PM3_CMD_DATA_SIZE - 2 ] ;
} PACKED payload ;
2019-09-13 22:31:17 +08:00
2019-09-09 05:19:06 +08:00
payload . startblock = startblock ;
2019-09-13 22:31:17 +08:00
payload . endblock = endblock ;
2019-09-09 05:19:06 +08:00
memcpy ( payload . data , data , total_bytes ) ;
2019-09-13 22:31:17 +08:00
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-09-13 22:31:17 +08:00
SendCommandNG ( CMD_HF_ICLASS_CLONE , ( uint8_t * ) & payload , total_bytes + 2 ) ;
2019-09-09 05:19:06 +08:00
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_CLONE , & resp , 4500 ) = = 0 ) {
2019-03-14 19:30:32 +08:00
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
2019-09-09 05:19:06 +08:00
DropField ( ) ;
return PM3_ETIMEOUT ;
2019-03-10 06:35:06 +08:00
}
2019-09-13 22:31:17 +08:00
if ( resp . status = = PM3_SUCCESS ) {
if ( resp . data . asBytes [ 0 ] = = 1 )
2019-09-09 05:19:06 +08:00
PrintAndLogEx ( SUCCESS , " Clone successful " ) ;
else
PrintAndLogEx ( WARNING , " Clone failed " ) ;
}
return resp . status ;
2015-10-08 05:00:46 +08:00
}
2019-03-10 18:20:22 +08:00
static int ReadBlock ( uint8_t * KEY , uint8_t blockno , uint8_t keyType , bool elite , bool rawkey , bool verbose , bool auth ) {
2019-03-10 06:35:06 +08:00
2019-09-08 23:37:14 +08:00
int numberAuthRetries = ICLASS_AUTH_RETRY ;
// return data.
struct p {
2019-09-13 22:31:17 +08:00
bool isOK ;
2019-09-08 23:37:14 +08:00
uint8_t blockdata [ 8 ] ;
} PACKED ;
2019-03-10 06:35:06 +08:00
2019-09-08 23:37:14 +08:00
struct p * result = NULL ;
2019-09-13 22:31:17 +08:00
2019-09-08 23:37:14 +08:00
do {
// block 0,1 should always be able to read, and block 5 on some cards.
if ( auth | | blockno > = 2 ) {
uint8_t MAC [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ;
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
if ( ! select_and_auth ( KEY , MAC , div_key , ( keyType = = 0x18 ) , elite , rawkey , verbose ) ) {
numberAuthRetries - - ;
2019-09-09 03:21:30 +08:00
DropField ( ) ;
2019-09-08 23:37:14 +08:00
continue ;
}
} else {
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 ) ) {
numberAuthRetries - - ;
2019-09-09 03:21:30 +08:00
DropField ( ) ;
2019-09-08 23:37:14 +08:00
continue ;
}
2019-09-13 22:31:17 +08:00
}
2019-09-08 23:37:14 +08:00
PacketResponseNG resp ;
2019-09-09 03:21:30 +08:00
clearCommandBuffer ( ) ;
2019-09-13 22:31:17 +08:00
SendCommandNG ( CMD_HF_ICLASS_READBL , ( uint8_t * ) & blockno , sizeof ( uint8_t ) ) ;
2019-09-08 23:37:14 +08:00
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_READBL , & resp , 2000 ) = = 0 ) {
2019-09-09 03:21:30 +08:00
if ( verbose ) PrintAndLogEx ( WARNING , " Command execute timeout " ) ;
DropField ( ) ;
2019-09-08 23:37:14 +08:00
return PM3_ETIMEOUT ;
}
2019-09-13 22:31:17 +08:00
if ( resp . status ! = PM3_SUCCESS ) {
2019-09-09 03:21:30 +08:00
if ( verbose ) PrintAndLogEx ( ERR , " failed to communicate with card " ) ;
DropField ( ) ;
2019-09-08 23:37:14 +08:00
return PM3_EWRONGANSVER ;
}
2019-09-13 22:31:17 +08:00
result = ( struct p * ) resp . data . asBytes ;
2019-09-08 23:37:14 +08:00
if ( result - > isOK )
break ;
} while ( numberAuthRetries ) ;
2019-09-09 03:21:30 +08:00
DropField ( ) ;
2019-09-13 22:31:17 +08:00
if ( numberAuthRetries = = 0 ) {
PrintAndLogEx ( ERR , " failed to authenticate and read block " ) ;
2019-09-08 23:37:14 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-09-12 04:51:13 +08:00
PrintAndLogEx ( SUCCESS , " block %02X: %s \n " , blockno , sprint_hex ( result - > blockdata , sizeof ( result - > blockdata ) ) ) ;
2020-03-11 00:10:05 +08:00
2020-02-27 23:35:17 +08:00
if ( blockno = = 6 ) {
if ( IsCryptoHelperPresent ( ) ) {
DecodeBlock6 ( result - > blockdata ) ;
}
}
2019-09-08 23:37:14 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClass_ReadBlock ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
uint8_t blockno = 0 ;
uint8_t keyType = 0x88 ; //debit key
2019-03-10 07:00:59 +08:00
uint8_t KEY [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2019-03-10 06:35:06 +08:00
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
char tempStr [ 50 ] = { 0 } ;
2019-09-09 03:21:30 +08:00
bool got_blockno = false ;
2019-03-10 06:35:06 +08:00
bool elite = false ;
bool rawkey = false ;
bool errors = false ;
bool auth = false ;
bool verbose = false ;
uint8_t cmdp = 0 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2019-03-10 07:00:59 +08:00
case ' h ' :
return usage_hf_iclass_readblock ( ) ;
case ' b ' :
2019-09-08 23:37:14 +08:00
blockno = param_get8ex ( Cmd , cmdp + 1 , 7 , 16 ) ;
2019-09-09 03:21:30 +08:00
got_blockno = true ;
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' c ' :
2019-09-08 23:37:14 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " CREDIT " ) ) ;
2019-03-10 07:00:59 +08:00
keyType = 0x18 ;
cmdp + + ;
break ;
case ' e ' :
2019-09-08 23:37:14 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " elite algo " ) ) ;
2019-03-10 07:00:59 +08:00
elite = true ;
cmdp + + ;
break ;
case ' k ' :
auth = true ;
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
if ( keyNbr < ICLASS_KEYS_MAX ) {
2019-09-13 22:31:17 +08:00
PrintAndLogEx ( SUCCESS , " Using key[%d] %s " , keyNbr , sprint_hex ( iClass_Key_Table [ keyNbr ] , 8 ) ) ;
2019-03-10 07:00:59 +08:00
memcpy ( KEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
errors = true ;
}
2019-03-10 06:35:06 +08:00
} else {
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
}
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' r ' :
2019-09-08 23:37:14 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " raw mode " ) ) ;
2019-03-10 07:00:59 +08:00
rawkey = true ;
cmdp + + ;
break ;
case ' v ' :
verbose = true ;
cmdp + + ;
break ;
default :
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
2019-03-10 07:00:59 +08:00
break ;
2019-03-10 06:35:06 +08:00
}
}
2019-09-13 22:31:17 +08:00
if ( got_blockno = = false )
2019-09-09 03:21:30 +08:00
errors = true ;
2019-09-13 22:31:17 +08:00
2019-03-10 06:35:06 +08:00
if ( errors | | cmdp < 4 ) return usage_hf_iclass_readblock ( ) ;
if ( ! auth )
PrintAndLogEx ( FAILED , " warning: no authentication used with read, only a few specific blocks can be read accurately without authentication. " ) ;
2019-08-24 11:08:03 +08:00
2019-03-10 06:35:06 +08:00
return ReadBlock ( KEY , blockno , keyType , elite , rawkey , verbose , auth ) ;
2015-10-08 05:00:46 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClass_loclass ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
char opt = tolower ( param_getchar ( Cmd , 0 ) ) ;
2019-03-10 07:00:59 +08:00
if ( strlen ( Cmd ) < 1 | | opt = = ' h ' )
2019-08-29 13:47:17 +08:00
return usage_hf_iclass_loclass ( ) ;
2019-03-10 06:35:06 +08:00
if ( opt = = ' f ' ) {
2019-06-08 00:41:39 +08:00
char fileName [ FILE_PATH_SIZE ] = { 0 } ;
2019-03-10 06:35:06 +08:00
if ( param_getstr ( Cmd , 1 , fileName , sizeof ( fileName ) ) > 0 ) {
return bruteforceFileNoKeys ( fileName ) ;
} else {
PrintAndLogEx ( WARNING , " You must specify a filename " ) ;
2019-08-29 13:47:17 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
} else if ( opt = = ' t ' ) {
2019-09-22 00:32:07 +08:00
char opt2 = tolower ( param_getchar ( Cmd , 1 ) ) ;
2019-03-10 06:35:06 +08:00
int errors = testCipherUtils ( ) ;
errors + = testMAC ( ) ;
errors + = doKeyTests ( 0 ) ;
2019-10-13 06:48:26 +08:00
errors + = testElite ( opt2 = = ' l ' ) ;
2019-08-08 22:57:33 +08:00
if ( errors ) PrintAndLogEx ( ERR , " There were errors!!! " ) ;
2019-08-29 21:55:52 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-01-05 16:16:06 +08:00
}
2014-04-15 18:47:01 +08:00
2019-03-10 18:20:22 +08:00
void printIclassDumpContents ( uint8_t * iclass_dump , uint8_t startblock , uint8_t endblock , size_t filesize ) {
2019-03-09 15:59:13 +08:00
2020-04-03 14:36:55 +08:00
uint8_t maxmemcount ;
2019-03-10 06:35:06 +08:00
uint8_t filemaxblock = filesize / 8 ;
2020-04-03 14:36:55 +08:00
uint8_t mem_config = iclass_dump [ 13 ] ;
2016-01-20 02:31:34 +08:00
2019-03-10 06:35:06 +08:00
if ( mem_config & 0x80 )
maxmemcount = 255 ;
else
maxmemcount = 31 ;
2015-10-08 05:00:46 +08:00
2019-03-10 06:35:06 +08:00
if ( startblock = = 0 )
startblock = 6 ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
if ( ( endblock > maxmemcount ) | | ( endblock = = 0 ) )
endblock = maxmemcount ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
// remember endblock needs to relate to zero-index arrays.
2019-03-10 07:00:59 +08:00
if ( endblock > filemaxblock - 1 )
endblock = filemaxblock - 1 ;
2016-12-09 21:38:51 +08:00
2020-04-03 14:36:55 +08:00
/*
PrintAndLogEx ( INFO , " startblock: %u, endblock: %u, filesize: %zu, maxmemcount: %u, filemaxblock: %u "
, startblock
, endblock
, filesize
, maxmemcount
, filemaxblock
) ;
*/
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
int i = startblock ;
2020-02-04 07:41:57 +08:00
PrintAndLogEx ( INFO , " ------+--+-------------------------+---------- " ) ;
2019-03-10 07:00:59 +08:00
while ( i < = endblock ) {
2019-03-10 06:35:06 +08:00
uint8_t * blk = iclass_dump + ( i * 8 ) ;
2020-02-04 07:41:57 +08:00
PrintAndLogEx ( INFO , " |%02X| %s " , i , sprint_hex_ascii ( blk , 8 ) ) ;
2019-03-10 06:35:06 +08:00
i + + ;
}
2020-02-04 07:41:57 +08:00
PrintAndLogEx ( INFO , " ------+--+-------------------------+---------- " ) ;
2015-10-08 05:00:46 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassReadTagFile ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
int startblock = 0 ;
int endblock = 0 ;
char filename [ FILE_PATH_SIZE ] ;
2020-04-03 15:11:12 +08:00
bool errors = false , verbose = false ;
2020-04-03 14:36:55 +08:00
uint8_t cmdp = 0 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
case ' h ' :
return usage_hf_iclass_readtagfile ( ) ;
case ' f ' :
if ( param_getstr ( Cmd , cmdp + 1 , filename , FILE_PATH_SIZE ) > = FILE_PATH_SIZE ) {
PrintAndLogEx ( FAILED , " Filename too long " ) ;
errors = true ;
break ;
}
cmdp + = 2 ;
break ;
case ' s ' :
startblock = param_get8ex ( Cmd , cmdp + 1 , 0 , 10 ) ;
cmdp + = 2 ;
break ;
case ' e ' :
endblock = param_get8ex ( Cmd , cmdp + 1 , 0 , 10 ) ;
cmdp + = 2 ;
2020-04-03 15:11:12 +08:00
break ;
case ' v ' :
verbose = true ;
cmdp + + ;
break ;
2020-04-03 14:36:55 +08:00
default :
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
errors = true ;
break ;
}
}
2019-03-10 06:35:06 +08:00
2020-04-03 14:36:55 +08:00
if ( errors | | ( strlen ( Cmd ) = = 0 ) ) return usage_hf_iclass_readtagfile ( ) ;
2019-03-10 06:35:06 +08:00
// file handling and reading
2020-04-03 14:36:55 +08:00
FILE * f = fopen ( filename , " rb " ) ;
2019-03-10 07:00:59 +08:00
if ( ! f ) {
2019-05-15 16:24:06 +08:00
PrintAndLogEx ( FAILED , " File: " _YELLOW_ ( " %s " ) " : not found or locked. " , filename ) ;
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
fseek ( f , 0 , SEEK_END ) ;
long fsize = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
2019-04-07 18:41:29 +08:00
if ( fsize < = 0 ) {
2019-07-14 06:35:18 +08:00
PrintAndLogEx ( ERR , " Error, when getting filesize " ) ;
2019-03-10 06:35:06 +08:00
fclose ( f ) ;
2020-04-03 13:22:54 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
uint8_t * dump = calloc ( fsize , sizeof ( uint8_t ) ) ;
2019-03-10 07:00:59 +08:00
if ( ! dump ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
fclose ( f ) ;
2020-04-03 13:22:54 +08:00
return PM3_EMALLOC ;
2019-03-10 06:35:06 +08:00
}
size_t bytes_read = fread ( dump , 1 , fsize , f ) ;
fclose ( f ) ;
2020-04-03 14:36:55 +08:00
2020-04-03 15:11:12 +08:00
if ( verbose ) {
PrintAndLogEx ( INFO , " File: " _YELLOW_ ( " %s " ) , filename ) ;
2020-04-06 13:29:39 +08:00
PrintAndLogEx ( INFO , " File size %zu bytes, file blocks %d (0x%x) " , bytes_read , ( uint16_t ) ( bytes_read > > 3 ) , ( uint16_t ) ( bytes_read > > 3 ) ) ;
2020-04-03 15:11:12 +08:00
PrintAndLogEx ( INFO , " Printing blocks from " ) ;
PrintAndLogEx ( INFO , " start " _YELLOW_ ( " 0x%02x " ) " end " _YELLOW_ ( " 0x%02x " ) , ( startblock = = 0 ) ? 6 : startblock , endblock ) ;
}
2019-03-10 06:35:06 +08:00
uint8_t * csn = dump ;
2020-04-03 14:36:55 +08:00
PrintAndLogEx ( INFO , " ------+--+-------------------------+---------- " ) ;
PrintAndLogEx ( INFO , " CSN |00| " _GREEN_ ( " %s " ) " | " , sprint_hex ( csn , 8 ) ) ;
2019-03-10 06:35:06 +08:00
printIclassDumpContents ( dump , startblock , endblock , bytes_read ) ;
free ( dump ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 18:20:22 +08:00
void HFiClassCalcDivKey ( uint8_t * CSN , uint8_t * KEY , uint8_t * div_key , bool elite ) {
2019-03-10 06:35:06 +08:00
if ( elite ) {
2019-06-08 00:41:39 +08:00
uint8_t keytable [ 128 ] = { 0 } ;
uint8_t key_index [ 8 ] = { 0 } ;
2019-03-10 06:35:06 +08:00
uint8_t key_sel [ 8 ] = { 0 } ;
uint8_t key_sel_p [ 8 ] = { 0 } ;
hash2 ( KEY , keytable ) ;
hash1 ( CSN , key_index ) ;
2019-03-10 07:00:59 +08:00
for ( uint8_t i = 0 ; i < 8 ; i + + )
2019-03-10 06:35:06 +08:00
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 ) ;
}
2015-10-08 05:00:46 +08:00
}
//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
2019-03-10 18:20:22 +08:00
static void HFiClassCalcNewKey ( uint8_t * CSN , uint8_t * OLDKEY , uint8_t * NEWKEY , uint8_t * xor_div_key , bool elite , bool oldElite , bool verbose ) {
2019-03-10 07:00:59 +08:00
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 } ;
2019-03-10 06:35:06 +08:00
//get old div key
HFiClassCalcDivKey ( CSN , OLDKEY , old_div_key , oldElite ) ;
//get new div key
HFiClassCalcDivKey ( CSN , NEWKEY , new_div_key , elite ) ;
2019-08-01 06:14:09 +08:00
for ( uint8_t i = 0 ; i < ARRAYLEN ( old_div_key ) ; i + + ) {
2019-03-10 06:35:06 +08:00
xor_div_key [ i ] = old_div_key [ i ] ^ new_div_key [ i ] ;
}
if ( verbose ) {
2020-04-12 17:07:20 +08:00
PrintAndLogEx ( SUCCESS , " Old div key : %s " , sprint_hex ( old_div_key , 8 ) ) ;
PrintAndLogEx ( SUCCESS , " New div key : %s " , sprint_hex ( new_div_key , 8 ) ) ;
PrintAndLogEx ( SUCCESS , " Xor div key : " _YELLOW_ ( " %s " ) " \n " , sprint_hex ( xor_div_key , 8 ) ) ;
2019-03-10 06:35:06 +08:00
}
2015-10-08 05:00:46 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassCalcNewKey ( const char * Cmd ) {
2019-03-10 07:00:59 +08:00
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 } ;
2019-03-10 06:35:06 +08:00
uint8_t keyNbr = 0 ;
uint8_t dataLen = 0 ;
char tempStr [ 50 ] = { 0 } ;
bool givenCSN = false ;
2020-04-12 17:07:20 +08:00
bool old_elite = false ;
2019-03-10 06:35:06 +08:00
bool elite = false ;
bool errors = false ;
uint8_t cmdp = 0 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2019-03-10 07:00:59 +08:00
case ' h ' :
return usage_hf_iclass_calc_newkey ( ) ;
case ' e ' :
dataLen = param_getstr ( Cmd , cmdp , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 2 )
2020-04-12 17:07:20 +08:00
old_elite = true ;
2019-03-10 07:00:59 +08:00
elite = true ;
cmdp + + ;
break ;
case ' n ' :
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , NEWKEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
if ( keyNbr < ICLASS_KEYS_MAX ) {
memcpy ( NEWKEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
PrintAndLogEx ( WARNING , " \n ERROR: NewKey Nbr is invalid \n " ) ;
errors = true ;
}
2019-03-10 06:35:06 +08:00
} else {
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: NewKey is incorrect length \n " ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
}
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' o ' :
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 16 ) {
errors = param_gethex ( tempStr , 0 , OLDKEY , dataLen ) ;
} else if ( dataLen = = 1 ) {
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
if ( keyNbr < ICLASS_KEYS_MAX ) {
memcpy ( OLDKEY , iClass_Key_Table [ keyNbr ] , 8 ) ;
} else {
PrintAndLogEx ( WARNING , " \n ERROR: Credit KeyNbr is invalid \n " ) ;
errors = true ;
}
2019-03-10 06:35:06 +08:00
} else {
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: Credit Key is incorrect length \n " ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
}
2019-03-10 07:00:59 +08:00
cmdp + = 2 ;
break ;
case ' s ' :
givenCSN = true ;
if ( param_gethex ( Cmd , cmdp + 1 , CSN , 16 ) )
return usage_hf_iclass_calc_newkey ( ) ;
cmdp + = 2 ;
break ;
default :
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
2019-03-10 07:00:59 +08:00
break ;
2019-03-10 06:35:06 +08:00
}
}
if ( errors | | cmdp < 4 ) return usage_hf_iclass_calc_newkey ( ) ;
2019-06-08 00:41:39 +08:00
if ( ! givenCSN ) {
uint8_t CCNR [ 12 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2019-09-11 22:56:08 +08:00
if ( ! select_only ( CSN , CCNR , false , true ) ) {
DropField ( ) ;
2020-04-03 13:22:54 +08:00
return PM3_ESOFT ;
2019-09-11 22:56:08 +08:00
}
2019-06-08 00:41:39 +08:00
}
2019-03-10 06:35:06 +08:00
2020-04-12 17:07:20 +08:00
HFiClassCalcNewKey ( CSN , OLDKEY , NEWKEY , xor_div_key , elite , old_elite , true ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-03-10 18:20:22 +08:00
static int loadKeys ( char * filename ) {
2019-03-10 06:35:06 +08:00
FILE * f ;
f = fopen ( filename , " rb " ) ;
2019-03-10 07:00:59 +08:00
if ( ! f ) {
2019-05-15 16:24:06 +08:00
PrintAndLogEx ( FAILED , " File: " _YELLOW_ ( " %s " ) " : not found or locked. " , filename ) ;
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
fseek ( f , 0 , SEEK_END ) ;
long fsize = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
2019-04-07 18:41:29 +08:00
if ( fsize < = 0 ) {
2019-07-14 06:35:18 +08:00
PrintAndLogEx ( ERR , " Error, when getting filesize " ) ;
2019-03-10 06:35:06 +08:00
fclose ( f ) ;
2020-04-03 13:22:54 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
uint8_t * dump = calloc ( fsize , sizeof ( uint8_t ) ) ;
2019-03-10 07:00:59 +08:00
if ( ! dump ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
fclose ( f ) ;
2020-04-03 13:22:54 +08:00
return PM3_EMALLOC ;
2019-03-10 06:35:06 +08:00
}
size_t bytes_read = fread ( dump , 1 , fsize , f ) ;
fclose ( f ) ;
2019-03-10 07:00:59 +08:00
if ( bytes_read > ICLASS_KEYS_MAX * 8 ) {
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( WARNING , " File is too long to load - bytes: %zu " , bytes_read ) ;
2019-03-10 06:35:06 +08:00
free ( dump ) ;
2020-04-03 13:22:54 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
uint8_t i = 0 ;
2019-03-10 07:00:59 +08:00
for ( ; i < bytes_read / 8 ; i + + )
memcpy ( iClass_Key_Table [ i ] , dump + ( i * 8 ) , 8 ) ;
2019-03-10 06:35:06 +08:00
free ( dump ) ;
2019-08-24 11:08:03 +08:00
PrintAndLogEx ( SUCCESS , " Loaded " _GREEN_ ( " %2d " ) " keys from %s " , i , filename ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-03-10 18:20:22 +08:00
static int saveKeys ( char * filename ) {
2019-03-10 06:35:06 +08:00
FILE * f ;
f = fopen ( filename , " wb " ) ;
if ( ! f ) {
2019-05-15 16:24:06 +08:00
PrintAndLogEx ( FAILED , " File: " _YELLOW_ ( " %s " ) " : not found or locked. " , filename ) ;
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
for ( uint8_t i = 0 ; i < ICLASS_KEYS_MAX ; i + + ) {
if ( fwrite ( iClass_Key_Table [ i ] , 8 , 1 , f ) ! = 1 ) {
2019-05-15 16:24:06 +08:00
PrintAndLogEx ( WARNING , " save key failed to write to file: " _YELLOW_ ( " %s " ) , filename ) ;
2019-03-10 06:35:06 +08:00
break ;
}
}
fclose ( f ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-03-10 18:20:22 +08:00
static int printKeys ( void ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-08-24 11:08:03 +08:00
for ( uint8_t i = 0 ; i < ICLASS_KEYS_MAX ; i + + ) {
2019-08-30 16:45:52 +08:00
if ( memcmp ( iClass_Key_Table [ i ] , " \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " , 8 ) = = 0 )
2019-08-24 11:08:03 +08:00
PrintAndLogEx ( NORMAL , " %u: %s " , i , sprint_hex ( iClass_Key_Table [ i ] , 8 ) ) ;
2019-08-30 16:45:52 +08:00
else
2019-08-24 11:08:03 +08:00
PrintAndLogEx ( NORMAL , " %u: " _YELLOW_ ( " %s " ) , i , sprint_hex ( iClass_Key_Table [ i ] , 8 ) ) ;
}
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassManageKeys ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
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 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2019-03-10 07:00:59 +08:00
case ' h ' :
return usage_hf_iclass_managekeys ( ) ;
case ' f ' :
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
if ( fileNameLen < 1 ) {
2019-08-17 21:59:11 +08:00
PrintAndLogEx ( ERR , " No filename found " ) ;
2019-03-10 07:00:59 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' n ' :
keyNbr = param_get8 ( Cmd , cmdp + 1 ) ;
if ( keyNbr > = ICLASS_KEYS_MAX ) {
2019-08-29 21:55:52 +08:00
PrintAndLogEx ( ERR , " Invalid block number, MAX is " _YELLOW_ ( " %d " ) , ICLASS_KEYS_MAX ) ;
2019-03-10 07:00:59 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' k ' :
operation + = 3 ; //set key
dataLen = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( dataLen = = 16 ) { //ul-c or ev1/ntag key length
errors = param_gethex ( tempStr , 0 , KEY , dataLen ) ;
} else {
PrintAndLogEx ( WARNING , " \n ERROR: Key is incorrect length \n " ) ;
errors = true ;
}
cmdp + = 2 ;
break ;
case ' p ' :
operation + = 4 ; //print keys in memory
cmdp + + ;
break ;
case ' l ' :
operation + = 5 ; //load keys from file
cmdp + + ;
break ;
case ' s ' :
operation + = 6 ; //save keys to file
cmdp + + ;
break ;
default :
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
2019-03-10 07:00:59 +08:00
break ;
2019-03-10 06:35:06 +08:00
}
}
if ( errors ) return usage_hf_iclass_managekeys ( ) ;
2019-03-10 07:00:59 +08:00
if ( operation = = 0 ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " no operation specified (load, save, or print) \n " ) ;
return usage_hf_iclass_managekeys ( ) ;
}
2019-03-10 07:00:59 +08:00
if ( operation > 6 ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " Too many operations specified \n " ) ;
return usage_hf_iclass_managekeys ( ) ;
}
2019-03-10 07:00:59 +08:00
if ( operation > 4 & & fileNameLen = = 0 ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " You must enter a filename when loading or saving \n " ) ;
return usage_hf_iclass_managekeys ( ) ;
}
2019-03-10 07:00:59 +08:00
switch ( operation ) {
case 3 :
memcpy ( iClass_Key_Table [ keyNbr ] , KEY , 8 ) ;
2019-08-24 11:08:03 +08:00
return PM3_SUCCESS ;
2019-03-10 07:00:59 +08:00
case 4 :
return printKeys ( ) ;
case 5 :
return loadKeys ( filename ) ;
case 6 :
return saveKeys ( filename ) ;
2019-03-10 06:35:06 +08:00
}
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassCheckKeys ( const char * Cmd ) {
2017-12-13 17:18:38 +08:00
2019-03-10 06:35:06 +08:00
// empty string
if ( strlen ( Cmd ) = = 0 ) return usage_hf_iclass_chk ( ) ;
2019-03-10 07:00:59 +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 } ;
2019-03-10 06:35:06 +08:00
// elite key, raw key, standard key
bool use_elite = false ;
bool use_raw = false ;
bool use_credit_key = false ;
bool found_debit = false ;
//bool found_credit = false;
bool got_csn = false ;
bool errors = false ;
uint8_t cmdp = 0x00 ;
char filename [ FILE_PATH_SIZE ] = { 0 } ;
uint8_t fileNameLen = 0 ;
iclass_premac_t * pre = NULL ;
// time
uint64_t t1 = msclock ( ) ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
2019-03-10 07:00:59 +08:00
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
case ' h ' :
return usage_hf_iclass_chk ( ) ;
case ' f ' :
fileNameLen = param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) ;
if ( fileNameLen < 1 ) {
2019-08-24 11:08:03 +08:00
PrintAndLogEx ( WARNING , _RED_ ( " no filename found after f " ) ) ;
2019-03-10 07:00:59 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' e ' :
use_elite = true ;
cmdp + + ;
break ;
case ' c ' :
use_credit_key = true ;
cmdp + + ;
break ;
case ' r ' :
use_raw = true ;
cmdp + + ;
break ;
default :
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
2019-03-10 07:00:59 +08:00
break ;
2019-03-10 06:35:06 +08:00
}
}
if ( errors ) return usage_hf_iclass_chk ( ) ;
2019-08-29 13:47:17 +08:00
uint8_t * keyBlock = NULL ;
uint16_t keycount = 0 ;
// load keys
2019-08-30 16:45:52 +08:00
int res = loadFileDICTIONARY_safe ( filename , ( void * * ) & keyBlock , 8 , & keycount ) ;
2019-08-29 13:47:17 +08:00
if ( res ! = PM3_SUCCESS | | keycount = = 0 ) {
free ( keyBlock ) ;
return res ;
}
2019-03-10 06:35:06 +08:00
// Get CSN / UID and CCNR
PrintAndLogEx ( SUCCESS , " Reading tag CSN " ) ;
2019-09-08 23:37:14 +08:00
for ( uint8_t i = 0 ; i < ICLASS_AUTH_RETRY & & ! got_csn ; i + + ) {
2019-08-29 13:47:17 +08:00
got_csn = select_only ( CSN , CCNR , false , false ) ;
2019-08-30 16:45:52 +08:00
if ( got_csn = = false )
2019-09-11 22:56:08 +08:00
PrintAndLogEx ( WARNING , " one more try " ) ;
2019-08-30 16:45:52 +08:00
}
2019-03-10 06:35:06 +08:00
2019-08-30 16:45:52 +08:00
if ( got_csn = = false ) {
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( WARNING , " Tried 10 times. Can't select card, aborting... " ) ;
2019-09-11 22:56:08 +08:00
DropField ( ) ;
2019-08-24 11:08:03 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-08-29 13:47:17 +08:00
pre = calloc ( keycount , sizeof ( iclass_premac_t ) ) ;
2019-03-10 07:00:59 +08:00
if ( ! pre ) {
2019-09-11 22:56:08 +08:00
DropField ( ) ;
2019-03-10 06:35:06 +08:00
free ( keyBlock ) ;
2019-08-24 11:08:03 +08:00
return PM3_EMALLOC ;
2019-03-10 06:35:06 +08:00
}
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( SUCCESS , " Generating diversified keys " ) ;
2019-03-10 06:35:06 +08:00
if ( use_elite )
2019-08-24 11:08:03 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " elite algo " ) ) ;
2019-03-10 06:35:06 +08:00
if ( use_raw )
2019-08-24 11:08:03 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " raw mode " ) ) ;
2019-03-10 06:35:06 +08:00
2019-08-24 11:08:03 +08:00
PrintAndLogEx ( SUCCESS , " Searching for " _YELLOW_ ( " %s " ) " key " , ( use_credit_key ) ? " CREDIT " : " DEBIT " ) ;
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( SUCCESS , " Tag info " ) ;
PrintAndLogEx ( SUCCESS , " CSN | %s " , sprint_hex ( CSN , sizeof ( CSN ) ) ) ;
PrintAndLogEx ( SUCCESS , " CCNR | %s " , sprint_hex ( CCNR , sizeof ( CCNR ) ) ) ;
2019-08-29 13:47:17 +08:00
GenerateMacFrom ( CSN , CCNR , use_raw , use_elite , keyBlock , keycount , pre ) ;
2019-03-10 06:35:06 +08:00
//PrintPreCalcMac(keyBlock, keycnt, pre);
// max 42 keys inside USB_COMMAND. 512/4 = 103 mac
2019-08-29 13:47:17 +08:00
uint32_t chunksize = keycount > ( PM3_CMD_DATA_SIZE / 4 ) ? ( PM3_CMD_DATA_SIZE / 4 ) : keycount ;
2019-03-10 06:35:06 +08:00
bool lastChunk = false ;
2019-04-30 20:19:26 +08:00
// fast push mode
conn . block_after_ACK = true ;
2019-05-01 07:38:52 +08:00
2019-08-24 11:08:03 +08:00
// keep track of position of found key
uint8_t found_offset = 0 ;
uint32_t key_offset = 0 ;
2019-03-10 06:35:06 +08:00
// main keychunk loop
2019-10-20 04:39:28 +08:00
for ( key_offset = 0 ; key_offset < keycount ; key_offset + = chunksize ) {
2019-03-10 06:35:06 +08:00
uint64_t t2 = msclock ( ) ;
uint8_t timeout = 0 ;
2019-07-11 19:01:34 +08:00
if ( kbd_enter_pressed ( ) ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " \n [!] Aborted via keyboard! \n " ) ;
goto out ;
}
2019-08-29 13:47:17 +08:00
uint32_t keys = ( ( keycount - key_offset ) > chunksize ) ? chunksize : keycount - key_offset ;
2019-03-10 06:35:06 +08:00
// last chunk?
2019-08-29 13:47:17 +08:00
if ( keys = = keycount - key_offset ) {
2019-03-10 06:35:06 +08:00
lastChunk = true ;
2019-05-09 01:16:37 +08:00
// Disable fast mode on last command
conn . block_after_ACK = false ;
}
2019-04-19 23:03:39 +08:00
uint32_t flags = lastChunk < < 8 ;
2019-03-10 06:35:06 +08:00
// bit 16
// - 1 indicates credit key
// - 0 indicates debit key (default)
2019-04-19 23:03:39 +08:00
flags | = ( use_credit_key < < 16 ) ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-08-24 11:08:03 +08:00
SendCommandOLD ( CMD_HF_ICLASS_CHKKEYS , flags , keys , 0 , pre + key_offset , 4 * keys ) ;
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
while ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
2019-03-10 06:35:06 +08:00
timeout + + ;
2019-03-10 07:00:59 +08:00
printf ( " . " ) ;
fflush ( stdout ) ;
2019-03-10 06:35:06 +08:00
if ( timeout > 120 ) {
2019-04-17 02:00:25 +08:00
PrintAndLogEx ( WARNING , " \n No response from Proxmark3. Aborting... " ) ;
2019-03-10 06:35:06 +08:00
goto out ;
}
}
2019-08-24 11:08:03 +08:00
found_offset = resp . oldarg [ 1 ] & 0xFF ;
2019-04-18 05:44:48 +08:00
uint8_t isOK = resp . oldarg [ 0 ] & 0xFF ;
2019-03-10 06:35:06 +08:00
t2 = msclock ( ) - t2 ;
2019-03-10 07:00:59 +08:00
switch ( isOK ) {
2019-03-10 06:35:06 +08:00
case 1 : {
found_debit = true ;
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( NORMAL , " \n [-] Chunk [%d/%d]: %.1fs [%s] idx [%u] - found key " _YELLOW_ ( " %s " )
2019-08-24 11:08:03 +08:00
, key_offset
2019-08-29 13:47:17 +08:00
, keycount
2019-03-10 07:00:59 +08:00
, ( float ) ( t2 / 1000.0 )
, ( use_credit_key ) ? " credit " : " debit "
2019-08-24 11:08:03 +08:00
, found_offset
2019-08-29 13:47:17 +08:00
, sprint_hex ( keyBlock + ( key_offset + found_offset ) * 8 , 8 )
2019-03-10 07:00:59 +08:00
) ;
2019-03-10 06:35:06 +08:00
break ;
}
case 0 : {
PrintAndLogEx ( NORMAL , " \n [-] Chunk [%d/%d] : %.1fs [%s] "
2019-08-24 11:08:03 +08:00
, key_offset
2019-08-29 13:47:17 +08:00
, keycount
2019-03-10 07:00:59 +08:00
, ( float ) ( t2 / 1000.0 )
, ( use_credit_key ) ? " credit " : " debit "
) ;
2019-03-10 06:35:06 +08:00
break ;
}
case 99 : {
}
2019-03-10 07:00:59 +08:00
default :
break ;
2019-03-10 06:35:06 +08:00
}
// both keys found.
2019-03-10 07:00:59 +08:00
if ( found_debit ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( SUCCESS , " All keys found, exiting " ) ;
break ;
}
} // end chunks of keys
2019-03-09 15:59:13 +08:00
out :
2019-03-10 06:35:06 +08:00
t1 = msclock ( ) - t1 ;
2017-12-13 17:18:38 +08:00
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( SUCCESS , " \n Time in iclass checkkeys: %.0f seconds \n " , ( float ) t1 / 1000.0 ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
2019-08-24 11:08:03 +08:00
2019-08-30 16:45:52 +08:00
// add to managekeys
if ( found_debit ) {
for ( uint8_t i = 0 ; i < ICLASS_KEYS_MAX ; i + + ) {
2019-08-24 11:08:03 +08:00
// simple check for preexistences
2019-08-30 16:45:52 +08:00
if ( memcmp ( iClass_Key_Table [ i ] , keyBlock + ( key_offset + found_offset ) * 8 , 8 ) = = 0 ) break ;
2019-08-24 11:08:03 +08:00
2019-08-30 16:45:52 +08:00
if ( memcmp ( iClass_Key_Table [ i ] , " \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " , 8 ) = = 0 ) {
2019-08-24 11:08:03 +08:00
memcpy ( iClass_Key_Table [ i ] , keyBlock + ( key_offset + found_offset ) * 8 , 8 ) ;
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( SUCCESS , " Added key to keyslot [%d] - " _YELLOW_ ( " `hf iclass managekeys p` " ) " to view " , i ) ;
2019-08-24 11:08:03 +08:00
break ;
}
}
}
2019-03-10 06:35:06 +08:00
free ( pre ) ;
free ( keyBlock ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2017-12-13 17:18:38 +08:00
}
2018-01-02 18:17:31 +08:00
2019-03-10 18:20:22 +08:00
static int cmp_uint32 ( const void * a , const void * b ) {
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
const iclass_prekey_t * x = ( const iclass_prekey_t * ) a ;
const iclass_prekey_t * y = ( const iclass_prekey_t * ) b ;
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
uint32_t mx = bytes_to_num ( ( uint8_t * ) x - > mac , 4 ) ;
uint32_t my = bytes_to_num ( ( uint8_t * ) y - > mac , 4 ) ;
2019-03-09 15:59:13 +08:00
2018-01-02 18:17:31 +08:00
if ( mx < my )
2019-03-10 06:35:06 +08:00
return - 1 ;
2019-03-09 15:59:13 +08:00
else
2019-03-10 06:35:06 +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.
2019-04-13 00:41:14 +08:00
static int CmdHFiClassLookUp ( const char * Cmd ) {
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
uint8_t CSN [ 8 ] ;
2019-03-10 07:00:59 +08:00
uint8_t EPURSE [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
uint8_t MACS [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2019-03-10 06:35:06 +08:00
uint8_t CCNR [ 12 ] ;
2019-03-10 07:00:59 +08:00
uint8_t MAC_TAG [ 4 ] = { 0 , 0 , 0 , 0 } ;
2019-03-10 06:35:06 +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 } ;
iclass_prekey_t * prekey = NULL ;
2019-08-29 13:47:17 +08:00
int len = 0 ;
2019-03-10 06:35:06 +08:00
// if empty string
if ( strlen ( Cmd ) = = 0 ) errors = true ;
// time
uint64_t t1 = msclock ( ) ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2019-03-10 07:00:59 +08:00
case ' h ' :
return usage_hf_iclass_lookup ( ) ;
case ' f ' :
2019-08-30 16:45:52 +08:00
if ( param_getstr ( Cmd , cmdp + 1 , filename , sizeof ( filename ) ) < 1 ) {
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( WARNING , " No filename found after f " ) ;
errors = true ;
}
cmdp + = 2 ;
break ;
case ' u ' :
param_gethex_ex ( Cmd , cmdp + 1 , CSN , & len ) ;
if ( len > > 1 ! = sizeof ( CSN ) ) {
2019-10-10 00:03:56 +08:00
PrintAndLogEx ( WARNING , " Wrong CSN length, expected %zu got [%d] " , sizeof ( CSN ) , len > > 1 ) ;
2019-03-10 07:00:59 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' m ' :
param_gethex_ex ( Cmd , cmdp + 1 , MACS , & len ) ;
if ( len > > 1 ! = sizeof ( MACS ) ) {
2019-10-10 00:03:56 +08:00
PrintAndLogEx ( WARNING , " Wrong MACS length, expected %zu got [%d] " , sizeof ( MACS ) , len > > 1 ) ;
2019-03-10 07:00:59 +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 ) ) {
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( WARNING , " Wrong EPURSE length, expected %zu got [%d] " , sizeof ( EPURSE ) , len > > 1 ) ;
2019-03-10 07:00:59 +08:00
errors = true ;
}
cmdp + = 2 ;
break ;
case ' e ' :
use_elite = true ;
cmdp + + ;
break ;
case ' r ' :
use_raw = true ;
cmdp + + ;
break ;
default :
PrintAndLogEx ( WARNING , " unknown parameter '%c' \n " , param_getchar ( Cmd , cmdp ) ) ;
2019-03-10 06:35:06 +08:00
errors = true ;
2019-03-10 07:00:59 +08:00
break ;
2019-03-10 06:35:06 +08:00
}
}
if ( errors ) return usage_hf_iclass_lookup ( ) ;
// stupid copy.. CCNR is a combo of epurse and reader nonce
memcpy ( CCNR , EPURSE , 8 ) ;
2019-03-10 07:00:59 +08:00
memcpy ( CCNR + 8 , MACS , 4 ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +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 ) ) ) ;
2019-03-10 06:35:06 +08:00
2019-08-29 13:47:17 +08:00
uint8_t * keyBlock = NULL ;
uint16_t keycount = 0 ;
// load keys
2019-08-30 16:45:52 +08:00
int res = loadFileDICTIONARY_safe ( filename , ( void * * ) & keyBlock , 8 , & keycount ) ;
2019-08-29 13:47:17 +08:00
if ( res ! = PM3_SUCCESS | | keycount = = 0 ) {
2019-03-10 06:35:06 +08:00
free ( keyBlock ) ;
2019-08-29 13:47:17 +08:00
return res ;
2019-03-10 06:35:06 +08:00
}
2019-08-30 16:45:52 +08:00
2019-03-10 06:35:06 +08:00
//iclass_prekey_t
2019-08-29 13:47:17 +08:00
prekey = calloc ( keycount , sizeof ( iclass_prekey_t ) ) ;
2019-03-10 07:00:59 +08:00
if ( ! prekey ) {
2019-03-10 06:35:06 +08:00
free ( keyBlock ) ;
2019-08-29 13:47:17 +08:00
return PM3_EMALLOC ;
2019-03-10 06:35:06 +08:00
}
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( INFO , " Generating diversified keys " ) ;
GenerateMacKeyFrom ( CSN , CCNR , use_raw , use_elite , keyBlock , keycount , prekey ) ;
2019-03-10 06:35:06 +08:00
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( INFO , " Sorting " ) ;
2019-03-10 06:35:06 +08:00
// sort mac list.
2019-08-29 13:47:17 +08:00
qsort ( prekey , keycount , sizeof ( iclass_prekey_t ) , cmp_uint32 ) ;
2019-03-10 06:35:06 +08:00
//PrintPreCalc(prekey, keycnt);
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( INFO , " Searching " ) ;
2019-03-10 06:35:06 +08:00
iclass_prekey_t * item ;
iclass_prekey_t lookup ;
memcpy ( lookup . mac , MAC_TAG , 4 ) ;
// binsearch
2019-08-29 13:47:17 +08:00
item = ( iclass_prekey_t * ) bsearch ( & lookup , prekey , keycount , sizeof ( iclass_prekey_t ) , cmp_uint32 ) ;
2019-03-10 06:35:06 +08:00
t1 = msclock ( ) - t1 ;
2019-03-10 07:00:59 +08:00
PrintAndLogEx ( NORMAL , " \n Time in iclass : %.0f seconds \n " , ( float ) t1 / 1000.0 ) ;
2019-08-24 11:08:03 +08:00
// foudn
if ( item ! = NULL ) {
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( SUCCESS , " [debit] found key " _YELLOW_ ( " %s " ) , sprint_hex ( item - > key , 8 ) ) ;
2019-08-30 16:45:52 +08:00
for ( uint8_t i = 0 ; i < ICLASS_KEYS_MAX ; i + + ) {
2019-08-24 11:08:03 +08:00
// simple check for preexistences
2019-08-30 16:45:52 +08:00
if ( memcmp ( item - > key , iClass_Key_Table [ i ] , 8 ) = = 0 ) break ;
2019-08-24 11:08:03 +08:00
2019-08-30 16:45:52 +08:00
if ( memcmp ( iClass_Key_Table [ i ] , " \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " , 8 ) = = 0 ) {
2019-08-24 11:08:03 +08:00
memcpy ( iClass_Key_Table [ i ] , item - > key , 8 ) ;
2019-08-29 13:47:17 +08:00
PrintAndLogEx ( SUCCESS , " Added key to keyslot [%d] - " _YELLOW_ ( " `hf iclass managekeys p` " ) " to view " , i ) ;
2019-08-24 11:08:03 +08:00
break ;
}
}
}
2019-08-30 16:45:52 +08:00
2019-03-10 06:35:06 +08:00
free ( prekey ) ;
free ( keyBlock ) ;
PrintAndLogEx ( NORMAL , " " ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2019-03-09 15:59:13 +08:00
}
2017-12-24 17:59:24 +08:00
// precalc diversified keys and their MAC
2019-08-29 13:47:17 +08:00
void GenerateMacFrom ( uint8_t * CSN , uint8_t * CCNR , bool use_raw , bool use_elite , uint8_t * keys , int keycnt , iclass_premac_t * list ) {
2019-03-10 07:00:59 +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 } ;
2017-12-24 17:59:24 +08:00
2019-03-10 07:00:59 +08:00
for ( int i = 0 ; i < keycnt ; i + + ) {
2017-12-24 17:59:24 +08:00
2019-03-10 07:00:59 +08:00
memcpy ( key , keys + 8 * i , 8 ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
if ( use_raw )
memcpy ( div_key , key , 8 ) ;
else
HFiClassCalcDivKey ( CSN , key , div_key , use_elite ) ;
2017-12-24 17:59:24 +08:00
2019-03-10 06:35:06 +08:00
doMAC ( CCNR , div_key , list [ i ] . mac ) ;
}
2018-01-02 18:17:31 +08:00
}
2019-08-29 13:47:17 +08:00
void GenerateMacKeyFrom ( uint8_t * CSN , uint8_t * CCNR , bool use_raw , bool use_elite , uint8_t * keys , int keycnt , iclass_prekey_t * list ) {
2018-01-02 18:17:31 +08:00
2019-03-10 07:00:59 +08:00
uint8_t div_key [ 8 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2018-01-02 18:17:31 +08:00
2019-03-10 07:00:59 +08:00
for ( int i = 0 ; i < keycnt ; i + + ) {
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
memcpy ( list [ i ] . key , keys + 8 * i , 8 ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
// generate diversifed key
if ( use_raw )
memcpy ( div_key , list [ i ] . key , 8 ) ;
else
HFiClassCalcDivKey ( CSN , list [ i ] . key , div_key , use_elite ) ;
2018-01-02 18:17:31 +08:00
2019-03-10 06:35:06 +08:00
// generate MAC
doMAC ( CCNR , div_key , list [ i ] . mac ) ;
}
2017-12-24 17:59:24 +08:00
}
// print diversified keys
2019-03-10 18:20:22 +08:00
void PrintPreCalcMac ( uint8_t * keys , int keycnt , iclass_premac_t * pre_list ) {
2017-12-24 17:59:24 +08:00
2019-03-10 07:00:59 +08:00
iclass_prekey_t * b = calloc ( keycnt , sizeof ( iclass_prekey_t ) ) ;
if ( ! b )
2019-03-10 06:35:06 +08:00
return ;
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
for ( int i = 0 ; i < keycnt ; i + + ) {
memcpy ( b [ i ] . key , keys + 8 * i , 8 ) ;
2019-03-10 06:35:06 +08:00
memcpy ( b [ i ] . mac , pre_list [ i ] . mac , 4 ) ;
}
PrintPreCalc ( b , keycnt ) ;
free ( b ) ;
2018-01-02 18:17:31 +08:00
}
2019-03-10 18:20:22 +08:00
void PrintPreCalc ( iclass_prekey_t * list , int itemcnt ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " -----+------------------+--------- " ) ;
PrintAndLogEx ( NORMAL , " #key | key | mac " ) ;
PrintAndLogEx ( NORMAL , " -----+------------------+--------- " ) ;
2019-03-10 07:00:59 +08:00
for ( int i = 0 ; i < itemcnt ; i + + ) {
2017-12-24 17:59:24 +08:00
2019-03-10 07:00:59 +08:00
if ( i < 10 ) {
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( NORMAL , " [%2d] | %016 " PRIx64 " | %08 " PRIx64 , i , bytes_to_num ( list [ i ] . key , 8 ) , bytes_to_num ( list [ i ] . mac , 4 ) ) ;
2019-03-10 07:00:59 +08:00
} else if ( i = = 10 ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( SUCCESS , " ... skip printing the rest " ) ;
}
}
2017-12-24 17:59:24 +08:00
}
2017-12-13 17:18:38 +08:00
2019-03-10 18:20:22 +08:00
static void permute ( uint8_t * data , uint8_t len , uint8_t * output ) {
2018-02-05 00:19:08 +08:00
# define KEY_SIZE 8
2019-03-10 07:00:59 +08:00
if ( len > KEY_SIZE ) {
for ( uint8_t m = 0 ; m < len ; m + = KEY_SIZE ) {
permute ( data + m , KEY_SIZE , output + m ) ;
2019-03-10 06:35:06 +08:00
}
return ;
}
2019-03-10 07:00:59 +08:00
if ( len ! = KEY_SIZE ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " [!] wrong key size \n " ) ;
return ;
}
2019-06-08 00:41:39 +08:00
for ( uint8_t i = 0 ; i < KEY_SIZE ; + + i ) {
uint8_t p = 0 ;
uint8_t mask = 0x80 > > i ;
for ( uint8_t j = 0 ; j < KEY_SIZE ; + + j ) {
2019-03-10 06:35:06 +08:00
p > > = 1 ;
if ( data [ j ] & mask )
p | = 0x80 ;
}
output [ i ] = p ;
}
2018-02-05 00:19:08 +08:00
}
2019-03-10 18:20:22 +08:00
static void permute_rev ( uint8_t * data , uint8_t len , uint8_t * output ) {
2019-03-10 06:35:06 +08:00
permute ( data , len , output ) ;
permute ( output , len , data ) ;
permute ( data , len , output ) ;
2018-02-05 00:19:08 +08:00
}
2019-03-10 18:20:22 +08:00
static void simple_crc ( uint8_t * data , uint8_t len , uint8_t * output ) {
2019-03-10 06:35:06 +08:00
uint8_t crc = 0 ;
2019-03-10 07:00:59 +08:00
for ( uint8_t i = 0 ; i < len ; + + i ) {
2019-03-10 06:35:06 +08:00
// seventh byte contains the crc.
2019-03-10 07:00:59 +08:00
if ( ( i & 0x7 ) = = 0x7 ) {
2019-03-10 06:35:06 +08:00
output [ i ] = crc ^ 0xFF ;
crc = 0 ;
} else {
output [ i ] = data [ i ] ;
crc ^ = data [ i ] ;
}
}
2018-02-05 00:19:08 +08:00
}
// DES doesn't use the MSB.
2019-03-10 18:20:22 +08:00
static void shave ( uint8_t * data , uint8_t len ) {
2019-03-10 07:00:59 +08:00
for ( uint8_t i = 0 ; i < len ; + + i )
2019-03-10 06:35:06 +08:00
data [ i ] & = 0xFE ;
2018-02-05 00:19:08 +08:00
}
2019-03-10 18:20:22 +08:00
static void generate_rev ( uint8_t * data , uint8_t len ) {
2019-03-10 06:35:06 +08:00
uint8_t * key = calloc ( len , sizeof ( uint8_t ) ) ;
PrintAndLogEx ( SUCCESS , " input permuted key | %s \n " , sprint_hex ( data , len ) ) ;
permute_rev ( data , len , key ) ;
PrintAndLogEx ( SUCCESS , " unpermuted key | %s \n " , sprint_hex ( key , len ) ) ;
shave ( key , len ) ;
PrintAndLogEx ( SUCCESS , " key | %s \n " , sprint_hex ( key , len ) ) ;
free ( key ) ;
2018-02-05 00:19:08 +08:00
}
2019-03-10 18:20:22 +08:00
static void generate ( uint8_t * data , uint8_t len ) {
2019-03-10 06:35:06 +08:00
uint8_t * key = calloc ( len , sizeof ( uint8_t ) ) ;
uint8_t * pkey = calloc ( len , sizeof ( uint8_t ) ) ;
PrintAndLogEx ( SUCCESS , " input key | %s \n " , sprint_hex ( data , len ) ) ;
permute ( data , len , pkey ) ;
PrintAndLogEx ( SUCCESS , " permuted key | %s \n " , sprint_hex ( pkey , len ) ) ;
2019-03-10 07:00:59 +08:00
simple_crc ( pkey , len , key ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( SUCCESS , " CRC'ed key | %s \n " , sprint_hex ( key , len ) ) ;
free ( key ) ;
free ( pkey ) ;
2018-02-05 00:19:08 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassPermuteKey ( const char * Cmd ) {
2018-02-05 00:19:08 +08:00
2019-03-10 06:35:06 +08:00
uint8_t key [ 8 ] = { 0 } ;
uint8_t data [ 16 ] = { 0 } ;
bool isReverse = false ;
int len = 0 ;
char cmdp = tolower ( param_getchar ( Cmd , 0 ) ) ;
2019-03-10 07:00:59 +08:00
if ( strlen ( Cmd ) = = 0 | | cmdp = = ' h ' ) return usage_hf_iclass_permutekey ( ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 07:00:59 +08:00
isReverse = ( cmdp = = ' r ' ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
param_gethex_ex ( Cmd , 1 , data , & len ) ;
2019-03-10 07:00:59 +08:00
if ( len % 2 ) return usage_hf_iclass_permutekey ( ) ;
2018-02-05 00:19:08 +08:00
2019-03-10 06:35:06 +08:00
len > > = 1 ;
2018-02-05 00:19:08 +08:00
2019-03-10 06:35:06 +08:00
memcpy ( key , data , 8 ) ;
2018-02-05 00:19:08 +08:00
2019-03-10 07:00:59 +08:00
if ( isReverse ) {
2019-03-10 06:35:06 +08:00
generate_rev ( data , len ) ;
2019-06-08 00:41:39 +08:00
uint8_t key_std_format [ 8 ] = { 0 } ;
2019-03-10 06:35:06 +08:00
permutekey_rev ( key , key_std_format ) ;
PrintAndLogEx ( SUCCESS , " holiman iclass key | %s \n " , sprint_hex ( key_std_format , 8 ) ) ;
2019-03-10 07:00:59 +08:00
} else {
2019-03-10 06:35:06 +08:00
generate ( data , len ) ;
2019-06-08 00:41:39 +08:00
uint8_t key_iclass_format [ 8 ] = { 0 } ;
2019-03-10 06:35:06 +08:00
permutekey ( key , key_iclass_format ) ;
PrintAndLogEx ( SUCCESS , " holiman std key | %s \n " , sprint_hex ( key_iclass_format , 8 ) ) ;
}
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2018-02-05 00:19:08 +08:00
}
2016-01-20 02:31:34 +08:00
static command_t CommandTable [ ] = {
2019-05-02 02:48:15 +08:00
{ " help " , CmdHelp , AlwaysAvailable , " This help " } ,
2019-09-07 16:32:16 +08:00
{ " calcnewkey " , CmdHFiClassCalcNewKey , AlwaysAvailable , " [options..] Calc diversified keys (blocks 3 & 4) to write new keys " } ,
{ " chk " , CmdHFiClassCheckKeys , AlwaysAvailable , " [options..] Check keys " } ,
2019-09-09 23:33:44 +08:00
{ " clone " , CmdHFiClassCloneTag , IfPm3Iclass , " [options..] Restore a dump file onto a iClass tag " } ,
{ " decrypt " , CmdHFiClassDecrypt , AlwaysAvailable , " [options..] Decrypt given block data or tag dump file " } ,
{ " dump " , CmdHFiClassReader_Dump , IfPm3Iclass , " [options..] Dump iClass tag to file " } ,
{ " eload " , CmdHFiClassELoad , IfPm3Iclass , " [f <fname>] Load iClass dump file into emulator memory " } ,
2019-09-07 16:32:16 +08:00
{ " encrypt " , CmdHFiClassEncryptBlk , AlwaysAvailable , " [options..] Encrypt given block data " } ,
2019-09-08 23:37:14 +08:00
{ " info " , CmdHFiClassInfo , AlwaysAvailable , " Tag information " } ,
2019-09-07 16:32:16 +08:00
{ " list " , CmdHFiClassList , AlwaysAvailable , " List iClass history " } ,
2019-09-09 23:33:44 +08:00
{ " loclass " , CmdHFiClass_loclass , AlwaysAvailable , " [options..] Use loclass to perform bruteforce reader attack " } ,
2019-05-02 02:48:15 +08:00
{ " lookup " , CmdHFiClassLookUp , AlwaysAvailable , " [options..] Uses authentication trace to check for key in dictionary file " } ,
2019-09-09 23:33:44 +08:00
{ " managekeys " , CmdHFiClassManageKeys , AlwaysAvailable , " [options..] Manage keys to use with iClass " } ,
2019-05-02 05:20:18 +08:00
{ " permutekey " , CmdHFiClassPermuteKey , IfPm3Iclass , " Permute function from 'heart of darkness' paper " } ,
2019-09-08 23:37:14 +08:00
{ " rdbl " , CmdHFiClass_ReadBlock , IfPm3Iclass , " [options..] Read iClass block " } ,
2019-05-02 05:20:18 +08:00
{ " reader " , CmdHFiClassReader , IfPm3Iclass , " Act like an iClass reader " } ,
2019-09-09 23:33:44 +08:00
{ " readtagfile " , CmdHFiClassReadTagFile , AlwaysAvailable , " [options..] Display content from tag dump file " } ,
{ " replay " , CmdHFiClassReader_Replay , IfPm3Iclass , " <mac> Read iClass tag via replay attack " } ,
2019-05-02 05:20:18 +08:00
{ " sim " , CmdHFiClassSim , IfPm3Iclass , " [options..] Simulate iClass tag " } ,
{ " sniff " , CmdHFiClassSniff , IfPm3Iclass , " Eavesdrop iClass communication " } ,
2019-09-08 23:37:14 +08:00
{ " wrbl " , CmdHFiClass_WriteBlock , IfPm3Iclass , " [options..] Write iClass block " } ,
2019-05-02 02:48:15 +08:00
{ NULL , NULL , NULL , NULL }
2011-05-18 20:33:32 +08:00
} ;
2019-04-13 00:41:14 +08:00
static int CmdHelp ( const char * Cmd ) {
( void ) Cmd ; // Cmd is not used so far
CmdsHelp ( CommandTable ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2019-04-13 00:41:14 +08:00
}
2019-03-10 18:20:22 +08:00
int CmdHFiClass ( const char * Cmd ) {
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2019-04-19 06:47:51 +08:00
return CmdsParse ( CommandTable , Cmd ) ;
2011-05-18 20:33:32 +08:00
}
2019-04-13 00:41:14 +08:00
int readIclass ( bool loop , bool verbose ) {
bool tagFound = false ;
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 ;
2020-01-17 20:44:41 +08:00
uint32_t res = PM3_ETIMEOUT ;
2019-04-13 00:41:14 +08:00
// loop in client not device - else on windows have a communication error
2019-07-11 19:01:34 +08:00
while ( ! kbd_enter_pressed ( ) ) {
2019-04-13 00:41:14 +08:00
clearCommandBuffer ( ) ;
2019-08-04 01:17:00 +08:00
SendCommandMIX ( CMD_HF_ICLASS_READER , flags , 0 , 0 , NULL , 0 ) ;
2019-10-19 21:38:53 +08:00
PacketResponseNG resp ;
2019-04-13 00:41:14 +08:00
if ( WaitForResponseTimeout ( CMD_ACK , & resp , 4500 ) ) {
2019-10-19 21:38:53 +08:00
2019-04-18 05:44:48 +08:00
uint8_t readStatus = resp . oldarg [ 0 ] & 0xff ;
uint8_t * data = resp . data . asBytes ;
2019-04-13 00:41:14 +08:00
2019-10-19 21:38:53 +08:00
// if (verbose) PrintAndLogEx(INFO, "Readstatus:%02x", readStatus);
2019-09-11 22:36:41 +08:00
2019-04-13 00:41:14 +08:00
// no tag found or button pressed
if ( ( readStatus = = 0 & & ! loop ) | | readStatus = = 0xFF ) {
// abort
2019-09-13 22:31:17 +08:00
DropField ( ) ;
return PM3_EOPABORTED ;
2019-04-13 00:41:14 +08:00
}
2019-09-11 22:36:41 +08:00
2019-04-13 00:41:14 +08:00
if ( readStatus & FLAG_ICLASS_READER_CSN ) {
2019-12-01 00:06:03 +08:00
PrintAndLogEx ( NORMAL , " \n " ) ;
PrintAndLogEx ( SUCCESS , " CSN: %s " , sprint_hex ( data , 8 ) ) ;
2019-04-13 00:41:14 +08:00
tagFound = true ;
}
2019-09-11 22:36:41 +08:00
2019-04-13 00:41:14 +08:00
if ( readStatus & FLAG_ICLASS_READER_CC ) {
2019-12-01 00:06:03 +08:00
PrintAndLogEx ( SUCCESS , " CC: %s " , sprint_hex ( data + 16 , 8 ) ) ;
2019-04-13 00:41:14 +08:00
}
2019-09-11 22:36:41 +08:00
2019-04-13 00:41:14 +08:00
if ( readStatus & FLAG_ICLASS_READER_CONF ) {
printIclassDumpInfo ( data ) ;
}
2019-09-11 22:36:41 +08:00
2019-10-19 21:38:53 +08:00
// if CSN ends with FF12E0, it's inside HID CSN range.
bool isHidRange = ( memcmp ( ( uint8_t * ) ( data + 5 ) , " \xFF \x12 \xE0 " , 3 ) = = 0 ) ;
2019-04-13 00:41:14 +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 ) ;
bool se_enabled = ( memcmp ( ( uint8_t * ) ( data + 8 * 5 ) , " \xff \xff \xff \x00 \x06 \xff \xff \xff " , 8 ) = = 0 ) ;
2019-12-01 00:06:03 +08:00
PrintAndLogEx ( SUCCESS , " App IA: %s " , sprint_hex ( data + 8 * 5 , 8 ) ) ;
2019-10-19 21:38:53 +08:00
2019-10-20 04:39:28 +08:00
if ( isHidRange ) {
if ( legacy )
PrintAndLogEx ( SUCCESS , " : Possible iClass - legacy credential tag " ) ;
2019-10-19 21:38:53 +08:00
2019-10-20 04:39:28 +08:00
if ( se_enabled )
PrintAndLogEx ( SUCCESS , " : Possible iClass - SE credential tag " ) ;
}
2019-10-19 21:38:53 +08:00
if ( isHidRange ) {
PrintAndLogEx ( SUCCESS , " : Tag is " _YELLOW_ ( " iClass " ) " , CSN is in HID range " ) ;
} else {
PrintAndLogEx ( SUCCESS , " : Tag is " _YELLOW_ ( " PicoPass " ) " , CSN is not in HID range " ) ;
}
2019-04-13 00:41:14 +08:00
}
if ( tagFound & & ! loop ) {
DropField ( ) ;
2019-09-09 23:33:44 +08:00
return PM3_SUCCESS ;
2019-04-13 00:41:14 +08:00
}
} else {
if ( verbose )
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
}
if ( ! loop ) break ;
}
DropField ( ) ;
2020-01-17 20:44:41 +08:00
return res ;
2011-05-18 20:33:32 +08:00
}
2019-04-13 00:41:14 +08:00