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-07-09 06:21:26 +08:00
// Copyright (C) 2019 piwi
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>
2020-07-09 06:21:26 +08:00
# include "cliparser.h"
2019-08-08 22:57:33 +08:00
# include "cmdparser.h" // command_t
# include "commonutil.h" // ARRAYLEN
# include "cmdtrace.h"
2020-10-05 02:52:59 +08:00
# include "cliparser.h"
2019-08-08 22:57:33 +08:00
# include "util_posix.h"
# include "comms.h"
2020-05-25 05:23:55 +08:00
# include "des.h"
2020-04-19 18:53:46 +08:00
# 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"
2020-07-14 02:13:25 +08:00
static uint8_t empty [ 8 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } ;
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 } ,
2020-08-08 17:46:22 +08:00
{ 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-07-21 20:09:05 +08:00
PrintAndLogEx ( NORMAL , " Simulate a iCLASS legacy/standard tag \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iCLASS sim [h] <option> [CSN] \n " ) ;
2020-04-03 15:11:12 +08:00
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-07-21 20:09:05 +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-07-21 20:09:05 +08:00
PrintAndLogEx ( NORMAL , " Loads iCLASS tag dump into emulator memory on device \n " ) ;
2020-04-03 15:11:12 +08:00
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
}
2020-07-21 20:09:05 +08:00
static int usage_hf_iclass_esave ( void ) {
PrintAndLogEx ( NORMAL , " Save emulator memory to file. " ) ;
PrintAndLogEx ( NORMAL , " if not filename is supplied, CSN will be used. " ) ;
PrintAndLogEx ( NORMAL , " Number of bytes to download defaults to 256. Other value is 2048 \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass esave [h] [f <filename>] [s <num of bytes>] \n " ) ;
PrintAndLogEx ( NORMAL , " Options " ) ;
PrintAndLogEx ( NORMAL , " h : Show this help " ) ;
PrintAndLogEx ( NORMAL , " f <filename> : filename of dump " ) ;
PrintAndLogEx ( NORMAL , " s <bytes> : (256|2048) number of bytes to save (default 256) " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass esave " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass esave f hf-iclass-dump.bin " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass esave s 2048 f hf-iclass-dump.bin " ) ) ;
PrintAndLogEx ( NORMAL , " " ) ;
return PM3_SUCCESS ;
}
2020-08-30 15:55:57 +08:00
static int usage_hf_iclass_eview ( void ) {
PrintAndLogEx ( NORMAL , " It displays emulator memory " ) ;
PrintAndLogEx ( NORMAL , " Number of bytes to download defaults to 256. Other value is 2048 \n " ) ;
PrintAndLogEx ( NORMAL , " Usage: hf iclass eview [s <num of bytes>] <v> " ) ;
PrintAndLogEx ( NORMAL , " s <bytes> : (256|2048) number of bytes to save (default 256) " ) ;
PrintAndLogEx ( NORMAL , " v : verbose output " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( NORMAL , " Examples: " ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " hf iclass eview " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " hf iclass eview s 2048 v " ) ) ;
return PM3_SUCCESS ;
}
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 " ) ;
2020-07-21 20:09:05 +08:00
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 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 " ) ;
2020-07-21 20:09:05 +08:00
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 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-07-21 20:09:05 +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 " ) ;
2020-10-13 17:54:38 +08:00
PrintAndLogEx ( NORMAL , " k <key> : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay " ) ;
2020-04-03 15:23:34 +08:00
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 " ) ;
2020-10-13 17:54:38 +08:00
PrintAndLogEx ( NORMAL , " n : replay of NR/MAC " ) ;
2020-04-03 15:23:34 +08:00
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
}
2020-08-15 14:49:39 +08:00
static int usage_hf_iclass_restore ( void ) {
2020-07-21 20:09:05 +08:00
PrintAndLogEx ( NORMAL , " Restore data from dumpfile onto a iCLASS tag \n " ) ;
2020-08-15 14:49:39 +08:00
PrintAndLogEx ( NORMAL , " Usage: hf iclass restore 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 " ) ;
2020-08-15 14:49:39 +08:00
PrintAndLogEx ( NORMAL , " f <filename> : specify a filename to restore " ) ;
PrintAndLogEx ( NORMAL , " b <block> : The first block to restore as 2 hex symbols " ) ;
PrintAndLogEx ( NORMAL , " l <last blk> : The last block to restore as 2 hex symbols " ) ;
2020-04-03 15:11:12 +08:00
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-08-15 14:49:39 +08:00
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass restore f hf-iclass-AA162D30F8FF12F1-dump.bin b 06 l 1A k 1122334455667788 e " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass restore f hf-iclass-AA162D30F8FF12F1-dump b 05 l 19 k 0 " ) ) ;
PrintAndLogEx ( NORMAL , _YELLOW_ ( " \t hf iclass restore 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-07-21 20:09:05 +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-10-13 17:54:38 +08:00
PrintAndLogEx ( NORMAL , " k <key> : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay " ) ;
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) " ) ;
2020-10-13 17:54:38 +08:00
// PrintAndLogEx(NORMAL, " n : replay of NR/MAC");
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-07-21 20:09:05 +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 " ) ;
2020-10-13 17:54:38 +08:00
PrintAndLogEx ( NORMAL , " k <key> : access Key as 16 hex symbols or 1 hex to select key from memory OR NR/MAC for replay " ) ;
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( NORMAL , " c : credit key assumed \n " ) ;
PrintAndLogEx ( NORMAL , " e : elite computations applied to key " ) ;
PrintAndLogEx ( NORMAL , " r : raw, no computations applied to key " ) ;
2020-10-13 17:54:38 +08:00
PrintAndLogEx ( NORMAL , " n : replay of NR/MAC " ) ;
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 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_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 , " " ) ;
2020-05-12 16:30:14 +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-07-21 20:09:05 +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_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 , " " ) ;
2020-05-12 16:30:14 +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
}
2020-10-05 02:52:59 +08:00
2020-08-20 03:51:10 +08:00
static int cmp_uint32 ( const void * a , const void * b ) {
const iclass_prekey_t * x = ( const iclass_prekey_t * ) a ;
const iclass_prekey_t * y = ( const iclass_prekey_t * ) b ;
uint32_t mx = bytes_to_num ( ( uint8_t * ) x - > mac , 4 ) ;
uint32_t my = bytes_to_num ( ( uint8_t * ) y - > mac , 4 ) ;
if ( mx < my )
return - 1 ;
else
return mx > my ;
}
2020-09-07 16:35:09 +08:00
bool check_known_default ( uint8_t * csn , uint8_t * epurse , uint8_t * rmac , uint8_t * tmac , uint8_t * key ) {
2020-08-20 03:51:10 +08:00
iclass_prekey_t * prekey = calloc ( ICLASS_KEYS_MAX , sizeof ( iclass_prekey_t ) ) ;
if ( prekey = = false ) {
return PM3_EMALLOC ;
}
uint8_t ccnr [ 12 ] ;
memcpy ( ccnr , epurse , 8 ) ;
memcpy ( ccnr + 8 , rmac , 4 ) ;
2020-09-07 16:35:09 +08:00
GenerateMacKeyFrom ( csn , ccnr , false , false , ( uint8_t * ) iClass_Key_Table , ICLASS_KEYS_MAX , prekey ) ;
2020-08-20 03:51:10 +08:00
qsort ( prekey , ICLASS_KEYS_MAX , sizeof ( iclass_prekey_t ) , cmp_uint32 ) ;
iclass_prekey_t lookup ;
memcpy ( lookup . mac , tmac , 4 ) ;
// binsearch
2020-09-07 16:35:09 +08:00
iclass_prekey_t * item = ( iclass_prekey_t * ) bsearch ( & lookup , prekey , ICLASS_KEYS_MAX , sizeof ( iclass_prekey_t ) , cmp_uint32 ) ;
2020-08-20 03:51:10 +08:00
if ( item ! = NULL ) {
memcpy ( key , item - > key , 8 ) ;
return true ;
}
return false ;
}
2020-02-27 23:35:17 +08:00
typedef enum {
None = 0 ,
DES ,
RFU ,
TRIPLEDES
} BLOCK79ENCRYPTION ;
2020-07-14 06:16:25 +08:00
static inline uint32_t leadingzeros ( uint64_t a ) {
# if defined __GNUC__
return __builtin_clzll ( a ) ;
# else
return 0 ;
# endif
2020-07-21 19:00:52 +08:00
}
static inline uint32_t countones ( uint64_t a ) {
# if defined __GNUC__
return __builtin_popcountll ( a ) ;
# else
return 0 ;
# endif
2020-07-14 06:16:25 +08:00
}
2020-08-13 18:25:04 +08:00
const char * card_types [ ] = {
2020-07-20 02:45:47 +08:00
" PicoPass 16K / 16 " , // 000
" PicoPass 32K with current book 16K / 16 " , // 001
" Unknown Card Type! " , // 010
" Unknown Card Type! " , // 011
" PicoPass 2K " , // 100
" Unknown Card Type! " , // 101
" PicoPass 16K / 2 " , // 110
" PicoPass 32K with current book 16K / 2 " , // 111
} ;
uint8_t card_app2_limit [ ] = {
0xff ,
0xff ,
0xff ,
0xff ,
0x1f ,
0xff ,
0xff ,
0xff ,
} ;
2020-07-14 06:16:25 +08:00
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 ) ;
}
2020-08-20 03:51:10 +08:00
uint8_t get_pagemap ( const picopass_hdr * hdr ) {
2020-07-29 17:02:30 +08:00
return ( hdr - > conf . fuses & ( FUSE_CRYPT0 | FUSE_CRYPT1 ) ) > > 3 ;
}
2019-08-08 22:57:33 +08:00
static void fuse_config ( const picopass_hdr * hdr ) {
2020-10-18 17:50:20 +08:00
uint16_t otp = ( hdr - > conf . otp [ 1 ] < < 8 | hdr - > conf . otp [ 0 ] ) ;
2020-10-20 07:00:23 +08:00
PrintAndLogEx ( INFO , " Raw: " _YELLOW_ ( " %s " ) , sprint_hex ( ( uint8_t * ) & hdr - > conf , 8 ) ) ;
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " " _YELLOW_ ( " %02X " ) " ..................... app limit " , hdr - > conf . app_limit ) ;
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " " _YELLOW_ ( " %04X " ) " ( %5u )...... OTP " , otp , otp ) ;
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " " _YELLOW_ ( " %02X " ) " ............ block write lock " , hdr - > conf . block_writelock ) ;
PrintAndLogEx ( INFO , " " _YELLOW_ ( " %02X " ) " ......... chip " , hdr - > conf . chip_config ) ;
PrintAndLogEx ( INFO , " " _YELLOW_ ( " %02X " ) " ...... mem " , hdr - > conf . mem_config ) ;
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " " _YELLOW_ ( " %02X " ) " ... EAS " , hdr - > conf . eas ) ;
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " " _YELLOW_ ( " %02X " ) " fuses " , hdr - > conf . fuses ) ;
2020-10-18 17:50:20 +08:00
2019-08-08 22:57:33 +08:00
uint8_t fuses = hdr - > conf . fuses ;
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " Fuses: " ) ;
2019-08-08 22:57:33 +08:00
if ( isset ( fuses , FUSE_FPERS ) )
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " mode......... " _GREEN_ ( " Personalization (programmable) " ) ) ;
2019-08-08 22:57:33 +08:00
else
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " mode......... " _YELLOW_ ( " Application (locked) " ) ) ;
2019-08-08 22:57:33 +08:00
if ( isset ( fuses , FUSE_CODING1 ) ) {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " coding...... RFU " ) ;
2019-08-08 22:57:33 +08:00
} else {
if ( isset ( fuses , FUSE_CODING0 ) )
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " coding....... " _YELLOW_ ( " ISO 14443-2 B / 15693 " ) ) ;
2019-08-08 22:57:33 +08:00
else
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " coding....... " _YELLOW_ ( " ISO 14443-B only " ) ) ;
2019-08-08 22:57:33 +08:00
}
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
uint8_t pagemap = get_pagemap ( hdr ) ;
switch ( pagemap ) {
case 0x0 :
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " crypt........ No auth possible. Read only if RA is enabled " ) ;
2020-07-29 17:02:30 +08:00
break ;
case 0x1 :
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " crypt........ Non secured page " ) ;
2020-07-29 17:02:30 +08:00
break ;
case 0x2 :
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " crypt........ Secured page, keys locked " ) ;
2020-07-29 17:02:30 +08:00
break ;
case 0x03 :
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " crypt........ Secured page, " _GREEN_ ( " keys not locked " ) ) ;
2020-07-29 17:02:30 +08:00
break ;
}
2019-08-08 22:57:33 +08:00
if ( isset ( fuses , FUSE_RA ) )
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " RA........... Read access enabled (non-secure mode) " ) ;
2019-08-08 22:57:33 +08:00
else
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " RA........... Read access not enabled " ) ;
2019-08-08 22:57:33 +08:00
}
2020-07-20 02:45:47 +08:00
static void getMemConfig ( uint8_t mem_cfg , uint8_t chip_cfg , uint8_t * app_areas , uint8_t * kb ) {
2020-08-13 18:25:04 +08:00
// How to determine chip type
2020-08-06 14:14:40 +08:00
2020-08-13 18:25:04 +08:00
// mem-bit 7 = 16K
2020-08-06 14:14:40 +08:00
// mem-bit 5 = Book
// mem-bit 4 = 2K
// chip-bit 4 = Multi App
2019-08-08 22:57:33 +08:00
uint8_t k16 = isset ( mem_cfg , 0x80 ) ;
2020-08-06 14:14:40 +08:00
//uint8_t k2 = isset(mem_cfg, 0x10);
2019-08-08 22:57:33 +08:00
uint8_t book = isset ( mem_cfg , 0x20 ) ;
if ( isset ( chip_cfg , 0x10 ) & & ! k16 & & ! book ) {
* kb = 2 ;
* app_areas = 2 ;
} else if ( isset ( chip_cfg , 0x10 ) & & k16 & & ! book ) {
* kb = 16 ;
* app_areas = 2 ;
} else if ( notset ( chip_cfg , 0x10 ) & & ! k16 & & ! book ) {
* kb = 16 ;
* app_areas = 16 ;
} else if ( isset ( chip_cfg , 0x10 ) & & k16 & & book ) {
* kb = 32 ;
* app_areas = 3 ;
} else if ( notset ( chip_cfg , 0x10 ) & & ! k16 & & book ) {
* kb = 32 ;
* app_areas = 17 ;
} else {
* kb = 32 ;
* app_areas = 2 ;
}
}
2020-07-20 02:45:47 +08:00
static uint8_t get_mem_config ( const picopass_hdr * hdr ) {
// three configuration bits that decides sizes
2020-08-06 14:14:40 +08:00
uint8_t type = ( hdr - > conf . chip_config & 0x10 ) > > 2 ;
// 16K bit 0 == 1==
type | = ( hdr - > conf . mem_config & 0x80 ) > > 6 ;
// BOOK bit 0 == 1==
type | = ( hdr - > conf . mem_config & 0x20 ) > > 5 ;
// 2K
//type |= (hdr->conf.mem_config & 0x10) >> 5;
2020-07-20 02:45:47 +08:00
return type ;
}
2019-08-08 22:57:33 +08:00
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 kb = 2 ;
uint8_t app_areas = 2 ;
2020-07-20 02:45:47 +08:00
getMemConfig ( mem , chip , & app_areas , & kb ) ;
2020-07-29 17:02:30 +08:00
uint8_t type = get_mem_config ( hdr ) ;
2020-07-20 02:45:47 +08:00
uint8_t app1_limit = hdr - > conf . app_limit - 5 ; // minus header blocks
uint8_t app2_limit = card_app2_limit [ type ] ;
2020-07-29 17:02:30 +08:00
uint8_t pagemap = get_pagemap ( hdr ) ;
2019-08-08 22:57:33 +08:00
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " -------------------------- " _CYAN_ ( " Memory " ) " -------------------------- " ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " %u KBits ( " _YELLOW_ ( " %u " ) " bytes ) " , kb , app2_limit * 8 ) ;
2020-07-29 17:02:30 +08:00
PrintAndLogEx ( INFO , " Tag has not App Areas " ) ;
return ;
}
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " %u KBits/%u App Areas ( " _YELLOW_ ( " %u " ) " bytes ) " , kb , app_areas , ( app2_limit + 1 ) * 8 ) ;
2020-08-13 18:25:04 +08:00
PrintAndLogEx ( INFO , " AA1 blocks %u { 0x06 - 0x%02X (06 - %02d) } " , app1_limit , app1_limit + 5 , app1_limit + 5 ) ;
2020-07-29 17:02:30 +08:00
PrintAndLogEx ( INFO , " AA2 blocks %u { 0x%02X - 0x%02X (%02d - %02d) } " , app2_limit - app1_limit , app1_limit + 5 + 1 , app2_limit , app1_limit + 5 + 1 , app2_limit ) ;
2020-06-27 04:20:32 +08:00
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " ------------------------- " _CYAN_ ( " KeyAccess " ) " ------------------------ " ) ;
PrintAndLogEx ( INFO , " * Kd, Debit key, AA1 Kc, Credit key, AA2 * " ) ;
2019-08-08 22:57:33 +08:00
uint8_t book = isset ( mem , 0x20 ) ;
if ( book ) {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " Read A....... debit " ) ;
PrintAndLogEx ( INFO , " Read B....... credit " ) ;
PrintAndLogEx ( INFO , " Write A...... debit " ) ;
PrintAndLogEx ( INFO , " Write B...... credit " ) ;
PrintAndLogEx ( INFO , " Debit........ debit or credit " ) ;
PrintAndLogEx ( INFO , " Credit....... credit " ) ;
2019-08-08 22:57:33 +08:00
} else {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " Read A....... debit or credit " ) ;
PrintAndLogEx ( INFO , " Read B....... debit or credit " ) ;
PrintAndLogEx ( INFO , " Write A...... credit " ) ;
PrintAndLogEx ( INFO , " Write B...... credit " ) ;
PrintAndLogEx ( INFO , " Debit........ debit or credit " ) ;
PrintAndLogEx ( INFO , " redit........ credit " ) ;
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 ) {
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " -------------------- " _CYAN_ ( " card configuration " ) " -------------------- " ) ;
2019-08-08 22:57:33 +08:00
fuse_config ( hdr ) ;
mem_app_config ( hdr ) ;
}
2020-07-21 20:09:05 +08:00
static void print_picopass_header ( const picopass_hdr * hdr ) {
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " --------------------------- " _CYAN_ ( " card " ) " --------------------------- " ) ;
PrintAndLogEx ( SUCCESS , " CSN: " _GREEN_ ( " %s " ) " uid " , sprint_hex ( hdr - > csn , sizeof ( hdr - > csn ) ) ) ;
PrintAndLogEx ( SUCCESS , " Config: %s Card configuration " , sprint_hex ( ( uint8_t * ) & hdr - > conf , sizeof ( hdr - > conf ) ) ) ;
PrintAndLogEx ( SUCCESS , " E-purse: %s Card challenge, CC " , sprint_hex ( hdr - > epurse , sizeof ( hdr - > epurse ) ) ) ;
PrintAndLogEx ( SUCCESS , " Kd: %s Debit key, hidden " , sprint_hex ( hdr - > key_d , sizeof ( hdr - > key_d ) ) ) ;
PrintAndLogEx ( SUCCESS , " Kc: %s Credit key, hidden " , sprint_hex ( hdr - > key_c , sizeof ( hdr - > key_c ) ) ) ;
PrintAndLogEx ( SUCCESS , " AIA: %s Application Issuer area " , sprint_hex ( hdr - > app_issuer_area , sizeof ( hdr - > app_issuer_area ) ) ) ;
2019-08-08 22:57:33 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassList ( const char * Cmd ) {
2020-10-10 02:46:48 +08:00
char args [ 128 ] = { 0 } ;
2020-10-10 02:19:36 +08:00
if ( strlen ( Cmd ) = = 0 ) {
snprintf ( args , sizeof ( args ) , " -t iclass " ) ;
2020-10-10 02:46:48 +08:00
} else {
strncpy ( args , Cmd , sizeof ( args ) - 1 ) ;
2020-10-10 02:19:36 +08:00
}
return CmdTraceList ( args ) ;
2014-04-24 20:13:33 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassSniff ( const char * Cmd ) {
2020-07-09 06:21:26 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf iclass sniff " ,
" Sniff the communication reader and tag " ,
2020-09-30 14:39:08 +08:00
" hf iclass sniff \n "
" hf iclass sniff -j -> jam e-purse updates \n "
2020-08-13 18:25:04 +08:00
) ;
void * argtable [ ] = {
2020-07-09 06:21:26 +08:00
arg_param_begin ,
arg_lit0 ( " j " , " jam " , " Jam (prevent) e-purse updates " ) ,
arg_param_end
} ;
2020-07-14 21:25:56 +08:00
2020-08-13 18:25:04 +08:00
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
2020-07-09 06:21:26 +08:00
bool jam_epurse_update = arg_get_lit ( ctx , 1 ) ;
2020-07-14 21:25:56 +08:00
CLIParserFree ( ctx ) ;
2020-08-13 18:25:04 +08:00
2020-07-09 06:21:26 +08:00
const uint8_t update_epurse_sequence [ 2 ] = { 0x87 , 0x02 } ;
struct {
uint8_t jam_search_len ;
uint8_t jam_search_string [ 2 ] ;
} PACKED payload ;
2020-08-13 18:25:04 +08:00
if ( jam_epurse_update ) {
2020-07-09 06:21:26 +08:00
payload . jam_search_len = sizeof ( update_epurse_sequence ) ;
memcpy ( payload . jam_search_string , update_epurse_sequence , sizeof ( payload . jam_search_string ) ) ;
}
2020-08-13 18:25:04 +08:00
2020-07-20 02:45:47 +08:00
PacketResponseNG resp ;
clearCommandBuffer ( ) ;
2020-07-09 06:21:26 +08:00
SendCommandNG ( CMD_HF_ICLASS_SNIFF , ( uint8_t * ) & payload , sizeof ( payload ) ) ;
2020-08-13 18:25:04 +08:00
2020-07-20 02:45:47 +08:00
WaitForResponse ( CMD_HF_ICLASS_SNIFF , & resp ) ;
2020-08-13 18:25:04 +08:00
2020-08-14 20:55:07 +08:00
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " hf iclass list " ) " ` to view captured tracelog " ) ;
2020-07-20 02:45:47 +08:00
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " trace save h " ) " ` to save tracelog for later analysing " ) ;
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 CSN [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2020-07-04 03:33:17 +08:00
uint8_t sim_type = param_get8ex ( Cmd , 0 , 0 , 10 ) ;
2017-08-19 15:49:41 +08:00
2020-07-04 03:33:17 +08:00
if ( sim_type = = 0 ) {
2019-03-10 06:35:06 +08:00
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-07-04 03:33:17 +08:00
PrintAndLogEx ( INFO , " simtype: %02x CSN: %s " , sim_type , sprint_hex ( CSN , 8 ) ) ;
2019-03-10 06:35:06 +08:00
}
2016-01-20 02:31:34 +08:00
2020-07-04 03:33:17 +08:00
if ( sim_type > 4 ) {
PrintAndLogEx ( ERR , " Undefined simtype %d " , sim_type ) ;
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 ;
2020-07-04 03:33:17 +08:00
switch ( sim_type ) {
2019-03-10 06:35:06 +08:00
2020-07-04 03:33:17 +08:00
case ICLASS_SIM_MODE_READER_ATTACK : {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( INFO , " Starting iCLASS sim 2 attack (elite mode) " ) ;
2020-07-04 03:33:17 +08:00
PrintAndLogEx ( INFO , " press " _YELLOW_ ( " `enter` " ) " to cancel " ) ;
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2020-07-20 06:52:46 +08:00
SendCommandMIX ( CMD_HF_ICLASS_SIMULATE , sim_type , NUM_CSNS , 1 , csns , 8 * NUM_CSNS ) ;
2019-03-10 06:35:06 +08:00
2020-07-20 06:52:46 +08:00
while ( WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) = = false ) {
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-16 15:01:14 +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 ) ;
2020-07-21 20:09:05 +08:00
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " hf iclass loclass h " ) " ` to recover elite key " ) ;
2019-03-10 06:35:06 +08:00
break ;
}
2020-07-04 03:33:17 +08:00
case ICLASS_SIM_MODE_READER_ATTACK_KEYROLL : {
2019-03-10 06:35:06 +08:00
// 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-07-20 06:52:46 +08:00
SendCommandMIX ( CMD_HF_ICLASS_SIMULATE , sim_type , NUM_CSNS , 1 , csns , 8 * NUM_CSNS ) ;
2019-03-10 06:35:06 +08:00
2020-07-20 06:52:46 +08:00
while ( WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) = = false ) {
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 ) ;
2020-08-13 18:25:04 +08:00
2020-07-21 20:09:05 +08:00
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " hf iclass loclass h " ) " ` to recover elite key " ) ;
2019-03-10 06:35:06 +08:00
break ;
}
2020-07-04 03:33:17 +08:00
case ICLASS_SIM_MODE_CSN :
case ICLASS_SIM_MODE_CSN_DEFAULT :
case ICLASS_SIM_MODE_FULL :
2019-03-10 06:35:06 +08:00
default : {
2020-10-17 22:36:58 +08:00
PrintAndLogEx ( INFO , " Starting iCLASS simulation " ) ;
PrintAndLogEx ( INFO , " press " _YELLOW_ ( " `enter` " ) " to cancel " ) ;
2019-06-08 00:41:39 +08:00
uint8_t numberOfCSNs = 0 ;
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2020-08-08 17:46:22 +08:00
SendCommandMIX ( CMD_HF_ICLASS_SIMULATE , sim_type , numberOfCSNs , 1 , CSN , 8 ) ;
2020-08-13 18:25:04 +08:00
2020-07-21 20:09:05 +08:00
if ( sim_type = = ICLASS_SIM_MODE_FULL )
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " hf iclass esave h " ) " ` to save the emulator memory to file " ) ;
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 ) {
2020-07-29 17:02:30 +08:00
return info_iclass ( ) ;
}
int read_iclass_csn ( bool loop , bool verbose ) {
uint32_t flags = ( FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE ) ;
int res = PM3_SUCCESS ;
2020-08-13 18:25:04 +08:00
while ( kbd_enter_pressed ( ) = = false ) {
2020-07-29 17:02:30 +08:00
clearCommandBuffer ( ) ;
SendCommandMIX ( CMD_HF_ICLASS_READER , flags , 0 , 0 , NULL , 0 ) ;
PacketResponseNG resp ;
if ( WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
uint8_t status = resp . oldarg [ 0 ] & 0xff ;
if ( loop = = false ) {
if ( status = = 0 | | status = = 0xFF ) {
if ( verbose ) PrintAndLogEx ( WARNING , " iCLASS / ISO15693 card select failed " ) ;
res = PM3_EOPABORTED ;
break ;
}
} else {
if ( status = = 0xFF )
continue ;
}
picopass_hdr * hdr = ( picopass_hdr * ) resp . data . asBytes ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( SUCCESS , " CSN: " _GREEN_ ( " %s " ) , sprint_hex ( hdr - > csn , sizeof ( hdr - > csn ) ) ) ;
PrintAndLogEx ( SUCCESS , " Config: " _GREEN_ ( " %s " ) , sprint_hex ( ( uint8_t * ) & hdr - > conf , sizeof ( hdr - > conf ) ) ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
if ( loop = = false )
break ;
}
}
2020-08-13 18:25:04 +08:00
DropField ( ) ;
2020-07-29 17:02:30 +08:00
return res ;
2019-09-08 23:37:14 +08:00
}
2019-04-13 00:41:14 +08:00
static int CmdHFiClassReader ( const char * Cmd ) {
2020-08-13 18:25:04 +08:00
2020-10-18 06:15:09 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf iclass reader " ,
" Act as a iCLASS reader. Look for iCLASS tags until Enter or the pm3 button is pressed \n " ,
" hf iclass reader \n "
" hf iclass reader -1 " ) ;
void * argtable [ ] = {
arg_param_begin ,
2020-10-18 06:46:15 +08:00
arg_lit0 ( " 1 " , " one " , " read once " ) ,
2020-10-18 06:15:09 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
2020-10-18 06:46:15 +08:00
bool read_once = arg_get_lit ( ctx , 1 ) ;
2020-10-18 06:15:09 +08:00
CLIParserFree ( ctx ) ;
PrintAndLogEx ( INFO , " Starting iCLASS reader mode " ) ;
PrintAndLogEx ( INFO , " press " _YELLOW_ ( " `enter` " ) " to cancel " ) ;
2020-10-18 06:46:15 +08:00
return read_iclass_csn ( ! read_once , true ) ;
2014-04-15 18:47:01 +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 ) {
2020-06-09 22:25:56 +08:00
return usage_hf_iclass_eload ( ) ;
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 : {
2020-06-10 07:17:49 +08:00
res = loadFileEML_safe ( filename , ( void * * ) & dump , & bytes_read ) ;
2019-08-29 13:47:17 +08:00
break ;
}
case JSON : {
2020-06-02 05:44:41 +08:00
res = loadFileJSON ( filename , dump , 2048 , & bytes_read , NULL ) ;
2019-08-29 13:47:17 +08:00
break ;
}
2020-05-12 05:50:14 +08:00
case DICTIONARY :
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
2020-07-21 20:09:05 +08:00
print_picopass_header ( ( picopass_hdr * ) dump ) ;
print_picopass_info ( ( picopass_hdr * ) dump ) ;
2019-08-29 13:47:17 +08:00
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
}
2020-07-21 20:09:05 +08:00
static int CmdHFiClassESave ( const char * Cmd ) {
char filename [ FILE_PATH_SIZE ] = { 0 } ;
char * fnameptr = filename ;
int len = 0 ;
uint16_t bytes = 256 ;
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_esave ( ) ;
case ' f ' :
len = param_getstr ( Cmd , cmdp + 1 , filename , FILE_PATH_SIZE ) ;
if ( len > = FILE_PATH_SIZE ) {
PrintAndLogEx ( FAILED , " Filename too long " ) ;
errors = true ;
break ;
}
cmdp + = 2 ;
break ;
case ' s ' :
bytes = param_get32ex ( Cmd , cmdp + 1 , 256 , 10 ) ;
if ( bytes > 4096 ) {
PrintAndLogEx ( WARNING , " Emulator memory is max 4096bytes. Truncating %u to 4096 " , bytes ) ;
bytes = 4096 ;
}
cmdp + = 2 ;
break ;
default :
PrintAndLogEx ( WARNING , " Unknown parameter '%c' " , param_getchar ( Cmd , cmdp ) ) ;
errors = true ;
break ;
}
}
//Validations
if ( errors ) {
return usage_hf_iclass_esave ( ) ;
}
uint8_t * dump = calloc ( bytes , sizeof ( uint8_t ) ) ;
2020-08-30 15:55:57 +08:00
if ( dump = = NULL ) {
2020-07-21 20:09:05 +08:00
PrintAndLogEx ( WARNING , " Fail, cannot allocate memory " ) ;
return PM3_EMALLOC ;
}
PrintAndLogEx ( INFO , " downloading from emulator memory " ) ;
if ( ! GetFromDevice ( BIG_BUF_EML , dump , bytes , 0 , NULL , 0 , NULL , 2500 , false ) ) {
PrintAndLogEx ( WARNING , " Fail, transfer from device time-out " ) ;
free ( dump ) ;
return PM3_ETIMEOUT ;
}
// user supplied filename?
if ( len < 1 ) {
2020-08-29 21:22:26 +08:00
fnameptr + = snprintf ( fnameptr , sizeof ( filename ) , " hf-iclass- " ) ;
2020-07-21 20:09:05 +08:00
FillFileNameByUID ( fnameptr , dump , " -dump " , 8 ) ;
}
saveFile ( filename , " .bin " , dump , bytes ) ;
2020-07-29 17:02:30 +08:00
saveFileEML ( filename , dump , bytes , 8 ) ;
2020-07-21 20:09:05 +08:00
saveFileJSON ( filename , jsfIclass , dump , bytes , NULL ) ;
free ( dump ) ;
2020-08-13 18:25:04 +08:00
2020-07-21 20:09:05 +08:00
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " hf iclass readtagfile " ) " ` to view dump file " ) ;
2020-08-13 18:25:04 +08:00
return PM3_SUCCESS ;
2020-07-21 20:09:05 +08:00
}
2020-08-30 15:55:57 +08:00
static int CmdHFiClassEView ( const char * Cmd ) {
uint16_t blocks = 32 , bytes = 256 ;
bool errors = false , verbose = false ;
uint8_t cmdp = 0 ;
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
case ' h ' :
return usage_hf_iclass_eview ( ) ;
case ' s ' :
bytes = param_get32ex ( Cmd , cmdp + 1 , 256 , 10 ) ;
if ( bytes > 4096 ) {
PrintAndLogEx ( WARNING , " Emulator memory is max 4096bytes. Truncating %u to 4096 " , bytes ) ;
bytes = 4096 ;
}
if ( bytes % 8 ! = 0 ) {
bytes & = 0xFFF8 ;
PrintAndLogEx ( WARNING , " Number not divided by 8, truncating to %u " , bytes ) ;
}
blocks = bytes / 8 ;
cmdp + = 2 ;
break ;
case ' v ' :
verbose = true ;
cmdp + + ;
break ;
default :
PrintAndLogEx ( WARNING , " Unknown parameter '%c' " , param_getchar ( Cmd , cmdp ) ) ;
errors = true ;
break ;
}
}
//Validations
if ( errors | | bytes = = 0 ) {
return usage_hf_iclass_eview ( ) ;
}
uint8_t * dump = calloc ( bytes , sizeof ( uint8_t ) ) ;
if ( dump = = NULL ) {
PrintAndLogEx ( WARNING , " Fail, cannot allocate memory " ) ;
return PM3_EMALLOC ;
}
memset ( dump , 0 , bytes ) ;
PrintAndLogEx ( INFO , " downloading from emulator memory " ) ;
if ( ! GetFromDevice ( BIG_BUF_EML , dump , bytes , 0 , NULL , 0 , NULL , 2500 , false ) ) {
PrintAndLogEx ( WARNING , " Fail, transfer from device time-out " ) ;
free ( dump ) ;
return PM3_ETIMEOUT ;
}
2020-09-07 16:35:09 +08:00
2020-08-30 15:55:57 +08:00
if ( verbose ) {
print_picopass_header ( ( picopass_hdr * ) dump ) ;
print_picopass_info ( ( picopass_hdr * ) dump ) ;
}
PrintAndLogEx ( NORMAL , " " ) ;
printIclassDumpContents ( dump , 1 , blocks , bytes ) ;
free ( dump ) ;
return PM3_SUCCESS ;
}
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 ;
2020-07-21 20:09:05 +08:00
bool verbose = 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 ;
2020-07-21 20:09:05 +08:00
case ' v ' :
verbose = true ;
cmdp + + ;
break ;
2019-08-29 20:17:39 +08:00
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-07-21 20:09:05 +08:00
bool use_sc = IsCryptoHelperPresent ( verbose ) ;
2020-02-27 23:35:17 +08:00
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 ) {
2020-07-20 02:45:47 +08:00
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 ;
2020-07-20 02:45:47 +08:00
getMemConfig ( mem , chip , & app_areas , & kb ) ;
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-04-22 08:22:55 +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-07-20 02:45:47 +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 ;
2020-04-16 15:01:14 +08:00
2020-03-12 04:00:19 +08:00
// Decrypted block 7,8,9 if configured.
2020-07-20 02:45:47 +08:00
if ( blocknum > 6 & & blocknum < = 9 & & 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
}
2020-07-20 02:45:47 +08:00
// use the first block (CSN) for filename
2020-03-12 00:08:03 +08:00
char * fptr = calloc ( 50 , sizeof ( uint8_t ) ) ;
2020-07-20 02:45:47 +08:00
if ( fptr = = false ) {
2020-03-12 00:08:03 +08:00
PrintAndLogEx ( WARNING , " Failed to allocate memory " ) ;
free ( decrypted ) ;
return PM3_EMALLOC ;
}
2020-07-20 02:45:47 +08:00
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 ) ;
2020-06-02 05:44:41 +08:00
saveFileJSON ( fptr , jsfIclass , decrypted , decryptedlen , NULL ) ;
2019-08-29 20:17:39 +08:00
2019-09-06 04:39:30 +08:00
printIclassDumpContents ( decrypted , 1 , ( decryptedlen / 8 ) , decryptedlen ) ;
2020-03-11 00:10:05 +08:00
2020-07-20 02:45:47 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
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 ) ;
2020-07-20 02:45:47 +08:00
PrintAndLogEx ( INFO , " Block 7 decoder " ) ;
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
2020-07-20 02:45:47 +08:00
PrintAndLogEx ( SUCCESS , " Binary..................... " _GREEN_ ( " %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 ) ;
2020-07-20 02:45:47 +08:00
2020-02-27 23:35:17 +08:00
} else {
PrintAndLogEx ( INFO , " No credential found. " ) ;
}
2020-08-13 18:25:04 +08:00
2020-07-20 02:45:47 +08:00
// decode block 9
if ( memcmp ( decrypted + ( 8 * 9 ) , empty , 8 ) ! = 0 ) {
uint8_t usr_blk_len = GetNumberBlocksForUserId ( decrypted + ( 8 * 6 ) ) ;
if ( usr_blk_len < 3 ) {
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( INFO , " Block 9 decoder " ) ;
2020-07-20 05:09:21 +08:00
uint8_t pinsize = 0 ;
2020-07-20 02:45:47 +08:00
if ( use_sc ) {
pinsize = GetPinSize ( decrypted + ( 8 * 6 ) ) ;
2020-08-13 18:25:04 +08:00
2020-07-20 05:09:21 +08:00
if ( pinsize > 0 ) {
2020-08-13 18:25:04 +08:00
2020-07-20 05:09:21 +08:00
uint64_t pin = bytes_to_num ( decrypted + ( 8 * 9 ) , 5 ) ;
char tmp [ 17 ] = { 0 } ;
2020-08-29 21:22:26 +08:00
snprintf ( tmp , sizeof ( tmp ) , " %. " PRIu64 , BCD2DEC ( pin ) ) ;
2020-07-20 05:09:21 +08:00
PrintAndLogEx ( INFO , " PIN........................ " _GREEN_ ( " %.*s " ) , pinsize , tmp ) ;
}
2020-07-20 02:45:47 +08:00
}
}
}
PrintAndLogEx ( INFO , " ----------------------------------------------------------------- " ) ;
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
}
2020-07-15 05:12:54 +08:00
static void iclass_encrypt_block_data ( uint8_t * blk_data , uint8_t * key ) {
2019-08-29 13:47:17 +08:00
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 ;
2020-07-21 20:09:05 +08:00
bool verbose = false ;
2019-08-29 13:47:17 +08:00
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 ;
2020-07-21 20:09:05 +08:00
case ' v ' :
verbose = true ;
cmdp + + ;
break ;
2019-08-29 13:47:17 +08:00
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-07-21 20:09:05 +08:00
bool use_sc = IsCryptoHelperPresent ( verbose ) ;
2020-02-27 23:35:17 +08:00
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 {
2020-07-15 05:12:54 +08:00
iclass_encrypt_block_data ( blk_data , key ) ;
2020-02-27 23:35:17 +08:00
}
2020-05-14 23:17:47 +08:00
PrintAndLogEx ( SUCCESS , " encrypted block %s " , sprint_hex ( blk_data , 8 ) ) ;
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2015-10-08 05:00:46 +08:00
}
2020-07-29 17:02:30 +08:00
static bool select_only ( uint8_t * CSN , uint8_t * CCNR , bool verbose ) {
2019-03-09 15:59:13 +08:00
2020-07-13 18:29:31 +08:00
uint8_t flags = ( FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE ) ;
2015-10-08 05:00:46 +08:00
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2020-07-13 18:29:31 +08:00
PacketResponseNG resp ;
2019-08-04 01:17:00 +08:00
SendCommandMIX ( CMD_HF_ICLASS_READER , flags , 0 , 0 , NULL , 0 ) ;
2020-07-10 01:42:34 +08:00
if ( WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) = = false ) {
2019-03-10 06:35:06 +08:00
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
return false ;
}
2015-02-15 04:17:08 +08:00
2020-07-13 18:29:31 +08:00
uint8_t isok = resp . oldarg [ 0 ] & 0xff ;
2015-10-08 05:00:46 +08:00
2020-07-13 18:29:31 +08:00
// no tag found or button pressed
if ( ( isok = = 0 ) | | isok = = 0xFF ) {
2020-07-10 01:42:34 +08:00
if ( verbose ) {
2020-07-13 18:29:31 +08:00
PrintAndLogEx ( FAILED , " failed tag-select, aborting... (%d) " , isok ) ;
2020-07-10 01:42:34 +08:00
}
return false ;
}
2019-03-09 15:59:13 +08:00
2020-07-10 01:42:34 +08:00
picopass_hdr * hdr = ( picopass_hdr * ) resp . data . asBytes ;
2019-03-09 15:59:13 +08:00
2020-07-10 01:42:34 +08:00
if ( CSN ! = NULL )
memcpy ( CSN , hdr - > csn , 8 ) ;
2019-03-09 15:59:13 +08:00
2020-07-10 01:42:34 +08:00
if ( CCNR ! = NULL )
memcpy ( CCNR , hdr - > epurse , 8 ) ;
2019-09-11 22:56:08 +08:00
2020-07-10 01:42:34 +08:00
if ( verbose ) {
PrintAndLogEx ( SUCCESS , " CSN %s " , sprint_hex ( CSN , 8 ) ) ;
PrintAndLogEx ( SUCCESS , " epurse %s " , sprint_hex ( CCNR , 8 ) ) ;
2019-03-10 06:35:06 +08:00
}
return true ;
2015-10-08 05:00:46 +08:00
}
2020-07-13 18:29:31 +08:00
static int CmdHFiClassDump ( const char * Cmd ) {
2015-10-08 05:00:46 +08:00
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 ;
2020-07-20 05:09:21 +08:00
uint8_t app_limit1 = 0 , app_limit2 = 0 ;
2019-03-10 06:35:06 +08:00
uint8_t fileNameLen = 0 ;
char filename [ FILE_PATH_SIZE ] = { 0 } ;
char tempStr [ 50 ] = { 0 } ;
bool have_credit_key = false ;
bool elite = false ;
bool rawkey = false ;
2020-10-13 17:54:38 +08:00
bool use_replay = false ;
2019-03-10 06:35:06 +08:00
bool errors = false ;
2020-07-29 17:02:30 +08:00
bool auth = false ;
2019-03-10 06:35:06 +08:00
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 ' :
2020-07-29 17:02:30 +08:00
auth = true ;
2019-03-10 07:00:59 +08:00
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 ) ;
2020-07-13 18:29:31 +08:00
PrintAndLogEx ( INFO , " AA2 (credit) index %u " , keyNbr ) ;
2019-03-10 07:00:59 +08:00
} 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 ' :
2020-07-29 17:02:30 +08:00
auth = true ;
2019-03-10 07:00:59 +08:00
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 ) ;
2020-07-13 18:29:31 +08:00
PrintAndLogEx ( INFO , " AA1 (debit) index %u " , keyNbr ) ;
2019-03-10 07:00:59 +08:00
} 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 ;
2020-10-13 17:54:38 +08:00
case ' n ' :
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " replay NR/MAC mode " ) ) ;
use_replay = true ;
cmdp + + ;
2020-10-20 07:00:23 +08:00
break ;
2019-03-10 07:00:59 +08:00
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
}
}
2020-10-13 17:54:38 +08:00
2020-10-18 05:09:34 +08:00
if ( ( use_replay + rawkey + elite ) > 1 ) {
2020-10-13 17:54:38 +08:00
PrintAndLogEx ( FAILED , " Can not use a combo of 'e', 'r', 'n' " ) ;
errors = true ;
}
2020-10-20 07:00:23 +08:00
2020-07-29 17:02:30 +08:00
if ( errors ) return usage_hf_iclass_dump ( ) ;
2019-03-10 06:35:06 +08:00
2020-07-29 17:02:30 +08:00
uint32_t flags = ( FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE ) ;
//get CSN and config
2019-04-18 18:43:35 +08:00
PacketResponseNG resp ;
2020-08-08 17:46:22 +08:00
uint8_t tag_data [ 0x100 * 8 ] ;
2020-08-03 23:42:05 +08:00
memset ( tag_data , 0xFF , sizeof ( tag_data ) ) ;
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 ) ;
2020-07-13 18:29:31 +08:00
if ( ! WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
2019-03-10 06:35:06 +08:00
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 ;
2020-07-29 17:02:30 +08:00
picopass_hdr * hdr = ( picopass_hdr * ) 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
}
2020-07-29 17:02:30 +08:00
uint8_t pagemap = get_pagemap ( hdr ) ;
if ( readStatus & ( FLAG_ICLASS_CSN | FLAG_ICLASS_CONF | FLAG_ICLASS_CC ) ) {
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
memcpy ( tag_data , hdr , 24 ) ;
2020-07-20 02:45:47 +08:00
uint8_t type = get_mem_config ( hdr ) ;
2020-07-29 17:02:30 +08:00
// tags configured for NON SECURE PAGE, acts different
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
PrintAndLogEx ( INFO , " Card in non-secure page mode detected " ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
app_limit1 = card_app2_limit [ type ] ;
app_limit2 = 0 ;
} else {
app_limit1 = hdr - > conf . app_limit ;
app_limit2 = card_app2_limit [ type ] ;
}
2020-08-13 18:25:04 +08:00
2020-07-20 02:45:47 +08:00
} else {
PrintAndLogEx ( FAILED , " failed to read block 0,1,2 " ) ;
DropField ( ) ;
2020-08-13 18:25:04 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2020-07-29 17:02:30 +08:00
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
2020-08-13 18:25:04 +08:00
PrintAndLogEx ( INFO , " Dumping all available memory, block 3 - %u (0x%02x) " , app_limit1 , app_limit1 ) ;
2020-07-29 17:02:30 +08:00
if ( auth ) {
PrintAndLogEx ( INFO , " No keys needed, ignoring user supplied key " ) ;
}
} else {
if ( auth = = false ) {
PrintAndLogEx ( FAILED , " Run command with keys " ) ;
return PM3_ESOFT ;
}
PrintAndLogEx ( INFO , " Card has atleast 2 application areas. AA1 limit %u (0x%02X) AA2 limit %u (0x%02X) " , app_limit1 , app_limit1 , app_limit2 , app_limit2 ) ;
2019-03-10 06:35:06 +08:00
}
2020-07-29 17:02:30 +08:00
iclass_dump_req_t payload = {
. req . use_raw = rawkey ,
. req . use_elite = elite ,
. req . use_credit_key = false ,
2020-10-13 17:54:38 +08:00
. req . use_replay = use_replay ,
2020-07-29 17:02:30 +08:00
. req . send_reply = true ,
. req . do_auth = auth ,
. end_block = app_limit1 ,
} ;
memcpy ( payload . req . key , KEY , 8 ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// tags configured for NON SECURE PAGE, acts different
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
payload . start_block = 3 ;
payload . req . do_auth = false ;
} else {
payload . start_block = 6 ;
}
2020-08-13 18:25:04 +08:00
2019-03-10 06:35:06 +08:00
clearCommandBuffer ( ) ;
2020-08-13 18:25:04 +08:00
SendCommandNG ( CMD_HF_ICLASS_DUMP , ( uint8_t * ) & payload , sizeof ( payload ) ) ;
2020-07-13 18:29:31 +08:00
2019-03-10 06:35:06 +08:00
while ( true ) {
2020-08-29 21:22:26 +08:00
PrintAndLogEx ( NORMAL , " . " NOLF ) ;
2019-07-11 19:01:34 +08:00
if ( kbd_enter_pressed ( ) ) {
2020-08-29 21:22:26 +08:00
PrintAndLogEx ( WARNING , " \n aborted via keyboard! \n " ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
2020-03-12 01:38:07 +08:00
return PM3_EOPABORTED ;
2019-03-10 06:35:06 +08:00
}
2020-07-13 18:29:31 +08:00
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_DUMP , & resp , 2000 ) )
2019-03-10 06:35:06 +08:00
break ;
}
2020-07-13 18:29:31 +08:00
2020-08-29 21:22:26 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-07-13 18:29:31 +08:00
if ( resp . status ! = PM3_SUCCESS ) {
PrintAndLogEx ( ERR , " failed to communicate with card " ) ;
return resp . status ;
}
struct p_resp {
bool isOK ;
2020-08-06 14:14:40 +08:00
uint16_t block_cnt ;
2020-07-13 18:29:31 +08:00
uint32_t bb_offset ;
} PACKED ;
struct p_resp * packet = ( struct p_resp * ) resp . data . asBytes ;
if ( packet - > isOK = = false ) {
2020-07-20 06:52:46 +08:00
PrintAndLogEx ( WARNING , " read AA1 blocks failed " ) ;
2019-09-09 05:23:06 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2020-07-29 17:02:30 +08:00
uint32_t startindex = packet - > bb_offset ;
2020-07-20 05:09:21 +08:00
uint32_t blocks_read = packet - > block_cnt ;
2020-07-29 17:02:30 +08:00
2020-08-08 17:46:22 +08:00
uint8_t tempbuf [ 0x100 * 8 ] ;
2020-07-20 05:09:21 +08:00
2019-03-10 06:35:06 +08:00
// response ok - now get bigbuf content of the dump
2020-07-29 17:02:30 +08:00
if ( ! GetFromDevice ( BIG_BUF , tempbuf , sizeof ( tempbuf ) , 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
}
2020-08-13 18:25:04 +08:00
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
2020-07-29 17:02:30 +08:00
// all memory available
2020-08-13 18:25:04 +08:00
memcpy ( tag_data + ( 8 * 3 ) , tempbuf + ( 8 * 3 ) , ( blocks_read * 8 ) ) ;
2020-07-29 17:02:30 +08:00
} else {
// div key KD
memcpy ( tag_data + ( 8 * 3 ) , tempbuf + ( 8 * 3 ) , 8 ) ;
2020-08-03 23:42:05 +08:00
// AIA data
memcpy ( tag_data + ( 8 * 5 ) , tempbuf + ( 8 * 5 ) , 8 ) ;
2020-07-29 17:02:30 +08:00
// AA1 data
2020-08-13 18:25:04 +08:00
memcpy ( tag_data + ( 8 * 6 ) , tempbuf + ( 8 * 6 ) , ( blocks_read * 8 ) ) ;
2020-07-29 17:02:30 +08:00
}
2019-03-10 06:35:06 +08:00
2020-07-29 17:02:30 +08:00
uint16_t bytes_got = ( app_limit1 + 1 ) * 8 ;
2020-08-13 18:25:04 +08:00
2020-07-13 18:29:31 +08:00
// try AA2 Kc, Credit
2020-07-29 17:02:30 +08:00
bool aa2_success = false ;
2019-09-09 05:19:06 +08:00
2020-07-29 17:02:30 +08:00
if ( have_credit_key & & pagemap ! = 0x01 ) {
2019-09-09 05:19:06 +08:00
2020-07-29 17:02:30 +08:00
// AA2 authenticate credit key
memcpy ( payload . req . key , CreditKEY , 8 ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
payload . req . use_credit_key = true ;
payload . start_block = app_limit1 + 1 ;
payload . end_block = app_limit2 ;
payload . req . do_auth = true ;
2020-07-13 18:29:31 +08:00
2020-07-20 02:45:47 +08:00
clearCommandBuffer ( ) ;
2020-08-13 18:25:04 +08:00
SendCommandNG ( CMD_HF_ICLASS_DUMP , ( uint8_t * ) & payload , sizeof ( payload ) ) ;
2020-07-13 18:29:31 +08:00
2020-07-29 17:02:30 +08:00
while ( true ) {
2020-08-29 21:22:26 +08:00
PrintAndLogEx ( NORMAL , " . " NOLF ) ;
2020-07-29 17:02:30 +08:00
if ( kbd_enter_pressed ( ) ) {
2020-08-29 21:22:26 +08:00
PrintAndLogEx ( WARNING , " \n aborted via keyboard! \n " ) ;
2020-07-29 17:02:30 +08:00
DropField ( ) ;
return PM3_EOPABORTED ;
}
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_DUMP , & resp , 2000 ) )
break ;
2020-07-20 02:45:47 +08:00
}
2020-08-29 21:22:26 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-07-20 02:45:47 +08:00
if ( resp . status ! = PM3_SUCCESS ) {
PrintAndLogEx ( ERR , " failed to communicate with card " ) ;
2020-07-29 17:02:30 +08:00
goto write_dump ;
2020-07-20 02:45:47 +08:00
}
2020-07-13 18:29:31 +08:00
2020-07-20 02:45:47 +08:00
packet = ( struct p_resp * ) resp . data . asBytes ;
if ( packet - > isOK = = false ) {
2020-07-29 17:02:30 +08:00
PrintAndLogEx ( WARNING , " failed read block using credit key " ) ;
goto write_dump ;
2020-07-20 02:45:47 +08:00
}
2020-07-29 17:02:30 +08:00
2020-07-20 02:45:47 +08:00
blocks_read = packet - > block_cnt ;
startindex = packet - > bb_offset ;
2020-07-20 05:09:21 +08:00
2020-07-29 17:02:30 +08:00
if ( blocks_read * 8 > sizeof ( tag_data ) - bytes_got ) {
PrintAndLogEx ( WARNING , " data exceeded buffer size! " ) ;
blocks_read = ( sizeof ( tag_data ) - bytes_got ) / 8 ;
2020-07-20 02:45:47 +08:00
}
2019-03-10 06:35:06 +08:00
2020-07-20 02:45:47 +08:00
// get dumped data from bigbuf
2020-07-29 17:02:30 +08:00
if ( ! GetFromDevice ( BIG_BUF , tempbuf , sizeof ( tempbuf ) , startindex , NULL , 0 , NULL , 2500 , false ) ) {
2020-07-20 02:45:47 +08:00
PrintAndLogEx ( WARNING , " command execution time out " ) ;
2020-07-29 17:02:30 +08:00
goto write_dump ;
2019-03-10 06:35:06 +08:00
}
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// div key KC
memcpy ( tag_data + ( 8 * 4 ) , tempbuf + ( 8 * 4 ) , 8 ) ;
2020-07-20 02:45:47 +08:00
2020-07-29 17:02:30 +08:00
// AA2 data
2020-08-13 18:25:04 +08:00
memcpy ( tag_data + ( 8 * ( app_limit1 + 1 ) ) , tempbuf + ( 8 * ( app_limit1 + 1 ) ) , ( blocks_read * 8 ) ) ;
2019-09-13 22:31:17 +08:00
2020-07-29 17:02:30 +08:00
bytes_got = ( blocks_read * 8 ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
aa2_success = true ;
}
2019-09-09 05:19:06 +08:00
2020-07-29 17:02:30 +08:00
write_dump :
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
if ( have_credit_key & & pagemap ! = 0x01 & & aa2_success = = false )
PrintAndLogEx ( INFO , " Reading AA2 failed. dumping AA1 data to file " ) ;
2019-03-10 06:35:06 +08:00
// print the dump
2020-07-29 17:02:30 +08:00
printIclassDumpContents ( tag_data , 1 , ( bytes_got / 8 ) , bytes_got ) ;
2019-03-10 06:35:06 +08:00
2020-07-29 17:02:30 +08:00
// use CSN as filename
2019-03-10 07:00:59 +08:00
if ( filename [ 0 ] = = 0 ) {
2019-08-29 20:33:09 +08:00
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
2020-08-13 20:04:09 +08:00
PrintAndLogEx ( SUCCESS , " saving dump file - %u blocks read " , bytes_got / 8 ) ;
2020-07-29 17:02:30 +08:00
saveFile ( filename , " .bin " , tag_data , bytes_got ) ;
saveFileEML ( filename , tag_data , bytes_got , 8 ) ;
saveFileJSON ( filename , jsfIclass , tag_data , bytes_got , NULL ) ;
2020-08-13 18:25:04 +08:00
2020-07-21 20:09:05 +08:00
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " hf iclass decrypt " ) " ` to decrypt dump file " ) ;
2020-07-29 17:02:30 +08:00
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " hf iclass view " ) " ` to view dump file " ) ;
2020-07-29 21:28:42 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
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
2020-10-13 17:54:38 +08:00
static int iclass_write_block ( uint8_t blockno , uint8_t * bldata , uint8_t * KEY , bool use_credit_key , bool elite , bool rawkey , bool replay , bool verbose ) {
2020-10-20 07:00:23 +08:00
2020-08-13 18:25:04 +08:00
iclass_writeblock_req_t payload = {
2020-07-29 17:02:30 +08:00
. req . use_raw = rawkey ,
. req . use_elite = elite ,
. req . use_credit_key = use_credit_key ,
2020-10-13 17:54:38 +08:00
. req . use_replay = replay ,
2020-07-29 17:02:30 +08:00
. req . blockno = blockno ,
. req . send_reply = true ,
2020-08-13 18:25:04 +08:00
. req . do_auth = true ,
2020-07-29 17:02:30 +08:00
} ;
memcpy ( payload . req . key , KEY , 8 ) ;
memcpy ( payload . data , bldata , sizeof ( payload . data ) ) ;
2020-08-13 18:25:04 +08:00
2020-07-14 02:13:25 +08:00
clearCommandBuffer ( ) ;
SendCommandNG ( CMD_HF_ICLASS_WRITEBL , ( uint8_t * ) & payload , sizeof ( payload ) ) ;
PacketResponseNG resp ;
2019-09-09 03:21:30 +08:00
2020-07-14 02:13:25 +08:00
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_WRITEBL , & resp , 2000 ) = = 0 ) {
if ( verbose ) PrintAndLogEx ( WARNING , " Command execute timeout " ) ;
return PM3_ETIMEOUT ;
}
2019-09-09 03:21:30 +08:00
2020-07-14 02:13:25 +08:00
if ( resp . status ! = PM3_SUCCESS ) {
if ( verbose ) PrintAndLogEx ( ERR , " failed to communicate with card " ) ;
2020-10-14 04:43:28 +08:00
return resp . status ;
2019-03-10 06:35:06 +08:00
}
2020-07-14 02:13:25 +08:00
return ( resp . data . asBytes [ 0 ] = = 1 ) ? PM3_SUCCESS : PM3_ESOFT ;
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 ;
2020-10-13 17:54:38 +08:00
bool use_replay = false ;
2019-03-10 06:35:06 +08:00
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 ;
2020-10-20 07:00:23 +08:00
/*
case ' n ' :
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " replay NR/MAC mode " ) ) ;
use_replay = true ;
cmdp + + ;
break ;
*/
2019-03-10 07:00:59 +08:00
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
2020-10-18 05:09:34 +08:00
if ( ( use_replay + rawkey + elite ) > 1 ) {
2020-10-13 17:54:38 +08:00
PrintAndLogEx ( FAILED , " Can not use a combo of 'e', 'r', 'n' " ) ;
errors = true ;
}
2019-03-10 06:35:06 +08:00
if ( errors | | cmdp < 6 ) return usage_hf_iclass_writeblock ( ) ;
2020-10-13 17:54:38 +08:00
int isok = iclass_write_block ( blockno , bldata , KEY , use_credit_key , elite , rawkey , use_replay , verbose ) ;
2020-10-20 07:00:23 +08:00
switch ( isok ) {
2020-10-14 04:43:28 +08:00
case PM3_SUCCESS :
PrintAndLogEx ( SUCCESS , " Wrote block %02X successful " , blockno ) ;
break ;
case PM3_ETEAROFF :
if ( verbose )
PrintAndLogEx ( INFO , " Writing tear off triggered " ) ;
break ;
default :
PrintAndLogEx ( FAILED , " Writing failed " ) ;
break ;
}
2020-07-14 02:13:25 +08:00
return isok ;
2015-10-08 05:00:46 +08:00
}
2020-08-03 23:42:05 +08:00
static int CmdHFiClassRestore ( 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 ;
2020-06-09 22:25:56 +08:00
2019-03-10 06:35:06 +08:00
while ( param_getchar ( Cmd , cmdp ) ! = 0x00 & & ! errors ) {
switch ( tolower ( param_getchar ( Cmd , cmdp ) ) ) {
2019-03-10 07:00:59 +08:00
case ' h ' :
2020-08-15 14:49:39 +08:00
return usage_hf_iclass_restore ( ) ;
2019-03-10 07:00:59 +08:00
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 ) {
2020-10-14 23:41:34 +08:00
PrintAndLogEx ( SUCCESS , " Using key[%d] " _GREEN_ ( " %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
2020-08-15 14:49:39 +08:00
if ( errors | | cmdp < 8 ) return usage_hf_iclass_restore ( ) ;
2019-03-10 06:35:06 +08:00
2020-10-14 23:41:34 +08:00
if ( rawkey + elite > 1 ) {
PrintAndLogEx ( FAILED , " Can not use both 'e', 'r' " ) ;
return PM3_EINVARG ;
}
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 " ) ;
2020-09-07 16:35:09 +08:00
return PM3_EINVARG ;
2019-09-09 05:19:06 +08:00
}
2019-03-10 06:35:06 +08:00
2020-10-14 23:41:34 +08:00
uint32_t payload_size = sizeof ( iclass_restore_req_t ) + ( sizeof ( iclass_restore_item_t ) * ( endblock - startblock + 1 ) ) ;
2019-09-13 22:31:17 +08:00
2020-10-14 23:41:34 +08:00
if ( payload_size > PM3_CMD_DATA_SIZE ) {
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
2020-06-09 22:25:56 +08:00
uint8_t * dump = NULL ;
size_t bytes_read = 0 ;
2020-06-10 07:17:49 +08:00
if ( loadFile_safe ( filename , " " , ( void * * ) & dump , & bytes_read ) ! = PM3_SUCCESS ) {
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
}
2020-06-09 22:25:56 +08:00
if ( bytes_read = = 0 ) {
PrintAndLogEx ( ERR , " file reading error " ) ;
2020-06-09 23:01:31 +08:00
free ( dump ) ;
2020-06-09 22:25:56 +08:00
return PM3_EFILE ;
}
2020-06-09 23:01:31 +08:00
2020-10-20 07:00:23 +08:00
if ( bytes_read < ( ( endblock - startblock + 1 ) * 8 ) ) {
2020-10-14 23:41:34 +08:00
PrintAndLogEx ( ERR , " file is smaller than your suggested block range ( " _RED_ ( " 0x%02x..0x%02x " ) " ) " ,
2020-10-20 07:00:23 +08:00
startblock , endblock
) ;
2020-06-09 23:01:31 +08:00
free ( dump ) ;
2020-06-09 22:25:56 +08:00
return PM3_EFILE ;
}
2019-09-09 05:19:06 +08:00
2020-10-14 23:41:34 +08:00
iclass_restore_req_t * payload = calloc ( 1 , payload_size ) ;
payload - > req . use_raw = rawkey ,
2020-10-20 07:00:23 +08:00
payload - > req . use_elite = elite ,
payload - > req . use_credit_key = use_credit_key ,
payload - > req . use_replay = false ,
payload - > req . blockno = startblock ,
payload - > req . send_reply = true ,
payload - > req . do_auth = true ,
memcpy ( payload - > req . key , KEY , 8 ) ;
2020-10-14 23:41:34 +08:00
payload - > item_cnt = ( endblock - startblock + 1 ) ;
2019-09-09 05:19:06 +08:00
// 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
2020-06-10 07:17:49 +08:00
2020-10-20 07:00:23 +08:00
for ( uint8_t i = 0 ; i < payload - > item_cnt ; i + + ) {
2020-10-14 23:41:34 +08:00
payload - > blocks [ i ] . blockno = startblock + i ;
2020-10-20 07:00:23 +08:00
memcpy ( payload - > blocks [ i ] . data , dump + ( startblock * 8 ) + ( i * 8 ) , sizeof ( payload - > blocks [ i ] . data ) ) ;
2019-03-14 17:01:07 +08:00
}
2019-03-10 06:35:06 +08:00
2020-10-14 23:41:34 +08:00
free ( dump ) ;
2019-09-09 05:19:06 +08:00
2019-09-13 22:31:17 +08:00
if ( verbose ) {
2020-10-16 22:07:39 +08:00
PrintAndLogEx ( INFO , " Preparing to restore block range 0x%02x..0x%02x " , startblock , endblock ) ;
2020-10-20 07:00:23 +08:00
2020-10-14 23:41:34 +08:00
PrintAndLogEx ( INFO , " ------+---------------------- " ) ;
PrintAndLogEx ( INFO , " block | data " ) ;
PrintAndLogEx ( INFO , " ------+---------------------- " ) ;
2020-10-20 07:00:23 +08:00
for ( uint8_t i = 0 ; i < payload - > item_cnt ; i + + ) {
2020-10-14 23:41:34 +08:00
iclass_restore_item_t item = payload - > blocks [ i ] ;
PrintAndLogEx ( INFO , " %02X | %s " , item . blockno , sprint_hex_inrow ( item . data , sizeof ( item . data ) ) ) ;
2019-09-09 05:19:06 +08:00
}
2019-03-10 06:35:06 +08:00
}
2019-03-14 19:30:32 +08:00
2020-10-14 23:41:34 +08:00
PrintAndLogEx ( INFO , " restore started... " ) ;
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 ( ) ;
2020-10-14 23:41:34 +08:00
SendCommandNG ( CMD_HF_ICLASS_RESTORE , ( uint8_t * ) payload , payload_size ) ;
2019-09-09 05:19:06 +08:00
2020-10-14 23:41:34 +08:00
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_RESTORE , & resp , 2500 ) = = 0 ) {
2019-03-14 19:30:32 +08:00
PrintAndLogEx ( WARNING , " command execute timeout " ) ;
2019-09-09 05:19:06 +08:00
DropField ( ) ;
2020-10-16 22:07:39 +08:00
free ( payload ) ;
2019-09-09 05:19:06 +08:00
return PM3_ETIMEOUT ;
2019-03-10 06:35:06 +08:00
}
2019-09-13 22:31:17 +08:00
if ( resp . status = = PM3_SUCCESS ) {
2020-10-14 23:41:34 +08:00
PrintAndLogEx ( SUCCESS , " iCLASS restore " _GREEN_ ( " successful " ) ) ;
PrintAndLogEx ( HINT , " Try ` " _YELLOW_ ( " hf iclass rdbl " ) " ` to verify data on card " ) ;
} else {
PrintAndLogEx ( WARNING , " iCLASS restore " _RED_ ( " failed " ) ) ;
2019-09-09 05:19:06 +08:00
}
2020-10-16 22:07:39 +08:00
free ( payload ) ;
2019-09-09 05:19:06 +08:00
return resp . status ;
2015-10-08 05:00:46 +08:00
}
2020-10-13 17:54:38 +08:00
static int iclass_read_block ( uint8_t * KEY , uint8_t blockno , uint8_t keyType , bool elite , bool rawkey , bool replay , bool verbose , bool auth , uint8_t * out ) {
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
iclass_auth_req_t payload = {
. use_raw = rawkey ,
. use_elite = elite ,
. use_credit_key = ( keyType = = 0x18 ) ,
2020-10-13 17:54:38 +08:00
. use_replay = replay ,
2020-07-29 17:02:30 +08:00
. blockno = blockno ,
. send_reply = true ,
2020-08-13 18:25:04 +08:00
. do_auth = auth ,
2020-07-29 17:02:30 +08:00
} ;
memcpy ( payload . key , KEY , 8 ) ;
2020-08-13 18:25:04 +08:00
2020-07-14 02:58:24 +08:00
PacketResponseNG resp ;
2020-08-13 18:25:04 +08:00
clearCommandBuffer ( ) ;
SendCommandNG ( CMD_HF_ICLASS_READBL , ( uint8_t * ) & payload , sizeof ( payload ) ) ;
2019-09-08 23:37:14 +08:00
2020-07-20 02:45:47 +08:00
if ( WaitForResponseTimeout ( CMD_HF_ICLASS_READBL , & resp , 2000 ) = = false ) {
2020-07-14 02:58:24 +08:00
if ( verbose ) PrintAndLogEx ( WARNING , " Command execute timeout " ) ;
return PM3_ETIMEOUT ;
}
2020-08-13 18:25:04 +08:00
2020-07-14 02:58:24 +08:00
if ( resp . status ! = PM3_SUCCESS ) {
if ( verbose ) PrintAndLogEx ( ERR , " failed to communicate with card " ) ;
return PM3_EWRONGANSWER ;
}
2019-09-09 03:21:30 +08:00
2020-07-20 02:45:47 +08:00
// return data.
2020-07-29 17:02:30 +08:00
iclass_readblock_resp_t * packet = ( iclass_readblock_resp_t * ) resp . data . asBytes ;
2020-07-14 02:58:24 +08:00
2020-07-29 17:02:30 +08:00
if ( packet - > isOK = = false ) {
if ( verbose ) PrintAndLogEx ( FAILED , " authentication error " ) ;
2020-07-20 02:45:47 +08:00
return PM3_ESOFT ;
}
2019-09-12 04:51:13 +08:00
2020-07-21 19:00:52 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-07-29 17:02:30 +08:00
PrintAndLogEx ( SUCCESS , " block %02X : " _GREEN_ ( " %s " ) , blockno , sprint_hex ( packet - > data , sizeof ( packet - > data ) ) ) ;
2020-07-21 19:00:52 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-07-14 06:16:25 +08:00
2020-07-29 17:02:30 +08:00
if ( out )
memcpy ( out , packet - > data , sizeof ( packet - > data ) ) ;
2020-07-14 02:13:25 +08:00
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 } ;
2020-07-21 19:00:52 +08:00
uint8_t key_idx = 0 ;
uint8_t key_len = 0 ;
2019-03-10 06:35:06 +08:00
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 ;
2020-10-13 17:54:38 +08:00
bool use_replay = false ;
2019-03-10 06:35:06 +08:00
bool errors = false ;
bool auth = false ;
bool verbose = false ;
uint8_t cmdp = 0 ;
2020-07-20 02:45:47 +08:00
2019-03-10 06:35:06 +08:00
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 ' :
2020-07-21 19:00:52 +08:00
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " KC 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 ;
2020-07-21 19:00:52 +08:00
key_len = param_getstr ( Cmd , cmdp + 1 , tempStr , sizeof ( tempStr ) ) ;
if ( key_len = = 16 ) {
errors = param_gethex ( tempStr , 0 , KEY , key_len ) ;
} else if ( key_len = = 1 ) {
key_idx = param_get8 ( Cmd , cmdp + 1 ) ;
if ( key_idx < ICLASS_KEYS_MAX ) {
memcpy ( KEY , iClass_Key_Table [ key_idx ] , 8 ) ;
2019-03-10 07:00:59 +08:00
} else {
2020-07-21 19:00:52 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: key index is invalid \n " ) ;
2019-03-10 07:00:59 +08:00
errors = true ;
}
2019-03-10 06:35:06 +08:00
} else {
2020-07-21 19:00:52 +08:00
PrintAndLogEx ( WARNING , " \n ERROR: incorrect key 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 ;
2020-10-13 17:54:38 +08:00
case ' n ' :
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " replay NR/MAC mode " ) ) ;
use_replay = true ;
cmdp + + ;
break ;
2019-03-10 07:00:59 +08:00
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 ;
2020-10-20 07:00:23 +08:00
2020-10-18 05:09:34 +08:00
if ( ( use_replay + rawkey + elite ) > 1 ) {
2020-10-13 17:54:38 +08:00
PrintAndLogEx ( FAILED , " Can not use a combo of 'e', 'r', 'n' " ) ;
errors = true ;
}
2019-09-13 22:31:17 +08:00
2020-07-20 02:45:47 +08:00
if ( errors ) return usage_hf_iclass_readblock ( ) ;
2019-03-10 06:35:06 +08:00
2020-07-21 19:00:52 +08:00
if ( verbose ) {
if ( key_len = = 1 )
PrintAndLogEx ( SUCCESS , " Using key[%d] %s " , key_idx , sprint_hex ( KEY , 8 ) ) ;
else
PrintAndLogEx ( SUCCESS , " Using key %s " , sprint_hex ( KEY , 8 ) ) ;
}
2020-07-29 17:02:30 +08:00
if ( auth = = false & & verbose ) {
PrintAndLogEx ( WARNING , " warning: no authentication used with read. Typical for cards configured toin `non-secure page` " ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
}
uint8_t data [ 8 ] = { 0 } ;
2020-10-13 17:54:38 +08:00
int res = iclass_read_block ( KEY , blockno , keyType , elite , rawkey , use_replay , verbose , auth , data ) ;
2020-07-29 17:02:30 +08:00
if ( res ! = PM3_SUCCESS )
return res ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
if ( blockno < 6 | | blockno > 7 )
return PM3_SUCCESS ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
if ( memcmp ( data , empty , 8 ) = = 0 )
return PM3_SUCCESS ;
bool use_sc = IsCryptoHelperPresent ( verbose ) ;
2020-08-13 18:25:04 +08:00
if ( use_sc = = false )
2020-07-29 17:02:30 +08:00
return PM3_SUCCESS ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// crypto helper available.
PrintAndLogEx ( INFO , " ----------------------------- " _CYAN_ ( " cardhelper " ) " ----------------------------- " ) ;
switch ( blockno ) {
case 6 : {
DecodeBlock6 ( data ) ;
break ;
}
case 7 : {
uint8_t dec_data [ 8 ] ;
uint64_t a = bytes_to_num ( data , 8 ) ;
bool starts = ( leadingzeros ( a ) < 12 ) ;
bool ones = ( countones ( a ) > 16 & & countones ( a ) < 48 ) ;
if ( starts & & ones ) {
PrintAndLogEx ( INFO , " data looks encrypted, False Positives " _YELLOW_ ( " ARE " ) " possible " ) ;
Decrypt ( data , dec_data ) ;
PrintAndLogEx ( SUCCESS , " decrypted : " _GREEN_ ( " %s " ) , sprint_hex ( dec_data , sizeof ( dec_data ) ) ) ;
} else {
memcpy ( dec_data , data , sizeof ( dec_data ) ) ;
PrintAndLogEx ( INFO , " data looks unencrypted, trying to decode " ) ;
}
if ( memcmp ( dec_data , empty , 8 ) ! = 0 ) {
//todo: remove preamble/sentinal
uint32_t top = 0 , mid , bot ;
mid = bytes_to_num ( dec_data , 4 ) ;
bot = bytes_to_num ( dec_data + 4 , 4 ) ;
2020-07-21 19:00:52 +08:00
2020-07-29 17:02:30 +08:00
char hexstr [ 16 + 1 ] = { 0 } ;
hex_to_buffer ( ( uint8_t * ) hexstr , dec_data , 8 , sizeof ( hexstr ) - 1 , 0 , 0 , true ) ;
char binstr [ 64 + 1 ] = { 0 } ;
hextobinstring ( binstr , hexstr ) ;
uint8_t i = 0 ;
while ( i < strlen ( binstr ) & & binstr [ i + + ] = = ' 0 ' ) ;
2019-08-24 11:08:03 +08:00
2020-07-29 17:02:30 +08:00
i & = 0x3C ;
PrintAndLogEx ( SUCCESS , " bin : %s " , binstr + i ) ;
2020-08-13 18:25:04 +08:00
PrintAndLogEx ( INFO , " " ) ;
2020-07-29 17:02:30 +08:00
PrintAndLogEx ( INFO , " ------------------------------ " _CYAN_ ( " wiegand " ) " ------------------------------- " ) ;
wiegand_message_t packed = initialize_message_object ( top , mid , bot ) ;
HIDTryUnpack ( & packed , true ) ;
} else {
PrintAndLogEx ( INFO , " no credential found " ) ;
}
break ;
}
}
PrintAndLogEx ( INFO , " ---------------------------------------------------------------------- " ) ;
return PM3_SUCCESS ;
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 ) ;
2020-08-08 17:46:22 +08:00
} else {
PrintAndLogEx ( WARNING , " You must specify a filename " ) ;
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 ) ) ;
2020-05-15 23:03:28 +08:00
2019-03-10 06:35:06 +08:00
int errors = testCipherUtils ( ) ;
errors + = testMAC ( ) ;
2020-05-15 23:03:28 +08:00
errors + = doKeyTests ( ) ;
2019-10-13 06:48:26 +08:00
errors + = testElite ( opt2 = = ' l ' ) ;
2020-05-15 23:03:28 +08:00
if ( errors ! = PM3_SUCCESS )
PrintAndLogEx ( ERR , " There were errors!!! " ) ;
2019-08-29 21:55:52 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2020-07-15 17:35:03 +08:00
return usage_hf_iclass_loclass ( ) ;
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-08-06 14:14:40 +08:00
picopass_hdr * hdr = ( picopass_hdr * ) iclass_dump ;
// picopass_ns_hdr *ns_hdr = (picopass_ns_hdr *)iclass_dump;
// uint8_t pagemap = get_pagemap(hdr);
2020-08-13 18:25:04 +08:00
// if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { }
2020-08-06 14:14:40 +08:00
uint8_t lock = hdr - > conf . block_writelock ;
// is chip in ReadOnly (RO)
bool ro = ( ( lock & 0x80 ) = = 0 ) ;
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
) ;
*/
2020-10-18 17:50:20 +08:00
uint8_t pagemap = get_pagemap ( hdr ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
int i = startblock ;
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( INFO , " blk| data | ascii |lck| info " ) ;
PrintAndLogEx ( INFO , " ----+-------------------------+----------+---+-------------- " ) ;
PrintAndLogEx ( INFO , " 0x00| " _GREEN_ ( " %s " ) " | | CSN " , sprint_hex_ascii ( iclass_dump , 8 ) ) ;
2020-10-20 07:00:23 +08:00
2020-10-18 17:50:20 +08:00
if ( i ! = 1 )
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-08-13 18:25:04 +08:00
2020-08-06 14:14:40 +08:00
bool bl_lock = false ;
2020-08-13 18:25:04 +08:00
if ( ro = = false ) {
switch ( i ) {
case 12 : {
2020-08-06 14:14:40 +08:00
bl_lock = ( ( lock & 0x40 ) = = 0 ) ;
break ;
}
case 11 : {
bl_lock = ( ( lock & 0x20 ) = = 0 ) ;
break ;
}
case 10 : {
bl_lock = ( ( lock & 0x10 ) = = 0 ) ;
break ;
}
case 9 : {
bl_lock = ( ( lock & 0x08 ) = = 0 ) ;
break ;
}
case 8 : {
bl_lock = ( ( lock & 0x04 ) = = 0 ) ;
break ;
}
case 7 : {
bl_lock = ( ( lock & 0x02 ) = = 0 ) ;
break ;
}
case 6 : {
bl_lock = ( ( lock & 0x01 ) = = 0 ) ;
break ;
}
}
} else {
bl_lock = true ;
}
2020-08-13 18:25:04 +08:00
2020-10-18 17:50:20 +08:00
const char * lockstr = ( bl_lock ) ? _RED_ ( " x " ) : " " ;
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
const char * info_nonks [ ] = { " CSN " , " Config " , " AIA " , " User " } ;
const char * s = info_nonks [ 3 ] ;
if ( i < 3 ) {
s = info_nonks [ i ] ;
2020-10-20 07:00:23 +08:00
}
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " 0x%02X| %s | %s | %s " , i , sprint_hex_ascii ( blk , 8 ) , lockstr , s ) ;
} else {
const char * info_ks [ ] = { " CSN " , " Config " , " E-purse " , " Debit " , " Credit " , " AIA " , " User " } ;
const char * s = info_ks [ 6 ] ;
if ( i < 6 ) {
s = info_ks [ i ] ;
}
PrintAndLogEx ( INFO , " 0x%02X| %s | %s | %s " , i , sprint_hex_ascii ( blk , 8 ) , lockstr , s ) ;
}
2019-03-10 06:35:06 +08:00
i + + ;
}
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " ----+-------------------------+----------+---+-------------- " ) ;
PrintAndLogEx ( NORMAL , " " ) ;
2015-10-08 05:00:46 +08:00
}
2020-07-29 17:02:30 +08:00
static int CmdHFiClassView ( const char * Cmd ) {
2020-10-05 03:23:48 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf iclass view " ,
" Print a iCLASS tag dump file " ,
" hf iclass view -f hf-iclass-AA162D30F8FF12F1-dump.bin \n "
" hf iclass view --startblock 1 --file hf-iclass-AA162D30F8FF12F1-dump.bin \n " ) ;
void * argtable [ ] = {
arg_param_begin ,
2020-10-06 21:03:24 +08:00
arg_str1 ( " f " , " file " , " <filename> " , " filename of dump " ) ,
2020-10-07 00:00:00 +08:00
arg_int0 ( " s " , " startblock " , " <dec> " , " print from this block (default block6) " ) ,
2020-10-06 21:03:24 +08:00
arg_int0 ( " e " , " endblock " , " <dec> " , " end printing at this block (default 0, ALL) " ) ,
arg_lit0 ( " v " , " verbose " , " verbose output " ) ,
2020-10-05 03:23:48 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
int fnlen = 0 ;
2019-03-10 06:35:06 +08:00
char filename [ FILE_PATH_SIZE ] ;
2020-10-07 00:00:00 +08:00
CLIParamStrToBuf ( arg_get_str ( ctx , 1 ) , ( uint8_t * ) filename , FILE_PATH_SIZE , & fnlen ) ;
2020-10-05 03:23:48 +08:00
int startblock = arg_get_int_def ( ctx , 2 , 0 ) ;
int endblock = arg_get_int_def ( ctx , 3 , 0 ) ;
bool verbose = arg_get_lit ( ctx , 4 ) ;
2019-03-10 06:35:06 +08:00
2020-10-07 00:00:00 +08:00
CLIParserFree ( ctx ) ;
2019-03-10 06:35:06 +08:00
2020-06-09 22:25:56 +08:00
uint8_t * dump = NULL ;
size_t bytes_read = 0 ;
2020-06-10 07:17:49 +08:00
if ( loadFile_safe ( filename , " " , ( void * * ) & dump , & bytes_read ) ! = PM3_SUCCESS ) {
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
}
2020-04-16 15:01:14 +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 " ) ;
2020-04-22 08:22:55 +08:00
PrintAndLogEx ( INFO , " start " _YELLOW_ ( " 0x%02x " ) " end " _YELLOW_ ( " 0x%02x " ) , ( startblock = = 0 ) ? 6 : startblock , endblock ) ;
2020-04-03 15:11:12 +08:00
}
2020-08-13 18:25:04 +08:00
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-07-21 20:09:05 +08:00
print_picopass_header ( ( picopass_hdr * ) dump ) ;
print_picopass_info ( ( picopass_hdr * ) dump ) ;
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 + + )
2020-05-15 23:03:28 +08:00
key_sel [ i ] = keytable [ key_index [ i ] ] ;
2019-03-10 06:35:06 +08:00
//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 ;
2020-08-13 18:25:04 +08:00
2019-03-10 06:35:06 +08:00
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 ( ) ;
2020-07-13 18:29:31 +08:00
if ( givenCSN = = false ) {
2019-06-08 00:41:39 +08:00
uint8_t CCNR [ 12 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2020-07-29 17:02:30 +08:00
if ( select_only ( CSN , CCNR , true ) = = false ) {
2019-09-11 22:56:08 +08:00
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 ) ;
2020-08-13 18:25:04 +08:00
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
2020-06-09 22:25:56 +08:00
uint8_t * dump = NULL ;
size_t bytes_read = 0 ;
2020-06-10 07:17:49 +08:00
if ( loadFile_safe ( filename , " " , ( void * * ) & dump , & bytes_read ) ! = PM3_SUCCESS ) {
2020-06-09 22:25:56 +08:00
PrintAndLogEx ( FAILED , " File: " _YELLOW_ ( " %s " ) " : not found or locked. " , filename ) ;
2020-04-03 13:22:54 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
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 ) ;
2020-04-22 08:22:55 +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 , " " ) ;
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " idx| key " ) ;
PrintAndLogEx ( INFO , " ---+------------------------ " ) ;
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 )
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " %u | " , i ) ;
2019-08-30 16:45:52 +08:00
else
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " %u | " _YELLOW_ ( " %s " ) , i , sprint_hex ( iClass_Key_Table [ i ] , 8 ) ) ;
2019-08-24 11:08:03 +08:00
}
2020-10-18 17:50:20 +08:00
PrintAndLogEx ( INFO , " ---+------------------------ " ) ;
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
}
2020-08-03 23:42:05 +08:00
static void add_key ( uint8_t * key ) {
uint8_t i ;
for ( i = 0 ; i < ICLASS_KEYS_MAX ; i + + ) {
if ( memcmp ( iClass_Key_Table [ i ] , key , 8 ) = = 0 ) {
PrintAndLogEx ( SUCCESS , " Key already at keyslot " _GREEN_ ( " %d " ) , i ) ;
break ;
}
if ( memcmp ( iClass_Key_Table [ i ] , " \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 " , 8 ) = = 0 ) {
memcpy ( iClass_Key_Table [ i ] , key , 8 ) ;
PrintAndLogEx ( SUCCESS , " Added key to keyslot " _GREEN_ ( " %d " ) , i ) ;
break ;
}
}
if ( i = = ICLASS_KEYS_MAX ) {
PrintAndLogEx ( INFO , " Couldn't find an empty keyslot " ) ;
} else {
PrintAndLogEx ( HINT , " Try " _YELLOW_ ( " `hf iclass managekeys p` " ) " to view keys " ) ;
}
}
2020-10-18 17:50:20 +08:00
2020-10-18 05:09:34 +08:00
/*
2020-10-18 17:50:20 +08:00
static int iclass_chk_keys ( uint8_t * keyBlock , uint32_t keycount ) {
iclass_premac_t * pre = calloc ( keycount , sizeof ( iclass_premac_t ) ) ;
if ( pre = = NULL ) {
return PM3_EMALLOC ;
}
// max 42 keys inside USB_COMMAND. 512/4 = 103 mac
uint32_t chunksize = keycount > ( PM3_CMD_DATA_SIZE / 4 ) ? ( PM3_CMD_DATA_SIZE / 4 ) : keycount ;
bool lastChunk = false ;
// fast push mode
conn . block_after_ACK = true ;
// keep track of position of found key
uint8_t found_offset = 0 ;
uint32_t key_offset = 0 ;
// main keychunk loop
for ( key_offset = 0 ; key_offset < keycount ; key_offset + = chunksize ) {
uint64_t t2 = msclock ( ) ;
uint8_t timeout = 0 ;
if ( kbd_enter_pressed ( ) ) {
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( WARNING , " Aborted via keyboard! " ) ;
goto out ;
}
uint32_t keys = ( ( keycount - key_offset ) > chunksize ) ? chunksize : keycount - key_offset ;
// last chunk?
if ( keys = = keycount - key_offset ) {
lastChunk = true ;
// Disable fast mode on last command
conn . block_after_ACK = false ;
}
uint32_t flags = lastChunk < < 8 ;
// bit 16
// - 1 indicates credit key
// - 0 indicates debit key (default)
flags | = ( use_credit_key < < 16 ) ;
clearCommandBuffer ( ) ;
SendCommandOLD ( CMD_HF_ICLASS_CHKKEYS , flags , keys , 0 , pre + key_offset , 4 * keys ) ;
PacketResponseNG resp ;
bool looped = false ;
while ( ! WaitForResponseTimeout ( CMD_HF_ICLASS_CHKKEYS , & resp , 2000 ) ) {
timeout + + ;
PrintAndLogEx ( NORMAL , " . " NOLF ) ;
if ( timeout > 120 ) {
PrintAndLogEx ( WARNING , " \n No response from Proxmark3. Aborting... " ) ;
goto out ;
}
looped = true ;
}
if ( looped )
PrintAndLogEx ( NORMAL , " " ) ;
found_offset = resp . oldarg [ 1 ] & 0xFF ;
uint8_t isOK = resp . oldarg [ 0 ] & 0xFF ;
t2 = msclock ( ) - t2 ;
switch ( isOK ) {
case 1 : {
found_key = true ;
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( SUCCESS , " Found valid key " _GREEN_ ( " %s " )
, sprint_hex ( keyBlock + ( key_offset + found_offset ) * 8 , 8 )
) ;
break ;
}
case 0 : {
PrintAndLogEx ( INPLACE , " Chunk [%d/%d] " , key_offset , keycount ) ;
break ;
}
case 99 : {
}
default : {
break ;
}
}
// both keys found.
if ( found_key ) {
break ;
}
}
2020-10-18 05:09:34 +08:00
return PM3_SUCCESS ;
}
*/
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 ;
2020-08-03 23:42:05 +08:00
bool found_key = false ;
2019-03-10 06:35:06 +08:00
//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 ;
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 ( ) ;
2020-05-20 14:58:28 +08:00
uint8_t * keyBlock = NULL ;
uint32_t keycount = 0 ;
// load keys
int res = loadFileDICTIONARY_safe ( filename , ( void * * ) & keyBlock , 8 , & keycount ) ;
if ( res ! = PM3_SUCCESS | | keycount = = 0 ) {
free ( keyBlock ) ;
return res ;
}
2020-10-20 07:00:23 +08:00
// Get CSN / UID and CCNR
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( SUCCESS , " Reading tag CSN / CCNR... " ) ;
for ( uint8_t i = 0 ; i < ICLASS_AUTH_RETRY & & ! got_csn ; i + + ) {
got_csn = select_only ( CSN , CCNR , false ) ;
if ( got_csn = = false )
PrintAndLogEx ( WARNING , " one more try " ) ;
}
if ( got_csn = = false ) {
PrintAndLogEx ( WARNING , " Tried 10 times. Can't select card, aborting... " ) ;
free ( keyBlock ) ;
DropField ( ) ;
return PM3_ESOFT ;
}
2020-10-20 07:00:23 +08:00
2020-10-18 17:50:20 +08:00
iclass_premac_t * pre = calloc ( keycount , sizeof ( iclass_premac_t ) ) ;
if ( pre = = NULL ) {
return PM3_EMALLOC ;
}
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( SUCCESS , " CSN: " _GREEN_ ( " %s " ) , sprint_hex ( CSN , sizeof ( CSN ) ) ) ;
PrintAndLogEx ( SUCCESS , " CCNR: " _GREEN_ ( " %s " ) , sprint_hex ( CCNR , sizeof ( CCNR ) ) ) ;
2020-08-18 17:11:43 +08:00
PrintAndLogEx ( SUCCESS , " Generating diversified keys %s " , ( use_elite | | use_raw ) ? NOLF : " " ) ;
2019-03-10 06:35:06 +08:00
if ( use_elite )
2020-08-18 17:11:43 +08:00
PrintAndLogEx ( NORMAL , " using " _YELLOW_ ( " elite algo " ) ) ;
2019-03-10 06:35:06 +08:00
if ( use_raw )
2020-08-18 17:11:43 +08:00
PrintAndLogEx ( NORMAL , " using " _YELLOW_ ( " raw mode " ) ) ;
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
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( SUCCESS , " Searching for " _YELLOW_ ( " %s " ) " key... " , ( use_credit_key ) ? " CREDIT " : " DEBIT " ) ;
2019-03-10 06:35:06 +08:00
2020-10-18 17:50:20 +08:00
2019-03-10 06:35:06 +08:00
// 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 ( ) ) {
2020-08-18 17:11:43 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
PrintAndLogEx ( WARNING , " Aborted via keyboard! " ) ;
2019-03-10 06:35:06 +08:00
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 ;
2020-08-13 18:25:04 +08:00
2020-08-03 23:42:05 +08:00
bool looped = false ;
2020-07-13 18:29:31 +08:00
while ( ! WaitForResponseTimeout ( CMD_HF_ICLASS_CHKKEYS , & resp , 2000 ) ) {
2019-03-10 06:35:06 +08:00
timeout + + ;
2020-08-29 21:22:26 +08:00
PrintAndLogEx ( NORMAL , " . " NOLF ) ;
2019-03-10 06:35:06 +08:00
if ( timeout > 120 ) {
2020-08-29 21:22:26 +08:00
PrintAndLogEx ( WARNING , " \n No response from Proxmark3. Aborting... " ) ;
2019-03-10 06:35:06 +08:00
goto out ;
}
2020-08-03 23:42:05 +08:00
looped = true ;
2019-03-10 06:35:06 +08:00
}
2020-08-03 23:42:05 +08:00
if ( looped )
PrintAndLogEx ( NORMAL , " " ) ;
2019-03-10 06:35:06 +08:00
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 : {
2020-08-03 23:42:05 +08:00
found_key = true ;
2020-08-18 17:11:43 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( SUCCESS , " Found valid key " _GREEN_ ( " %s " )
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 : {
2020-08-21 22:47:18 +08:00
PrintAndLogEx ( INPLACE , " Chunk [%d/%d] " , key_offset , keycount ) ;
2019-03-10 06:35:06 +08:00
break ;
}
case 99 : {
}
2020-08-18 17:11:43 +08:00
default : {
2019-03-10 07:00:59 +08:00
break ;
2020-08-18 17:11:43 +08:00
}
2019-03-10 06:35:06 +08:00
}
// both keys found.
2020-08-03 23:42:05 +08:00
if ( found_key ) {
2019-03-10 06:35:06 +08:00
break ;
}
2020-10-18 17:50:20 +08:00
}
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
2020-08-18 17:11:43 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-08-21 22:47:18 +08:00
PrintAndLogEx ( SUCCESS , " time in iclass chk " _YELLOW_ ( " %.0f " ) " seconds " , ( float ) t1 / 1000.0 ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
2019-08-24 11:08:03 +08:00
2020-08-03 23:42:05 +08:00
if ( found_key ) {
uint8_t * key = keyBlock + ( key_offset + found_offset ) * 8 ;
add_key ( key ) ;
2019-08-24 11:08:03 +08:00
}
2019-03-10 06:35:06 +08:00
free ( pre ) ;
free ( keyBlock ) ;
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
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
2020-07-21 20:09:05 +08:00
// this method tries to identify in which configuration mode a iCLASS / iCLASS SE reader is in.
2018-01-02 18:17:31 +08:00
// 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
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( SUCCESS , " CSN: " _GREEN_ ( " %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: " _GREEN_ ( " %s " ) , sprint_hex ( CCNR , sizeof ( CCNR ) ) ) ;
PrintAndLogEx ( SUCCESS , " TAG MAC: %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 ;
2020-05-20 14:58:28 +08:00
uint32_t keycount = 0 ;
2019-08-29 13:47:17 +08:00
// 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
}
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( SUCCESS , " Generating diversified keys... " ) ;
2019-08-29 13:47:17 +08:00
GenerateMacKeyFrom ( CSN , CCNR , use_raw , use_elite , keyBlock , keycount , prekey ) ;
2019-03-10 06:35:06 +08:00
2020-08-03 23:42:05 +08:00
if ( use_elite )
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " elite algo " ) ) ;
if ( use_raw )
PrintAndLogEx ( SUCCESS , " Using " _YELLOW_ ( " raw mode " ) ) ;
PrintAndLogEx ( SUCCESS , " 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
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( SUCCESS , " Searching for " _YELLOW_ ( " %s " ) " key... " , " DEBIT " ) ;
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 ) ;
2020-08-13 18:25:04 +08:00
2019-08-24 11:08:03 +08:00
if ( item ! = NULL ) {
2020-08-03 23:42:05 +08:00
PrintAndLogEx ( SUCCESS , " Found valid key " _GREEN_ ( " %s " ) , sprint_hex ( item - > key , 8 ) ) ;
add_key ( item - > key ) ;
2019-08-24 11:08:03 +08:00
}
2019-08-30 16:45:52 +08:00
2020-08-03 23:42:05 +08:00
t1 = msclock ( ) - t1 ;
2020-08-21 22:47:18 +08:00
PrintAndLogEx ( SUCCESS , " time in iclass lookup " _YELLOW_ ( " %.0f " ) " seconds " , ( float ) t1 / 1000.0 ) ;
2020-08-03 23:42:05 +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
2020-05-20 14:58:28 +08:00
void GenerateMacFrom ( uint8_t * CSN , uint8_t * CCNR , bool use_raw , bool use_elite , uint8_t * keys , uint32_t 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
2020-05-20 14:58:28 +08:00
//iceman: threading
for ( uint32_t 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
}
2020-05-20 14:58:28 +08:00
void GenerateMacKeyFrom ( uint8_t * CSN , uint8_t * CCNR , bool use_raw , bool use_elite , uint8_t * keys , uint32_t 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
2020-05-20 14:58:28 +08:00
//iceman: threading
for ( uint32_t 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
2020-05-20 14:58:28 +08:00
void PrintPreCalcMac ( uint8_t * keys , uint32_t keycnt , iclass_premac_t * pre_list ) {
2017-12-24 17:59:24 +08:00
2020-05-20 14:58:28 +08:00
iclass_prekey_t * b = calloc ( keycnt , sizeof ( iclass_prekey_t ) ) ;
2019-03-10 07:00:59 +08:00
if ( ! b )
2019-03-10 06:35:06 +08:00
return ;
2019-03-09 15:59:13 +08:00
2020-05-20 14:58:28 +08:00
for ( uint32_t i = 0 ; i < keycnt ; i + + ) {
2019-03-10 07:00:59 +08:00
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
}
2020-05-20 14:58:28 +08:00
void PrintPreCalc ( iclass_prekey_t * list , uint32_t 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 ) {
2020-05-14 22:59:02 +08:00
PrintAndLogEx ( WARNING , " wrong key size \n " ) ;
2019-03-10 06:35:06 +08:00
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 } ;
int len = 0 ;
2019-03-09 15:59:13 +08:00
2020-10-05 02:52:59 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf iclass permute " ,
" Permute function from 'heart of darkness' paper. " ,
" hf iclass permute --reverse --key 0123456789abcdef \n "
" hf iclass permute --key ff55330f0055330f \n " ) ;
void * argtable [ ] = {
arg_param_begin ,
2020-10-07 00:00:00 +08:00
arg_lit0 ( " r " , " reverse " , " reverse permuted key " ) ,
2020-10-05 02:52:59 +08:00
arg_str1 ( NULL , " key " , " <bytes> " , " input key " ) ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
2019-03-09 15:59:13 +08:00
2020-10-18 05:09:34 +08:00
bool isReverse = arg_get_lit ( ctx , 1 ) ;
2020-10-07 00:00:00 +08:00
2020-10-05 02:52:59 +08:00
CLIGetHexWithReturn ( ctx , 2 , data , & len ) ;
2018-02-05 00:19:08 +08:00
2020-10-07 00:00:00 +08:00
CLIParserFree ( ctx ) ;
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 ) ;
2020-07-09 06:21:26 +08:00
PrintAndLogEx ( SUCCESS , " Standard NIST format key " _YELLOW_ ( " %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 ) ;
2020-07-09 06:21:26 +08:00
PrintAndLogEx ( SUCCESS , " HID permuted iCLASS format: %s \n " , sprint_hex ( key_iclass_format , 8 ) ) ;
2019-03-10 06:35:06 +08:00
}
2019-05-15 16:24:06 +08:00
return PM3_SUCCESS ;
2018-02-05 00:19:08 +08:00
}
2020-10-18 05:09:34 +08:00
static int CmdHFiClassAutopwn ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf iclass autopwn " ,
" Tries to check keys, if found, dump card and save file " ,
" hf iclass autopwn \n " ) ;
void * argtable [ ] = {
arg_param_begin ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
CLIParserFree ( ctx ) ;
2020-10-20 07:00:23 +08:00
2020-10-18 05:09:34 +08:00
// Check keys.
// dump
PrintAndLogEx ( INFO , " to be implemented " ) ;
return PM3_SUCCESS ;
}
2016-01-20 02:31:34 +08:00
static command_t CommandTable [ ] = {
2020-08-03 23:42:05 +08:00
{ " ----------- " , CmdHelp , AlwaysAvailable , " --------------------- " _CYAN_ ( " operations " ) " --------------------- " } ,
2019-05-02 02:48:15 +08:00
{ " help " , CmdHelp , AlwaysAvailable , " This help " } ,
2020-08-03 23:42:05 +08:00
// {"clone", CmdHFiClassClone, IfPm3Iclass, "[options..] Create a HID credential to Picopass / iCLASS tag"},
{ " dump " , CmdHFiClassDump , IfPm3Iclass , " [options..] Dump Picopass / iCLASS tag to file " } ,
2019-09-08 23:37:14 +08:00
{ " info " , CmdHFiClassInfo , AlwaysAvailable , " Tag information " } ,
2020-08-03 23:42:05 +08:00
{ " list " , CmdHFiClassList , AlwaysAvailable , " List iclass history " } ,
{ " rdbl " , CmdHFiClass_ReadBlock , IfPm3Iclass , " [options..] Read Picopass / iCLASS block " } ,
{ " reader " , CmdHFiClassReader , IfPm3Iclass , " Act like an Picopass / iCLASS reader " } ,
{ " restore " , CmdHFiClassRestore , IfPm3Iclass , " [options..] Restore a dump file onto a Picopass / iCLASS tag " } ,
{ " sniff " , CmdHFiClassSniff , IfPm3Iclass , " Eavesdrop Picopass / iCLASS communication " } ,
{ " wrbl " , CmdHFiClass_WriteBlock , IfPm3Iclass , " [options..] Write Picopass / iCLASS block " } ,
{ " ----------- " , CmdHelp , AlwaysAvailable , " --------------------- " _CYAN_ ( " recovery " ) " --------------------- " } ,
2020-10-18 05:09:34 +08:00
{ " autopwn " , CmdHFiClassAutopwn , IfPm3Iclass , " [options..] Automatic key recovery tool for iCLASS " } ,
{ " chk " , CmdHFiClassCheckKeys , IfPm3Iclass , " [options..] Check keys " } ,
2019-09-09 23:33:44 +08:00
{ " loclass " , CmdHFiClass_loclass , AlwaysAvailable , " [options..] Use loclass to perform bruteforce reader attack " } ,
2020-08-13 18:25:04 +08:00
{ " lookup " , CmdHFiClassLookUp , AlwaysAvailable , " [options..] Uses authentication trace to check for key in dictionary file " } ,
2020-08-03 23:42:05 +08:00
{ " ----------- " , CmdHelp , AlwaysAvailable , " --------------------- " _CYAN_ ( " simulation " ) " --------------------- " } ,
{ " sim " , CmdHFiClassSim , IfPm3Iclass , " [options..] Simulate iCLASS tag " } ,
{ " eload " , CmdHFiClassELoad , IfPm3Iclass , " [f <fn> ] Load Picopass / iCLASS dump file into emulator memory " } ,
2020-08-13 18:25:04 +08:00
{ " esave " , CmdHFiClassESave , IfPm3Iclass , " [f <fn> ] Save emulator memory to file " } ,
2020-08-30 15:55:57 +08:00
{ " eview " , CmdHFiClassEView , IfPm3Iclass , " [options..] View emulator memory " } ,
2020-08-03 23:42:05 +08:00
2020-08-13 18:25:04 +08:00
{ " ----------- " , CmdHelp , AlwaysAvailable , " --------------------- " _CYAN_ ( " utils " ) " --------------------- " } ,
2020-08-03 23:42:05 +08:00
{ " calcnewkey " , CmdHFiClassCalcNewKey , AlwaysAvailable , " [options..] Calc diversified keys (blocks 3 & 4) to write new keys " } ,
{ " encrypt " , CmdHFiClassEncryptBlk , AlwaysAvailable , " [options..] Encrypt given block data " } ,
{ " decrypt " , CmdHFiClassDecrypt , AlwaysAvailable , " [options..] Decrypt given block data or tag dump file " } ,
{ " managekeys " , CmdHFiClassManageKeys , AlwaysAvailable , " [options..] Manage keys to use with iclass commands " } ,
2020-10-06 21:03:24 +08:00
{ " permute " , CmdHFiClassPermuteKey , IfPm3Iclass , " Permute function from 'heart of darkness' paper " } ,
2020-07-29 17:02:30 +08:00
{ " view " , CmdHFiClassView , AlwaysAvailable , " [options..] Display content from tag dump file " } ,
2020-08-03 23:42:05 +08:00
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
}
2020-07-20 02:45:47 +08:00
//static void test_credential_type(void) {
2020-08-13 18:25:04 +08:00
// need AA1 key
// Block 5 -> tells if its a legacy or SIO, also tells which key to use.
// tech | blocks used | desc | num of payloads
// -------+-----------------------+-----------------------------------+------
// legacy | 6,7,8,9 | AA!, Access control payload | 1
// SE | 6,7,8,9,10,11,12 | AA1, Secure identity object (SIO) | 1
// SR | 6,7,8,9, | AA1, Access control payload | 2
// | 10,11,12,13,14,15,16 | AA1, Secure identity object (SIO) |
// SEOS | | |
2020-07-20 02:45:47 +08:00
//}
2020-07-29 17:02:30 +08:00
int info_iclass ( void ) {
2019-04-13 00:41:14 +08:00
2020-07-29 17:02:30 +08:00
uint32_t flags = ( FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE ) ;
2019-04-13 00:41:14 +08:00
2020-07-29 17:02:30 +08:00
clearCommandBuffer ( ) ;
SendCommandMIX ( CMD_HF_ICLASS_READER , flags , 0 , 0 , NULL , 0 ) ;
PacketResponseNG resp ;
2019-10-19 21:38:53 +08:00
2020-07-29 17:02:30 +08:00
if ( WaitForResponseTimeout ( CMD_ACK , & resp , 2000 ) ) {
2019-10-19 21:38:53 +08:00
2020-07-29 17:02:30 +08:00
uint8_t readStatus = resp . oldarg [ 0 ] & 0xff ;
2019-09-11 22:36:41 +08:00
2020-07-29 17:02:30 +08:00
// no tag found or button pressed
if ( readStatus = = 0 | | readStatus = = 0xFF ) {
DropField ( ) ;
return PM3_EOPABORTED ;
}
2020-06-29 01:13:01 +08:00
2020-07-29 17:02:30 +08:00
picopass_hdr * hdr = ( picopass_hdr * ) resp . data . asBytes ;
picopass_ns_hdr * ns_hdr = ( picopass_ns_hdr * ) resp . data . asBytes ;
2020-06-29 01:13:01 +08:00
2020-07-29 17:02:30 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " --------------------- " _CYAN_ ( " Tag Information " ) " ---------------------- " ) ;
PrintAndLogEx ( INFO , " ------------------------------------------------------------ " ) ;
2019-09-11 22:36:41 +08:00
2020-07-29 17:02:30 +08:00
if ( readStatus & FLAG_ICLASS_CSN ) {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " CSN: " _GREEN_ ( " %s " ) " uid " , sprint_hex ( hdr - > csn , sizeof ( hdr - > csn ) ) ) ;
2020-07-29 17:02:30 +08:00
}
2019-09-11 22:36:41 +08:00
2020-07-29 17:02:30 +08:00
if ( readStatus & FLAG_ICLASS_CONF ) {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " Config: %s card configuration " , sprint_hex ( ( uint8_t * ) & hdr - > conf , sizeof ( hdr - > conf ) ) ) ;
2020-07-29 17:02:30 +08:00
}
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// page mapping. If fuse0|1 == 0x01, card is in non-secure mode, with CSN, CONF, AIA as top 3 blocks.
// page9 in http://www.proxmark.org/files/Documents/13.56%20MHz%20-%20iClass/DS%20Picopass%202KS%20V1-0.pdf
uint8_t pagemap = get_pagemap ( hdr ) ;
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " AIA: %s application issuer area " , sprint_hex ( ns_hdr - > app_issuer_area , sizeof ( ns_hdr - > app_issuer_area ) ) ) ;
2020-08-13 18:25:04 +08:00
} else {
2020-06-29 01:13:01 +08:00
2020-07-06 21:16:00 +08:00
if ( readStatus & FLAG_ICLASS_CC ) {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " E-purse: %s Card challenge, CC " , sprint_hex ( hdr - > epurse , sizeof ( hdr - > epurse ) ) ) ;
2019-04-13 00:41:14 +08:00
}
2020-08-13 18:25:04 +08:00
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " Kd: %s debit key, hidden " , sprint_hex ( hdr - > key_d , sizeof ( hdr - > key_d ) ) ) ;
PrintAndLogEx ( SUCCESS , " Kc: %s credit key, hidden " , sprint_hex ( hdr - > key_c , sizeof ( hdr - > key_c ) ) ) ;
2019-09-11 22:36:41 +08:00
2020-07-06 21:16:00 +08:00
if ( readStatus & FLAG_ICLASS_AIA ) {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " AIA: %s application issuer area " , sprint_hex ( hdr - > app_issuer_area , sizeof ( hdr - > app_issuer_area ) ) ) ;
2020-08-13 18:25:04 +08:00
}
2020-07-29 17:02:30 +08:00
}
2019-04-13 00:41:14 +08:00
2020-07-29 17:02:30 +08:00
if ( readStatus & FLAG_ICLASS_CONF ) {
2020-08-13 18:25:04 +08:00
print_picopass_info ( hdr ) ;
2020-07-29 17:02:30 +08:00
}
2019-10-19 21:38:53 +08:00
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( INFO , " ------------------------ " _CYAN_ ( " Fingerprint " ) " ----------------------- " ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
uint8_t aia [ 8 ] ;
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE )
memcpy ( aia , ns_hdr - > app_issuer_area , sizeof ( aia ) ) ;
else
memcpy ( aia , hdr - > app_issuer_area , sizeof ( aia ) ) ;
2019-10-19 21:38:53 +08:00
2020-07-29 17:02:30 +08:00
// if CSN ends with FF12E0, it's inside HID CSN range.
bool isHidRange = ( memcmp ( hdr - > csn + 5 , " \xFF \x12 \xE0 " , 3 ) = = 0 ) ;
2019-04-13 00:41:14 +08:00
2020-07-29 17:02:30 +08:00
bool legacy = ( memcmp ( aia , " \xff \xff \xff \xff \xff \xff \xff \xff " , 8 ) = = 0 ) ;
bool se_enabled = ( memcmp ( aia , " \xff \xff \xff \x00 \x06 \xff \xff \xff " , 8 ) = = 0 ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
if ( isHidRange ) {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " CSN.......... " _YELLOW_ ( " HID range " ) ) ;
2020-07-29 17:02:30 +08:00
if ( legacy )
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " Credential... " _GREEN_ ( " iCLASS legacy " ) ) ;
2020-07-29 17:02:30 +08:00
if ( se_enabled )
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " Credential... " _GREEN_ ( " iCLASS SE " ) ) ;
2019-04-13 00:41:14 +08:00
} else {
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " CSN..-....... " _YELLOW_ ( " outside HID range " ) ) ;
2019-04-13 00:41:14 +08:00
}
2020-07-29 17:02:30 +08:00
uint8_t cardtype = get_mem_config ( hdr ) ;
2020-10-19 16:33:45 +08:00
PrintAndLogEx ( SUCCESS , " Card type.... " _GREEN_ ( " %s " ) , card_types [ cardtype ] ) ;
2019-04-13 00:41:14 +08:00
}
2020-07-29 17:02:30 +08:00
2019-04-13 00:41:14 +08:00
DropField ( ) ;
2020-07-29 17:02:30 +08:00
return PM3_SUCCESS ;
2011-05-18 20:33:32 +08:00
}
2019-04-13 00:41:14 +08:00