2011-05-18 20:33:32 +08:00
//-----------------------------------------------------------------------------
// Gerhard de Koning Gans - May 2008
// Hagen Fritsch - June 2010
// Gerhard de Koning Gans - May 2011
2012-06-28 21:38:40 +08:00
// Gerhard de Koning Gans - June 2012 - Added iClass card and reader emulation
2020-07-06 21:16:00 +08:00
// piwi - 2019
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.
//-----------------------------------------------------------------------------
// Routines to support iClass.
//-----------------------------------------------------------------------------
// Contribution made during a security research at Radboud University Nijmegen
2019-03-09 15:59:13 +08:00
//
2011-05-18 20:33:32 +08:00
// Please feel free to contribute and extend iClass support!!
//-----------------------------------------------------------------------------
2019-08-08 22:57:33 +08:00
# include "iclass.h"
# include "proxmark3_arm.h"
2014-09-12 05:23:46 +08:00
# include "cmd.h"
2012-06-28 21:38:40 +08:00
// Needed for CRC in emulation mode;
// same construction as in ISO 14443;
// different initial value (CRC_ICLASS)
2018-01-29 23:38:03 +08:00
# include "crc16.h"
2015-02-27 03:35:35 +08:00
# include "optimized_cipher.h"
2019-08-08 22:57:33 +08:00
# include "appmain.h"
# include "BigBuf.h"
# include "fpgaloader.h"
# include "string.h"
# include "util.h"
# include "dbprint.h"
# include "protocols.h"
# include "ticks.h"
2020-07-04 03:33:17 +08:00
# include "iso15693.h"
2011-05-18 20:33:32 +08:00
2020-08-03 23:42:05 +08:00
static uint8_t get_pagemap ( const picopass_hdr * hdr ) {
return ( hdr - > conf . fuses & ( FUSE_CRYPT0 | FUSE_CRYPT1 ) ) > > 3 ;
}
2017-08-28 01:41:24 +08:00
// The length of a received command will in most cases be no more than 18 bytes.
2020-08-03 23:42:05 +08:00
// we expect max 34 (32+2) bytes as tag answer (response to READ4)
2019-03-09 15:59:13 +08:00
# ifndef ICLASS_BUFFER_SIZE
2020-07-20 02:45:47 +08:00
# define ICLASS_BUFFER_SIZE 34 + 2
2017-08-28 01:41:24 +08:00
# endif
2020-08-06 22:19:42 +08:00
# ifndef ICLASS_16KS_SIZE
# define ICLASS_16KS_SIZE 0x100 * 8
# endif
2020-07-04 03:33:17 +08:00
// iCLASS has a slightly different timing compared to ISO15693. According to the picopass data sheet the tag response is expected 330us after
// the reader command. This is measured from end of reader EOF to first modulation of the tag's SOF which starts with a 56,64us unmodulated period.
// 330us = 140 ssp_clk cycles @ 423,75kHz when simulating.
// 56,64us = 24 ssp_clk_cycles
2020-08-06 14:13:29 +08:00
# define DELAY_ICLASS_VCD_TO_VICC_SIM (140 - 26) // (140 - 24)
2018-02-01 22:19:47 +08:00
2020-07-04 03:33:17 +08:00
// times in ssp_clk_cycles @ 3,3625MHz when acting as reader
# define DELAY_ICLASS_VICC_TO_VCD_READER DELAY_ISO15693_VICC_TO_VCD_READER
2017-08-26 18:57:18 +08:00
2020-07-04 03:33:17 +08:00
// times in samples @ 212kHz when acting as reader
# define ICLASS_READER_TIMEOUT_ACTALL 330 // 1558us, nominal 330us + 7slots*160us = 1450us
# define ICLASS_READER_TIMEOUT_UPDATE 3390 // 16000us, nominal 4-15ms
# define ICLASS_READER_TIMEOUT_OTHERS 80 // 380us, nominal 330us
# define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1)
2019-08-08 22:57:33 +08:00
2020-07-29 17:02:30 +08:00
2017-08-26 18:57:18 +08:00
/*
* CARD TO READER
2019-03-09 15:59:13 +08:00
* in ISO15693 - 2 mode - Manchester
2017-08-26 18:57:18 +08:00
* in ISO 14443 b - BPSK coding
*
* Timings :
2019-03-09 15:59:13 +08:00
* ISO 15693 - 2
2017-08-26 18:57:18 +08:00
* Tout = 330 µ s , Tprog 1 = 4 to 15 ms , Tslot = 330 µ s + ( number of slots x 160 µ s )
* ISO 14443 a
2019-03-10 03:34:41 +08:00
* Tout = 100 µ s , Tprog = 4 to 15 ms , Tslot = 100 µ s + ( number of slots x 80 µ s )
2017-08-26 18:57:18 +08:00
* ISO 14443 b
2019-03-10 03:34:41 +08:00
Tout = 76 µ s , Tprog = 4 to 15 ms , Tslot = 119 µ s + ( number of slots x 150 µ s )
2017-08-26 18:57:18 +08:00
*
*
* So for current implementation in ISO15693 , its 330 µ s from end of reader , to start of card .
*/
2011-05-18 20:33:32 +08:00
//=============================================================================
2020-07-04 03:33:17 +08:00
// a `sniffer' for iClass communication
2011-05-18 20:33:32 +08:00
// Both sides of communication!
//=============================================================================
2020-07-04 03:33:17 +08:00
void SniffIClass ( uint8_t jam_search_len , uint8_t * jam_search_string ) {
SniffIso15693 ( jam_search_len , jam_search_string ) ;
2017-08-28 01:41:24 +08:00
}
2017-08-19 15:49:41 +08:00
2020-07-04 03:33:17 +08:00
static void rotateCSN ( uint8_t * original_csn , uint8_t * rotated_csn ) {
2020-07-06 21:16:00 +08:00
for ( uint8_t i = 0 ; i < 8 ; i + + ) {
2020-07-04 03:33:17 +08:00
rotated_csn [ i ] = ( original_csn [ i ] > > 3 ) | ( original_csn [ ( i + 1 ) % 8 ] < < 5 ) ;
2020-07-06 21:16:00 +08:00
}
2012-06-28 21:38:40 +08:00
}
2020-07-04 03:33:17 +08:00
// Encode SOF only
static void CodeIClassTagSOF ( void ) {
2020-07-13 23:56:19 +08:00
tosend_reset ( ) ;
tosend_t * ts = get_tosend ( ) ;
ts - > buf [ + + ts - > max ] = 0x1D ;
ts - > max + + ;
2012-06-28 21:38:40 +08:00
}
2019-09-11 21:18:40 +08:00
/*
2020-07-04 03:33:17 +08:00
* SOF comprises 3 parts ;
* * An unmodulated time of 56.64 us
* * 24 pulses of 423.75 kHz ( fc / 32 )
* * A logic 1 , which starts with an unmodulated time of 18.88 us
* followed by 8 pulses of 423.75 kHz ( fc / 32 )
*
*
* EOF comprises 3 parts :
* - A logic 0 ( which starts with 8 pulses of fc / 32 followed by an unmodulated
* time of 18.88 us .
* - 24 pulses of fc / 32
* - An unmodulated time of 56.64 us
*
*
* A logic 0 starts with 8 pulses of fc / 32
* followed by an unmodulated time of 256 / fc ( ~ 18 , 88u s ) .
*
* A logic 0 starts with unmodulated time of 256 / fc ( ~ 18 , 88u s ) followed by
* 8 pulses of fc / 32 ( also 18.88 us )
*
* The mode FPGA_HF_SIMULATOR_MODULATE_424K_8BIT which we use to simulate tag ,
* works like this .
* - A 1 - bit input to the FPGA becomes 8 pulses on 423.5 kHz ( fc / 32 ) ( 18.88 us ) .
* - A 0 - bit input to the FPGA becomes an unmodulated time of 18.88 us
*
* In this mode
* SOF can be written as 00011101 = 0x1D
* EOF can be written as 10111000 = 0xb8
* logic 1 be written as 01 = 0x1
* logic 0 be written as 10 = 0x2
*
*
*/
2014-06-08 04:00:31 +08:00
2014-04-17 15:53:54 +08:00
/**
* @ brief SimulateIClass simulates an iClass card .
* @ param arg0 type of simulation
2019-03-10 03:34:41 +08:00
* - 0 uses the first 8 bytes in usb data as CSN
* - 2 " dismantling iclass " - attack . This mode iterates through all CSN ' s specified
* in the usb data . This mode collects MAC from the reader , in order to do an offline
* attack on the keys . For more info , see " dismantling iclass " and proxclone . com .
* - Other : Uses the default CSN ( 031f ec8af7ff12e0 )
2014-04-17 15:53:54 +08:00
* @ param arg1 - number of CSN ' s contained in datain ( applicable for mode 2 only )
* @ param arg2
* @ param datain
*/
2017-08-20 01:50:55 +08:00
// turn off afterwards
2019-03-10 18:20:22 +08:00
void SimulateIClass ( uint32_t arg0 , uint32_t arg1 , uint32_t arg2 , uint8_t * datain ) {
2020-07-14 21:43:54 +08:00
iclass_simulate ( arg0 , arg1 , arg2 , datain , NULL , NULL ) ;
}
void iclass_simulate ( uint8_t sim_type , uint8_t num_csns , bool send_reply , uint8_t * datain , uint8_t * dataout , uint16_t * dataoutlen ) {
2017-08-19 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
LEDsoff ( ) ;
2020-07-13 18:21:42 +08:00
Iso15693InitTag ( ) ;
2020-08-13 18:25:04 +08:00
2019-03-10 03:34:41 +08:00
clear_trace ( ) ;
2020-08-13 18:25:04 +08:00
2020-08-06 22:19:42 +08:00
// only logg if we are called from the client.
set_tracing ( send_reply ) ;
2019-03-10 03:34:41 +08:00
//Use the emulator memory for SIM
uint8_t * emulator = BigBuf_get_EM_addr ( ) ;
2019-05-01 03:10:11 +08:00
uint8_t mac_responses [ PM3_CMD_DATA_SIZE ] = { 0 } ;
2019-03-10 03:34:41 +08:00
2020-07-14 21:43:54 +08:00
if ( sim_type = = ICLASS_SIM_MODE_CSN ) {
2019-03-10 03:34:41 +08:00
// Use the CSN from commandline
memcpy ( emulator , datain , 8 ) ;
2020-07-15 17:35:27 +08:00
do_iclass_simulation ( ICLASS_SIM_MODE_CSN , NULL ) ;
2020-07-14 21:43:54 +08:00
} else if ( sim_type = = ICLASS_SIM_MODE_CSN_DEFAULT ) {
2019-03-10 03:34:41 +08:00
//Default CSN
2020-07-04 03:33:17 +08:00
uint8_t csn [ ] = { 0x03 , 0x1f , 0xec , 0x8a , 0xf7 , 0xff , 0x12 , 0xe0 } ;
2019-03-10 03:34:41 +08:00
// Use the CSN from commandline
2020-07-04 03:33:17 +08:00
memcpy ( emulator , csn , 8 ) ;
2020-07-15 17:35:27 +08:00
do_iclass_simulation ( ICLASS_SIM_MODE_CSN , NULL ) ;
2019-03-10 03:34:41 +08:00
2020-07-14 21:43:54 +08:00
} else if ( sim_type = = ICLASS_SIM_MODE_READER_ATTACK ) {
Dbprintf ( " going into attack mode, %d CSNS sent " , num_csns ) ;
2019-03-10 03:34:41 +08:00
// In this mode, a number of csns are within datain. We'll simulate each one, one at a time
// in order to collect MAC's from the reader. This can later be used in an offlne-attack
// in order to obtain the keys, as in the "dismantling iclass"-paper.
2020-08-13 18:25:04 +08:00
# define EPURSE_MAC_SIZE 16
2019-03-10 03:34:41 +08:00
int i = 0 ;
2020-07-14 21:43:54 +08:00
for ( ; i < num_csns & & i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE ; i + + ) {
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
memcpy ( emulator , datain + ( i * 8 ) , 8 ) ;
2019-03-10 03:34:41 +08:00
2020-07-15 17:35:27 +08:00
if ( do_iclass_simulation ( ICLASS_SIM_MODE_EXIT_AFTER_MAC , mac_responses + i * EPURSE_MAC_SIZE ) ) {
2020-08-13 18:25:04 +08:00
if ( dataoutlen )
2020-07-14 21:43:54 +08:00
* dataoutlen = i * EPURSE_MAC_SIZE ;
2019-03-10 03:34:41 +08:00
// Button pressed
2020-07-14 21:43:54 +08:00
if ( send_reply )
reply_old ( CMD_ACK , CMD_HF_ICLASS_SIMULATE , i , 0 , mac_responses , i * EPURSE_MAC_SIZE ) ;
2019-03-10 03:34:41 +08:00
goto out ;
}
}
2020-08-13 18:25:04 +08:00
if ( dataoutlen )
2020-07-14 21:43:54 +08:00
* dataoutlen = i * EPURSE_MAC_SIZE ;
if ( send_reply )
reply_old ( CMD_ACK , CMD_HF_ICLASS_SIMULATE , i , 0 , mac_responses , i * EPURSE_MAC_SIZE ) ;
2019-03-10 03:34:41 +08:00
2020-07-14 21:43:54 +08:00
} else if ( sim_type = = ICLASS_SIM_MODE_FULL ) {
2020-07-29 21:29:30 +08:00
2019-03-10 03:34:41 +08:00
//This is 'full sim' mode, where we use the emulator storage for data.
//ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command
2020-07-29 21:29:30 +08:00
picopass_hdr * hdr = ( picopass_hdr * ) BigBuf_get_EM_addr ( ) ;
2020-08-03 23:42:05 +08:00
uint8_t pagemap = get_pagemap ( hdr ) ;
2020-07-29 21:29:30 +08:00
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
do_iclass_simulation_nonsec ( ) ;
} else {
do_iclass_simulation ( ICLASS_SIM_MODE_FULL , NULL ) ;
}
2020-07-15 17:35:27 +08:00
} else if ( sim_type = = ICLASS_SIM_MODE_CONFIG_CARD ) {
2020-07-29 21:29:30 +08:00
2020-08-13 18:25:04 +08:00
// config card
2020-07-15 17:35:27 +08:00
do_iclass_simulation ( ICLASS_SIM_MODE_FULL , NULL ) ;
// swap bin
2020-07-14 21:43:54 +08:00
} else if ( sim_type = = ICLASS_SIM_MODE_READER_ATTACK_KEYROLL ) {
2019-03-10 03:34:41 +08:00
// This is the KEYROLL version of sim 2.
// the collected data (mac_response) is doubled out since we are trying to collect both keys in the keyroll process.
// Keyroll iceman 9 csns * 8 * 2 = 144
// keyroll CARL55 15csns * 8 * 2 = 15 * 8 * 2 = 240
2020-07-14 21:43:54 +08:00
Dbprintf ( " going into attack keyroll mode, %d CSNS sent " , num_csns ) ;
2019-03-10 03:34:41 +08:00
// In this mode, a number of csns are within datain. We'll simulate each one, one at a time
// in order to collect MAC's from the reader. This can later be used in an offlne-attack
// in order to obtain the keys, as in the "dismantling iclass"-paper.
// keyroll mode, reader swaps between old key and new key alternatively when fail a authentication.
// attack below is same as SIM 2, but we run the CSN twice to collected the mac for both keys.
int i = 0 ;
// The usb data is 512 bytes, fitting 65 8-byte CSNs in there. iceman fork uses 9 CSNS
2020-07-14 21:43:54 +08:00
for ( ; i < num_csns & & i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE ; i + + ) {
2019-03-10 03:34:41 +08:00
2019-03-10 07:00:59 +08:00
memcpy ( emulator , datain + ( i * 8 ) , 8 ) ;
2019-03-10 03:34:41 +08:00
// keyroll 1
2020-07-15 17:35:27 +08:00
if ( do_iclass_simulation ( ICLASS_SIM_MODE_EXIT_AFTER_MAC , mac_responses + i * EPURSE_MAC_SIZE ) ) {
2020-08-13 18:25:04 +08:00
if ( dataoutlen )
2020-07-14 21:43:54 +08:00
* dataoutlen = i * EPURSE_MAC_SIZE * 2 ;
2020-08-13 18:25:04 +08:00
if ( send_reply )
2020-07-14 21:43:54 +08:00
reply_old ( CMD_ACK , CMD_HF_ICLASS_SIMULATE , i * 2 , 0 , mac_responses , i * EPURSE_MAC_SIZE * 2 ) ;
2019-03-10 03:34:41 +08:00
// Button pressed
goto out ;
}
// keyroll 2
2020-07-15 17:35:27 +08:00
if ( do_iclass_simulation ( ICLASS_SIM_MODE_EXIT_AFTER_MAC , mac_responses + ( i + num_csns ) * EPURSE_MAC_SIZE ) ) {
2020-07-14 21:43:54 +08:00
2020-08-13 18:25:04 +08:00
if ( dataoutlen )
2020-07-14 21:43:54 +08:00
* dataoutlen = i * EPURSE_MAC_SIZE * 2 ;
2020-08-13 18:25:04 +08:00
if ( send_reply )
2020-07-14 21:43:54 +08:00
reply_old ( CMD_ACK , CMD_HF_ICLASS_SIMULATE , i * 2 , 0 , mac_responses , i * EPURSE_MAC_SIZE * 2 ) ;
2019-03-10 03:34:41 +08:00
// Button pressed
goto out ;
}
}
2020-08-13 18:25:04 +08:00
if ( dataoutlen )
2020-07-14 21:43:54 +08:00
* dataoutlen = i * EPURSE_MAC_SIZE * 2 ;
// double the amount of collected data.
if ( send_reply )
reply_old ( CMD_ACK , CMD_HF_ICLASS_SIMULATE , i * 2 , 0 , mac_responses , i * EPURSE_MAC_SIZE * 2 ) ;
2020-08-13 18:25:04 +08:00
2019-03-10 03:34:41 +08:00
} else {
// We may want a mode here where we hardcode the csns to use (from proxclone).
// That will speed things up a little, but not required just yet.
2020-07-14 05:27:16 +08:00
DbpString ( " the mode is not implemented, reserved for future use " ) ;
2019-03-10 03:34:41 +08:00
}
2017-09-12 14:45:38 +08:00
2019-03-09 15:59:13 +08:00
out :
2020-07-14 21:43:54 +08:00
if ( dataout & & dataoutlen )
memcpy ( dataout , mac_responses , * dataoutlen ) ;
2019-03-10 03:34:41 +08:00
switch_off ( ) ;
BigBuf_free_keep_EM ( ) ;
2014-04-17 15:53:54 +08:00
}
2015-02-19 03:02:44 +08:00
2014-04-17 15:53:54 +08:00
/**
2020-07-29 21:29:30 +08:00
* Simulation assumes a SECURE PAGE simulation with authentication and application areas .
2020-08-13 18:25:04 +08:00
*
2020-07-29 21:29:30 +08:00
*
2014-04-17 15:53:54 +08:00
* @ brief Does the actual simulation
* @ param csn - csn to use
* @ param breakAfterMacReceived if true , returns after reader MAC has been received .
*/
2020-07-15 17:35:27 +08:00
int do_iclass_simulation ( int simulationMode , uint8_t * reader_mac_buf ) {
2017-08-19 15:49:41 +08:00
2019-03-10 03:34:41 +08:00
// free eventually allocated BigBuf memory
BigBuf_free_keep_EM ( ) ;
2020-07-13 18:21:42 +08:00
uint16_t page_size = 32 * 8 ;
uint8_t current_page = 0 ;
2019-03-10 03:34:41 +08:00
2020-07-13 18:21:42 +08:00
// maintain cipher states for both credit and debit key for each page
State cipher_state_KD [ 8 ] ;
State cipher_state_KC [ 8 ] ;
State * cipher_state = & cipher_state_KD [ 0 ] ;
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
uint8_t * emulator = BigBuf_get_EM_addr ( ) ;
uint8_t * csn = emulator ;
2019-03-10 03:34:41 +08:00
// CSN followed by two CRC bytes
uint8_t anticoll_data [ 10 ] = { 0 } ;
uint8_t csn_data [ 10 ] = { 0 } ;
memcpy ( csn_data , csn , sizeof ( csn_data ) ) ;
// Construct anticollision-CSN
rotateCSN ( csn_data , anticoll_data ) ;
// Compute CRC on both CSNs
AddCrc ( anticoll_data , 8 ) ;
AddCrc ( csn_data , 8 ) ;
2020-07-13 18:21:42 +08:00
uint8_t diversified_kd [ 8 ] = { 0 } ;
uint8_t diversified_kc [ 8 ] = { 0 } ;
uint8_t * diversified_key = diversified_kd ;
2020-07-04 03:33:17 +08:00
2020-07-13 18:21:42 +08:00
// configuration block
uint8_t conf_block [ 10 ] = { 0x12 , 0xFF , 0xFF , 0xFF , 0x7F , 0x1F , 0xFF , 0x3C , 0x00 , 0x00 } ;
2019-11-07 16:43:32 +08:00
2019-03-10 03:34:41 +08:00
// e-Purse
2019-03-10 07:00:59 +08:00
uint8_t card_challenge_data [ 8 ] = { 0xfe , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ;
2019-03-10 03:34:41 +08:00
2020-07-04 03:33:17 +08:00
// AIA
uint8_t aia_data [ 10 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0x00 , 0x00 } ;
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
if ( simulationMode = = ICLASS_SIM_MODE_FULL ) {
2020-07-13 18:21:42 +08:00
memcpy ( conf_block , emulator + ( 8 * 1 ) , 8 ) ; // blk 1
memcpy ( card_challenge_data , emulator + ( 8 * 2 ) , 8 ) ; // e-purse, blk 2
memcpy ( diversified_kd , emulator + ( 8 * 3 ) , 8 ) ; // Kd, blk 3
memcpy ( diversified_kc , emulator + ( 8 * 4 ) , 8 ) ; // Kc, blk 4
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
// (iceman) this only works for 2KS / 16KS tags.
// Use application data from block 5
memcpy ( aia_data , emulator + ( 8 * 5 ) , 8 ) ;
2019-03-10 03:34:41 +08:00
}
2020-07-04 03:33:17 +08:00
AddCrc ( conf_block , 8 ) ;
AddCrc ( aia_data , 8 ) ;
2019-03-10 03:34:41 +08:00
// set epurse of sim2,4 attack
if ( reader_mac_buf ! = NULL ) {
memcpy ( reader_mac_buf , card_challenge_data , 8 ) ;
}
2020-07-13 18:21:42 +08:00
if ( ( conf_block [ 5 ] & 0x80 ) = = 0x80 ) {
page_size = 256 * 8 ;
}
2020-07-04 03:33:17 +08:00
2020-07-13 18:21:42 +08:00
// From PicoPass DS:
// When the page is in personalization mode this bit is equal to 1.
// Once the application issuer has personalized and coded its dedicated areas, this bit must be set to 0:
// the page is then "in application mode".
bool personalization_mode = conf_block [ 7 ] & 0x80 ;
2020-07-04 03:33:17 +08:00
2020-08-06 14:13:29 +08:00
uint8_t block_wr_lock = conf_block [ 3 ] ;
2020-08-13 18:25:04 +08:00
2020-07-13 18:21:42 +08:00
// chip memory may be divided in 8 pages
uint8_t max_page = ( ( conf_block [ 4 ] & 0x10 ) = = 0x10 ) ? 0 : 7 ;
2020-07-04 03:33:17 +08:00
2020-08-12 05:46:11 +08:00
// pre-calculate the cipher states, feeding it the CC
2020-07-13 18:21:42 +08:00
cipher_state_KD [ 0 ] = opt_doTagMAC_1 ( card_challenge_data , diversified_kd ) ;
cipher_state_KC [ 0 ] = opt_doTagMAC_1 ( card_challenge_data , diversified_kc ) ;
2020-07-04 03:33:17 +08:00
2020-07-13 18:21:42 +08:00
if ( simulationMode = = ICLASS_SIM_MODE_FULL ) {
2020-07-04 03:33:17 +08:00
2020-07-13 18:21:42 +08:00
for ( int i = 1 ; i < max_page ; i + + ) {
2020-07-04 03:33:17 +08:00
2020-07-13 18:21:42 +08:00
uint8_t * epurse = emulator + ( i * page_size ) + ( 8 * 2 ) ;
uint8_t * kd = emulator + ( i * page_size ) + ( 8 * 3 ) ;
uint8_t * kc = emulator + ( i * page_size ) + ( 8 * 4 ) ;
2020-07-04 03:33:17 +08:00
2020-07-13 18:21:42 +08:00
cipher_state_KD [ i ] = opt_doTagMAC_1 ( epurse , kd ) ;
cipher_state_KC [ i ] = opt_doTagMAC_1 ( epurse , kc ) ;
}
}
2020-07-04 03:33:17 +08:00
// Anti-collision process:
2019-03-10 03:34:41 +08:00
// Reader 0a
// Tag 0f
// Reader 0c
// Tag anticoll. CSN
// Reader 81 anticoll. CSN
// Tag CSN
2019-08-11 00:33:06 +08:00
uint8_t * modulated_response = NULL ;
2019-03-10 03:34:41 +08:00
int modulated_response_size = 0 ;
2019-03-10 07:00:59 +08:00
uint8_t * trace_data = NULL ;
2019-03-10 03:34:41 +08:00
int trace_data_size = 0 ;
// Respond SOF -- takes 1 bytes
2020-08-03 23:42:05 +08:00
uint8_t * resp_sof = BigBuf_malloc ( 1 ) ;
2020-07-04 03:33:17 +08:00
int resp_sof_len ;
2019-03-10 03:34:41 +08:00
// Anticollision CSN (rotated CSN)
// 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte)
2020-08-03 23:42:05 +08:00
uint8_t * resp_anticoll = BigBuf_malloc ( 22 ) ;
2019-03-10 03:34:41 +08:00
int resp_anticoll_len ;
2020-08-03 23:42:05 +08:00
// CSN (block 0)
2019-03-10 03:34:41 +08:00
// 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte)
2020-08-03 23:42:05 +08:00
uint8_t * resp_csn = BigBuf_malloc ( 22 ) ;
2019-03-10 03:34:41 +08:00
int resp_csn_len ;
2020-07-04 03:33:17 +08:00
// configuration (blk 1) PICOPASS 2ks
2020-08-03 23:42:05 +08:00
uint8_t * resp_conf = BigBuf_malloc ( 22 ) ;
2019-03-10 03:34:41 +08:00
int resp_conf_len ;
2020-07-06 21:16:00 +08:00
// e-Purse (blk 2)
2019-03-10 03:34:41 +08:00
// 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit)
2020-08-03 23:42:05 +08:00
uint8_t * resp_cc = BigBuf_malloc ( 18 ) ;
2019-03-10 03:34:41 +08:00
int resp_cc_len ;
2020-07-13 18:21:42 +08:00
// Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only
uint8_t * resp_ff = BigBuf_malloc ( 22 ) ;
int resp_ff_len ;
uint8_t ff_data [ 10 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0x00 , 0x00 } ;
AddCrc ( ff_data , 8 ) ;
2020-07-04 03:33:17 +08:00
2020-07-06 21:16:00 +08:00
// Application Issuer Area (blk 5)
2020-08-03 23:42:05 +08:00
uint8_t * resp_aia = BigBuf_malloc ( 22 ) ;
2019-03-10 03:34:41 +08:00
int resp_aia_len ;
// receive command
uint8_t * receivedCmd = BigBuf_malloc ( MAX_FRAME_SIZE ) ;
// Prepare card messages
2020-07-13 23:56:19 +08:00
tosend_t * ts = get_tosend ( ) ;
2019-03-10 03:34:41 +08:00
// First card answer: SOF
CodeIClassTagSOF ( ) ;
2020-07-13 23:56:19 +08:00
memcpy ( resp_sof , ts - > buf , ts - > max ) ;
resp_sof_len = ts - > max ;
2019-03-10 03:34:41 +08:00
// Anticollision CSN
2020-07-04 03:33:17 +08:00
CodeIso15693AsTag ( anticoll_data , sizeof ( anticoll_data ) ) ;
2020-07-13 23:56:19 +08:00
memcpy ( resp_anticoll , ts - > buf , ts - > max ) ;
resp_anticoll_len = ts - > max ;
2019-03-10 03:34:41 +08:00
2020-07-04 03:33:17 +08:00
// CSN (block 0)
CodeIso15693AsTag ( csn_data , sizeof ( csn_data ) ) ;
2020-07-13 23:56:19 +08:00
memcpy ( resp_csn , ts - > buf , ts - > max ) ;
resp_csn_len = ts - > max ;
2019-03-10 03:34:41 +08:00
2020-07-04 03:33:17 +08:00
// Configuration (block 1)
CodeIso15693AsTag ( conf_block , sizeof ( conf_block ) ) ;
2020-07-13 23:56:19 +08:00
memcpy ( resp_conf , ts - > buf , ts - > max ) ;
resp_conf_len = ts - > max ;
2019-03-10 03:34:41 +08:00
2020-07-04 03:33:17 +08:00
// e-Purse (block 2)
CodeIso15693AsTag ( card_challenge_data , sizeof ( card_challenge_data ) ) ;
2020-07-13 23:56:19 +08:00
memcpy ( resp_cc , ts - > buf , ts - > max ) ;
resp_cc_len = ts - > max ;
2019-03-10 03:34:41 +08:00
2020-07-13 18:21:42 +08:00
// Kd, Kc (blocks 3 and 4)
CodeIso15693AsTag ( ff_data , sizeof ( ff_data ) ) ;
2020-07-13 23:56:19 +08:00
memcpy ( resp_ff , ts - > buf , ts - > max ) ;
resp_ff_len = ts - > max ;
2020-07-04 03:33:17 +08:00
// Application Issuer Area (block 5)
CodeIso15693AsTag ( aia_data , sizeof ( aia_data ) ) ;
2020-07-13 23:56:19 +08:00
memcpy ( resp_aia , ts - > buf , ts - > max ) ;
resp_aia_len = ts - > max ;
2019-03-10 03:34:41 +08:00
//This is used for responding to READ-block commands or other data which is dynamically generated
//First the 'trace'-data, not encoded for FPGA
2020-08-03 23:42:05 +08:00
uint8_t * data_generic_trace = BigBuf_malloc ( 34 ) ; // 32 bytes data + 2byte CRC is max tag answer
2019-03-10 03:34:41 +08:00
//Then storage for the modulated data
//Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes)
2020-08-03 23:42:05 +08:00
uint8_t * data_response = BigBuf_malloc ( ( 34 * 2 ) + 3 ) ;
2019-08-06 00:01:11 +08:00
2020-07-13 18:21:42 +08:00
enum { IDLE , ACTIVATED , SELECTED , HALTED } chip_state = IDLE ;
2019-03-10 03:34:41 +08:00
2020-07-04 03:33:17 +08:00
bool button_pressed = false ;
2019-09-11 19:09:08 +08:00
uint8_t cmd , options , block ;
2020-07-04 03:33:17 +08:00
int len = 0 ;
2020-07-20 02:45:47 +08:00
bool exit_loop = false ;
2020-08-08 17:40:06 +08:00
bool using_kc = false ;
int kc_attempt = 0 ;
2020-07-06 21:16:00 +08:00
while ( exit_loop = = false ) {
2019-03-10 03:34:41 +08:00
WDT_HIT ( ) ;
2020-08-03 23:42:05 +08:00
// Now look at the reader command and provide appropriate responses
// default is no response:
modulated_response = NULL ;
modulated_response_size = 0 ;
trace_data = NULL ;
trace_data_size = 0 ;
2020-08-13 18:25:04 +08:00
2020-07-13 18:21:42 +08:00
uint32_t reader_eof_time = 0 ;
len = GetIso15693CommandFromReader ( receivedCmd , MAX_FRAME_SIZE , & reader_eof_time ) ;
2020-07-04 03:33:17 +08:00
if ( len < 0 ) {
button_pressed = true ;
2020-07-06 21:16:00 +08:00
exit_loop = true ;
2019-03-10 03:34:41 +08:00
continue ;
}
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
// extra response data
2019-09-11 19:09:08 +08:00
cmd = receivedCmd [ 0 ] & 0xF ;
options = ( receivedCmd [ 0 ] > > 4 ) & 0xFF ;
block = receivedCmd [ 1 ] ;
2020-07-04 03:33:17 +08:00
if ( cmd = = ICLASS_CMD_ACTALL & & len = = 1 ) { // 0x0A
2020-07-14 05:27:16 +08:00
// Reader in anti collision phase
2020-08-03 23:42:05 +08:00
modulated_response = resp_sof ;
modulated_response_size = resp_sof_len ;
chip_state = ACTIVATED ;
2020-07-14 05:27:16 +08:00
goto send ;
2020-08-13 18:25:04 +08:00
2020-07-14 05:27:16 +08:00
} else if ( cmd = = ICLASS_CMD_READ_OR_IDENTIFY & & len = = 1 ) { // 0x0C
// Reader asks for anti collision CSN
if ( chip_state = = SELECTED | | chip_state = = ACTIVATED ) {
modulated_response = resp_anticoll ;
modulated_response_size = resp_anticoll_len ;
trace_data = anticoll_data ;
trace_data_size = sizeof ( anticoll_data ) ;
}
goto send ;
2020-08-03 23:42:05 +08:00
} else if ( cmd = = ICLASS_CMD_SELECT & & len = = 9 ) {
// Reader selects anticollision CSN.
// Tag sends the corresponding real CSN
if ( chip_state = = ACTIVATED | | chip_state = = SELECTED ) {
if ( ! memcmp ( receivedCmd + 1 , anticoll_data , 8 ) ) {
modulated_response = resp_csn ;
modulated_response_size = resp_csn_len ;
trace_data = csn_data ;
trace_data_size = sizeof ( csn_data ) ;
chip_state = SELECTED ;
} else {
chip_state = IDLE ;
}
} else if ( chip_state = = HALTED | | chip_state = = IDLE ) {
// RESELECT with CSN
if ( ! memcmp ( receivedCmd + 1 , csn_data , 8 ) ) {
modulated_response = resp_csn ;
modulated_response_size = resp_csn_len ;
trace_data = csn_data ;
trace_data_size = sizeof ( csn_data ) ;
chip_state = SELECTED ;
}
}
2020-07-21 16:50:23 +08:00
goto send ;
2020-07-14 05:27:16 +08:00
} else if ( cmd = = ICLASS_CMD_READ_OR_IDENTIFY & & len = = 4 ) { // 0x0C
2020-07-04 03:33:17 +08:00
2020-07-14 05:27:16 +08:00
if ( chip_state ! = SELECTED ) {
goto send ;
}
2020-08-03 23:42:05 +08:00
if ( simulationMode = = ICLASS_SIM_MODE_EXIT_AFTER_MAC ) {
// provide defaults for blocks 0 ... 5
2020-08-13 18:25:04 +08:00
2020-08-03 23:42:05 +08:00
// block0,1,2,5 is always readable.
switch ( block ) {
case 0 : { // csn (0c 00)
modulated_response = resp_csn ;
modulated_response_size = resp_csn_len ;
trace_data = csn_data ;
trace_data_size = sizeof ( csn_data ) ;
goto send ;
2020-07-14 05:27:16 +08:00
}
2020-08-03 23:42:05 +08:00
case 1 : { // configuration (0c 01)
modulated_response = resp_conf ;
modulated_response_size = resp_conf_len ;
trace_data = conf_block ;
trace_data_size = sizeof ( conf_block ) ;
goto send ;
}
case 2 : { // e-purse (0c 02)
modulated_response = resp_cc ;
modulated_response_size = resp_cc_len ;
trace_data = card_challenge_data ;
trace_data_size = sizeof ( card_challenge_data ) ;
// set epurse of sim2,4 attack
if ( reader_mac_buf ! = NULL ) {
memcpy ( reader_mac_buf , card_challenge_data , 8 ) ;
}
goto send ;
}
case 3 :
case 4 : { // Kd, Kc, always respond with 0xff bytes
modulated_response = resp_ff ;
modulated_response_size = resp_ff_len ;
trace_data = ff_data ;
trace_data_size = sizeof ( ff_data ) ;
goto send ;
}
case 5 : { // Application Issuer Area (0c 05)
modulated_response = resp_aia ;
modulated_response_size = resp_aia_len ;
trace_data = aia_data ;
trace_data_size = sizeof ( aia_data ) ;
goto send ;
}
} // switch
} else if ( simulationMode = = ICLASS_SIM_MODE_FULL ) {
if ( block = = 3 | | block = = 4 ) { // Kd, Kc, always respond with 0xff bytes
2020-07-14 05:27:16 +08:00
modulated_response = resp_ff ;
modulated_response_size = resp_ff_len ;
trace_data = ff_data ;
trace_data_size = sizeof ( ff_data ) ;
2020-08-03 23:42:05 +08:00
} else { // use data from emulator memory
2020-08-08 17:40:06 +08:00
memcpy ( data_generic_trace , emulator + ( current_page * page_size ) + ( block * 8 ) , 8 ) ;
2020-08-03 23:42:05 +08:00
AddCrc ( data_generic_trace , 8 ) ;
trace_data = data_generic_trace ;
trace_data_size = 10 ;
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
memcpy ( data_response , ts - > buf , ts - > max ) ;
modulated_response = data_response ;
modulated_response_size = ts - > max ;
2020-07-14 05:27:16 +08:00
}
2020-08-03 23:42:05 +08:00
goto send ;
}
2019-08-11 00:33:06 +08:00
2020-08-03 23:42:05 +08:00
} else if ( cmd = = ICLASS_CMD_READCHECK & & block = = 0x02 & & len = = 2 ) { // 0x88
2019-09-11 19:09:08 +08:00
// Read e-purse KD (88 02) KC (18 02)
2020-07-14 05:27:16 +08:00
if ( chip_state ! = SELECTED ) {
2020-07-06 21:16:00 +08:00
goto send ;
2020-07-13 18:21:42 +08:00
}
2020-07-06 21:16:00 +08:00
2020-08-03 23:42:05 +08:00
// debit key
2020-08-13 18:25:04 +08:00
if ( receivedCmd [ 0 ] = = 0x88 ) {
2020-07-14 05:27:16 +08:00
cipher_state = & cipher_state_KD [ current_page ] ;
diversified_key = diversified_kd ;
2020-08-08 17:40:06 +08:00
using_kc = false ;
2020-07-14 05:27:16 +08:00
} else {
cipher_state = & cipher_state_KC [ current_page ] ;
diversified_key = diversified_kc ;
2020-08-08 17:40:06 +08:00
using_kc = true ;
2020-07-14 05:27:16 +08:00
}
modulated_response = resp_cc ;
modulated_response_size = resp_cc_len ;
trace_data = card_challenge_data ;
trace_data_size = sizeof ( card_challenge_data ) ;
goto send ;
2020-08-13 18:25:04 +08:00
} else if ( cmd = = ICLASS_CMD_CHECK & & len = = 9 ) { // 0x05
2020-07-14 05:27:16 +08:00
2019-03-10 03:34:41 +08:00
// Reader random and reader MAC!!!
2020-07-14 05:27:16 +08:00
if ( chip_state ! = SELECTED ) {
goto send ;
}
2020-07-04 03:33:17 +08:00
if ( simulationMode = = ICLASS_SIM_MODE_FULL ) {
2019-03-10 03:34:41 +08:00
// NR, from reader, is in receivedCmd +1
2020-07-06 21:16:00 +08:00
opt_doTagMAC_2 ( * cipher_state , receivedCmd + 1 , data_generic_trace , diversified_key ) ;
2019-03-10 03:34:41 +08:00
2020-08-20 04:37:37 +08:00
/*
2020-08-12 05:46:11 +08:00
uint8_t _mac [ 4 ] = { 0 } ;
opt_doReaderMAC_2 ( * cipher_state , receivedCmd + 1 , _mac , diversified_key ) ;
if ( _mac [ 0 ] ! = receivedCmd [ 5 ] | | _mac [ 1 ] ! = receivedCmd [ 6 ] | | _mac [ 2 ] ! = receivedCmd [ 7 ] | | _mac [ 3 ] ! = receivedCmd [ 8 ] ) {
2020-08-13 18:25:04 +08:00
Dbprintf ( " reader auth " _RED_ ( " failed " ) ) ;
2020-08-12 05:46:11 +08:00
Dbprintf ( " hf iclass lookup u %02x%02x%02x%02x%02x%02x%02x%02x p %02x%02x%02x%02x%02x%02x%02x%02x m %02x%02x%02x%02x%02x%02x%02x%02x f iclass_default_keys.dic " ,
2020-08-13 18:25:04 +08:00
csn_data [ 0 ] , csn_data [ 1 ] , csn_data [ 2 ] , csn_data [ 3 ] , csn_data [ 4 ] , csn_data [ 5 ] , csn_data [ 6 ] , csn_data [ 7 ] ,
card_challenge_data [ 0 ] , card_challenge_data [ 1 ] , card_challenge_data [ 2 ] , card_challenge_data [ 3 ] ,
card_challenge_data [ 4 ] , card_challenge_data [ 5 ] , card_challenge_data [ 6 ] , card_challenge_data [ 7 ] ,
receivedCmd [ 1 ] , receivedCmd [ 2 ] , receivedCmd [ 3 ] , receivedCmd [ 4 ] ,
receivedCmd [ 5 ] , receivedCmd [ 6 ] , receivedCmd [ 7 ] , receivedCmd [ 8 ]
) ;
2020-08-12 05:46:11 +08:00
goto send ;
}
2020-08-20 04:37:37 +08:00
*/
2020-08-12 05:46:11 +08:00
2019-03-10 03:34:41 +08:00
trace_data = data_generic_trace ;
trace_data_size = 4 ;
2020-07-06 21:16:00 +08:00
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
2020-07-13 23:56:19 +08:00
memcpy ( data_response , ts - > buf , ts - > max ) ;
2019-03-10 03:34:41 +08:00
modulated_response = data_response ;
2020-07-13 23:56:19 +08:00
modulated_response_size = ts - > max ;
2020-08-08 17:40:06 +08:00
if ( using_kc )
kc_attempt + + ;
2019-03-10 03:34:41 +08:00
} else {
// Not fullsim, we don't respond
2020-07-14 05:27:16 +08:00
chip_state = HALTED ;
2019-03-10 03:34:41 +08:00
2020-07-14 05:27:16 +08:00
if ( simulationMode = = ICLASS_SIM_MODE_EXIT_AFTER_MAC ) {
2019-03-10 03:34:41 +08:00
2019-06-06 16:05:09 +08:00
if ( DBGLEVEL = = DBG_EXTENDED ) {
2020-07-14 05:27:16 +08:00
Dbprintf ( " CSN: %02x %02x %02x %02x %02x %02x %02x %02x " , csn [ 0 ] , csn [ 1 ] , csn [ 2 ] , csn [ 3 ] , csn [ 4 ] , csn [ 5 ] , csn [ 6 ] , csn [ 7 ] ) ;
Dbprintf ( " RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x " , len ,
2019-03-10 07:00:59 +08:00
receivedCmd [ 0 ] , receivedCmd [ 1 ] , receivedCmd [ 2 ] ,
receivedCmd [ 3 ] , receivedCmd [ 4 ] , receivedCmd [ 5 ] ,
receivedCmd [ 6 ] , receivedCmd [ 7 ] , receivedCmd [ 8 ] ) ;
2019-03-10 03:34:41 +08:00
} else {
2020-07-14 05:27:16 +08:00
Dbprintf ( " CSN: %02x .... %02x OK " , csn [ 0 ] , csn [ 7 ] ) ;
2019-03-10 03:34:41 +08:00
}
if ( reader_mac_buf ! = NULL ) {
2020-07-06 21:16:00 +08:00
// save NR and MAC for sim 2,4
2019-03-10 07:00:59 +08:00
memcpy ( reader_mac_buf + 8 , receivedCmd + 1 , 8 ) ;
2019-03-10 03:34:41 +08:00
}
2020-07-06 21:16:00 +08:00
exit_loop = true ;
2019-03-10 03:34:41 +08:00
}
}
goto send ;
2020-07-06 21:16:00 +08:00
2019-09-11 19:09:08 +08:00
} else if ( cmd = = ICLASS_CMD_HALT & & options = = 0 & & len = = 1 ) {
2020-07-04 03:33:17 +08:00
2020-07-14 05:27:16 +08:00
if ( chip_state ! = SELECTED ) {
2020-07-04 03:33:17 +08:00
goto send ;
}
2020-07-14 05:27:16 +08:00
// Reader ends the session
modulated_response = resp_sof ;
modulated_response_size = resp_sof_len ;
chip_state = HALTED ;
goto send ;
2020-07-06 21:16:00 +08:00
2020-07-04 03:33:17 +08:00
} else if ( simulationMode = = ICLASS_SIM_MODE_FULL & & cmd = = ICLASS_CMD_READ4 & & len = = 4 ) { // 0x06
2020-07-14 05:27:16 +08:00
if ( chip_state ! = SELECTED ) {
2020-07-04 03:33:17 +08:00
goto send ;
}
2020-07-14 05:27:16 +08:00
//Read block
2020-08-03 23:42:05 +08:00
memcpy ( data_generic_trace , emulator + ( current_page * page_size ) + ( block * 8 ) , 32 ) ;
AddCrc ( data_generic_trace , 32 ) ;
2020-07-14 05:27:16 +08:00
trace_data = data_generic_trace ;
trace_data_size = 34 ;
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
2020-07-21 16:50:23 +08:00
memcpy ( data_response , ts - > buf , ts - > max ) ;
modulated_response = data_response ;
2020-07-14 05:27:16 +08:00
modulated_response_size = ts - > max ;
goto send ;
2020-07-06 21:16:00 +08:00
2020-07-21 16:50:23 +08:00
} else if ( cmd = = ICLASS_CMD_UPDATE & & ( len = = 12 | | len = = 14 ) ) {
2020-07-06 21:16:00 +08:00
2020-07-13 18:21:42 +08:00
// We're expected to respond with the data+crc, exactly what's already in the receivedCmd
// receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b
2020-07-14 05:27:16 +08:00
if ( chip_state ! = SELECTED ) {
goto send ;
}
2020-08-06 14:13:29 +08:00
// is chip in ReadOnly (RO)
if ( ( block_wr_lock & 0x80 ) = = 0 ) goto send ;
2020-08-13 18:25:04 +08:00
if ( block = = 12 & & ( block_wr_lock & 0x40 ) = = 0 ) goto send ;
if ( block = = 11 & & ( block_wr_lock & 0x20 ) = = 0 ) goto send ;
if ( block = = 10 & & ( block_wr_lock & 0x10 ) = = 0 ) goto send ;
if ( block = = 9 & & ( block_wr_lock & 0x08 ) = = 0 ) goto send ;
if ( block = = 8 & & ( block_wr_lock & 0x04 ) = = 0 ) goto send ;
if ( block = = 7 & & ( block_wr_lock & 0x02 ) = = 0 ) goto send ;
if ( block = = 6 & & ( block_wr_lock & 0x01 ) = = 0 ) goto send ;
2020-07-14 05:27:16 +08:00
if ( block = = 2 ) { // update e-purse
memcpy ( card_challenge_data , receivedCmd + 2 , 8 ) ;
CodeIso15693AsTag ( card_challenge_data , sizeof ( card_challenge_data ) ) ;
memcpy ( resp_cc , ts - > buf , ts - > max ) ;
resp_cc_len = ts - > max ;
cipher_state_KD [ current_page ] = opt_doTagMAC_1 ( card_challenge_data , diversified_kd ) ;
cipher_state_KC [ current_page ] = opt_doTagMAC_1 ( card_challenge_data , diversified_kc ) ;
2020-07-14 21:43:54 +08:00
if ( simulationMode = = ICLASS_SIM_MODE_FULL ) {
memcpy ( emulator + ( current_page * page_size ) + ( 8 * 2 ) , card_challenge_data , 8 ) ;
2020-08-13 18:25:04 +08:00
}
2020-07-14 05:27:16 +08:00
} else if ( block = = 3 ) { // update Kd
for ( int i = 0 ; i < 8 ; i + + ) {
if ( personalization_mode ) {
diversified_kd [ i ] = receivedCmd [ 2 + i ] ;
} else {
diversified_kd [ i ] ^ = receivedCmd [ 2 + i ] ;
2020-07-13 18:21:42 +08:00
}
2020-07-14 05:27:16 +08:00
}
cipher_state_KD [ current_page ] = opt_doTagMAC_1 ( card_challenge_data , diversified_kd ) ;
if ( simulationMode = = ICLASS_SIM_MODE_FULL ) {
memcpy ( emulator + ( current_page * page_size ) + ( 8 * 3 ) , diversified_kd , 8 ) ;
}
} else if ( block = = 4 ) { // update Kc
for ( int i = 0 ; i < 8 ; i + + ) {
if ( personalization_mode ) {
diversified_kc [ i ] = receivedCmd [ 2 + i ] ;
} else {
diversified_kc [ i ] ^ = receivedCmd [ 2 + i ] ;
2020-07-13 18:21:42 +08:00
}
2020-07-06 21:16:00 +08:00
}
2020-07-14 05:27:16 +08:00
cipher_state_KC [ current_page ] = opt_doTagMAC_1 ( card_challenge_data , diversified_kc ) ;
if ( simulationMode = = ICLASS_SIM_MODE_FULL ) {
memcpy ( emulator + ( current_page * page_size ) + ( 8 * 4 ) , diversified_kc , 8 ) ;
}
} else if ( simulationMode = = ICLASS_SIM_MODE_FULL ) {
// update emulator memory
2020-07-06 21:16:00 +08:00
memcpy ( emulator + ( current_page * page_size ) + ( 8 * block ) , receivedCmd + 2 , 8 ) ;
2020-07-13 18:21:42 +08:00
}
2020-07-14 05:27:16 +08:00
memcpy ( data_generic_trace , receivedCmd + 2 , 8 ) ;
AddCrc ( data_generic_trace , 8 ) ;
trace_data = data_generic_trace ;
trace_data_size = 10 ;
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
memcpy ( data_response , ts - > buf , ts - > max ) ;
modulated_response = data_response ;
modulated_response_size = ts - > max ;
2019-03-10 03:34:41 +08:00
goto send ;
2020-07-06 21:16:00 +08:00
2020-07-14 05:27:16 +08:00
} else if ( cmd = = ICLASS_CMD_PAGESEL & & len = = 4 ) { // 0x84
2020-07-13 18:21:42 +08:00
// Pagesel,
2020-07-06 21:16:00 +08:00
// - enables to select a page in the selected chip memory and return its configuration block
2020-07-13 18:21:42 +08:00
// Chips with a single page will not answer to this command
// Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC
2020-07-14 05:27:16 +08:00
if ( chip_state ! = SELECTED ) {
goto send ;
}
2020-07-06 21:16:00 +08:00
2020-07-14 05:27:16 +08:00
if ( simulationMode = = ICLASS_SIM_MODE_FULL & & max_page > 0 ) {
2020-07-06 21:16:00 +08:00
2020-08-06 14:13:29 +08:00
// if on 2k, always ignore 3msb, & 0x1F)
uint8_t page = receivedCmd [ 1 ] & 0x1F ;
2020-08-13 18:25:04 +08:00
if ( page > max_page ) {
2020-08-06 14:13:29 +08:00
goto send ;
}
current_page = page ;
2020-07-06 21:16:00 +08:00
2020-07-14 05:27:16 +08:00
memcpy ( data_generic_trace , emulator + ( current_page * page_size ) + ( 8 * 1 ) , 8 ) ;
memcpy ( diversified_kd , emulator + ( current_page * page_size ) + ( 8 * 3 ) , 8 ) ;
memcpy ( diversified_kc , emulator + ( current_page * page_size ) + ( 8 * 4 ) , 8 ) ;
2020-07-06 21:16:00 +08:00
2020-07-14 05:27:16 +08:00
cipher_state = & cipher_state_KD [ current_page ] ;
2020-07-06 21:16:00 +08:00
2020-07-14 05:27:16 +08:00
personalization_mode = data_generic_trace [ 7 ] & 0x80 ;
2020-08-06 14:13:29 +08:00
block_wr_lock = data_generic_trace [ 3 ] ;
2020-08-13 18:25:04 +08:00
2020-07-14 05:27:16 +08:00
AddCrc ( data_generic_trace , 8 ) ;
2020-07-06 21:16:00 +08:00
2020-07-14 05:27:16 +08:00
trace_data = data_generic_trace ;
trace_data_size = 10 ;
2020-07-06 21:16:00 +08:00
2020-07-14 05:27:16 +08:00
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
memcpy ( data_response , ts - > buf , ts - > max ) ;
modulated_response = data_response ;
modulated_response_size = ts - > max ;
2020-07-13 18:21:42 +08:00
}
2020-07-14 05:27:16 +08:00
goto send ;
2020-08-13 18:25:04 +08:00
} else if ( cmd = = ICLASS_CMD_DETECT ) { // 0x0F
2020-08-08 17:40:06 +08:00
// not supported yet, ignore
2020-07-14 05:27:16 +08:00
} else if ( cmd = = 0x26 & & len = = 5 ) {
2020-07-13 18:21:42 +08:00
// standard ISO15693 INVENTORY command. Ignore.
2019-03-10 03:34:41 +08:00
} else {
// Never seen this command before
2020-07-04 03:33:17 +08:00
if ( DBGLEVEL > = DBG_EXTENDED )
2020-07-14 05:27:16 +08:00
print_result ( " Unhandled command received " , receivedCmd , len ) ;
2019-03-10 03:34:41 +08:00
}
2018-02-01 22:19:47 +08:00
2019-03-09 15:59:13 +08:00
send :
2019-03-10 03:34:41 +08:00
/**
A legit tag has about 330u s delay between reader EOT and tag SOF .
* */
if ( modulated_response_size > 0 ) {
2020-07-04 03:33:17 +08:00
uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM ;
TransmitTo15693Reader ( modulated_response , modulated_response_size , & response_time , 0 , false ) ;
2020-07-21 16:50:23 +08:00
LogTrace_ISO15693 ( trace_data , trace_data_size , response_time * 32 , ( response_time * 32 ) + ( modulated_response_size * 32 * 64 ) , NULL , false ) ;
2019-03-10 03:34:41 +08:00
}
2020-08-13 18:25:04 +08:00
2020-08-06 14:13:29 +08:00
if ( chip_state = = HALTED ) {
uint32_t wait_time = GetCountSspClk ( ) + ICLASS_READER_TIMEOUT_ACTALL ;
while ( GetCountSspClk ( ) < wait_time ) { } ;
}
2020-08-08 17:40:06 +08:00
// CC attack
// wait to trigger the reader bug, then wait 1000ms
if ( kc_attempt > 3 ) {
uint32_t wait_time = GetCountSspClk ( ) + ( 16000 * 100 ) ;
while ( GetCountSspClk ( ) < wait_time ) { } ;
kc_attempt = 0 ;
exit_loop = true ;
}
2019-03-10 03:34:41 +08:00
}
2012-06-28 21:38:40 +08:00
2019-03-10 03:34:41 +08:00
LEDsoff ( ) ;
2019-03-09 15:59:13 +08:00
2020-07-04 03:33:17 +08:00
if ( button_pressed )
2020-07-14 05:27:16 +08:00
DbpString ( " button pressed " ) ;
2019-03-09 15:59:13 +08:00
2020-07-04 03:33:17 +08:00
return button_pressed ;
2012-06-28 21:38:40 +08:00
}
2020-07-29 21:29:30 +08:00
int do_iclass_simulation_nonsec ( void ) {
// free eventually allocated BigBuf memory
BigBuf_free_keep_EM ( ) ;
uint16_t page_size = 32 * 8 ;
uint8_t current_page = 0 ;
uint8_t * emulator = BigBuf_get_EM_addr ( ) ;
uint8_t * csn = emulator ;
// CSN followed by two CRC bytes
uint8_t anticoll_data [ 10 ] = { 0 } ;
uint8_t csn_data [ 10 ] = { 0 } ;
memcpy ( csn_data , csn , sizeof ( csn_data ) ) ;
// Construct anticollision-CSN
rotateCSN ( csn_data , anticoll_data ) ;
// Compute CRC on both CSNs
AddCrc ( anticoll_data , 8 ) ;
AddCrc ( csn_data , 8 ) ;
// configuration block
uint8_t conf_block [ 10 ] = { 0x12 , 0xFF , 0xFF , 0xFF , 0x7F , 0x1F , 0xFF , 0x3C , 0x00 , 0x00 } ;
// AIA
uint8_t aia_data [ 10 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0x00 , 0x00 } ;
2020-08-13 18:25:04 +08:00
memcpy ( conf_block , emulator + ( 8 * 1 ) , 8 ) ;
2020-07-29 21:29:30 +08:00
memcpy ( aia_data , emulator + ( 8 * 2 ) , 8 ) ;
AddCrc ( conf_block , 8 ) ;
AddCrc ( aia_data , 8 ) ;
if ( ( conf_block [ 5 ] & 0x80 ) = = 0x80 ) {
page_size = 256 * 8 ;
}
// chip memory may be divided in 8 pages
uint8_t max_page = ( ( conf_block [ 4 ] & 0x10 ) = = 0x10 ) ? 0 : 7 ;
// Anti-collision process:
// Reader 0a
// Tag 0f
// Reader 0c
// Tag anticoll. CSN
// Reader 81 anticoll. CSN
// Tag CSN
uint8_t * modulated_response = NULL ;
int modulated_response_size = 0 ;
uint8_t * trace_data = NULL ;
int trace_data_size = 0 ;
// Respond SOF -- takes 1 bytes
uint8_t * resp_sof = BigBuf_malloc ( 2 ) ;
int resp_sof_len ;
// Anticollision CSN (rotated CSN)
// 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte)
uint8_t * resp_anticoll = BigBuf_malloc ( 28 ) ;
int resp_anticoll_len ;
// CSN
// 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte)
uint8_t * resp_csn = BigBuf_malloc ( 28 ) ;
int resp_csn_len ;
// configuration (blk 1) PICOPASS 2ks
uint8_t * resp_conf = BigBuf_malloc ( 28 ) ;
int resp_conf_len ;
// Application Issuer Area (blk 5)
uint8_t * resp_aia = BigBuf_malloc ( 28 ) ;
int resp_aia_len ;
// receive command
uint8_t * receivedCmd = BigBuf_malloc ( MAX_FRAME_SIZE ) ;
// Prepare card messages
tosend_t * ts = get_tosend ( ) ;
ts - > max = 0 ;
// First card answer: SOF
CodeIClassTagSOF ( ) ;
memcpy ( resp_sof , ts - > buf , ts - > max ) ;
resp_sof_len = ts - > max ;
// Anticollision CSN
CodeIso15693AsTag ( anticoll_data , sizeof ( anticoll_data ) ) ;
memcpy ( resp_anticoll , ts - > buf , ts - > max ) ;
resp_anticoll_len = ts - > max ;
// CSN (block 0)
CodeIso15693AsTag ( csn_data , sizeof ( csn_data ) ) ;
memcpy ( resp_csn , ts - > buf , ts - > max ) ;
resp_csn_len = ts - > max ;
// Configuration (block 1)
CodeIso15693AsTag ( conf_block , sizeof ( conf_block ) ) ;
memcpy ( resp_conf , ts - > buf , ts - > max ) ;
resp_conf_len = ts - > max ;
2020-08-03 23:42:05 +08:00
// Application Issuer Area (block 2)
2020-07-29 21:29:30 +08:00
CodeIso15693AsTag ( aia_data , sizeof ( aia_data ) ) ;
memcpy ( resp_aia , ts - > buf , ts - > max ) ;
resp_aia_len = ts - > max ;
//This is used for responding to READ-block commands or other data which is dynamically generated
//First the 'trace'-data, not encoded for FPGA
uint8_t * data_generic_trace = BigBuf_malloc ( 32 + 2 ) ; // 32 bytes data + 2byte CRC is max tag answer
//Then storage for the modulated data
//Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes)
uint8_t * data_response = BigBuf_malloc ( ( 32 + 2 ) * 2 + 2 ) ;
enum { IDLE , ACTIVATED , SELECTED , HALTED } chip_state = IDLE ;
bool button_pressed = false ;
uint8_t cmd , options , block ;
int len = 0 ;
bool exit_loop = false ;
while ( exit_loop = = false ) {
WDT_HIT ( ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 21:29:30 +08:00
uint32_t reader_eof_time = 0 ;
len = GetIso15693CommandFromReader ( receivedCmd , MAX_FRAME_SIZE , & reader_eof_time ) ;
if ( len < 0 ) {
button_pressed = true ;
exit_loop = true ;
continue ;
}
// Now look at the reader command and provide appropriate responses
// default is no response:
modulated_response = NULL ;
modulated_response_size = 0 ;
trace_data = NULL ;
trace_data_size = 0 ;
// extra response data
cmd = receivedCmd [ 0 ] & 0xF ;
options = ( receivedCmd [ 0 ] > > 4 ) & 0xFF ;
block = receivedCmd [ 1 ] ;
if ( cmd = = ICLASS_CMD_ACTALL & & len = = 1 ) { // 0x0A
// Reader in anti collision phase
2020-08-13 18:25:04 +08:00
if ( chip_state ! = HALTED ) {
2020-07-29 21:29:30 +08:00
modulated_response = resp_sof ;
modulated_response_size = resp_sof_len ;
chip_state = ACTIVATED ;
}
goto send ;
2020-08-13 18:25:04 +08:00
2020-07-29 21:29:30 +08:00
} else if ( cmd = = ICLASS_CMD_READ_OR_IDENTIFY & & len = = 1 ) { // 0x0C
// Reader asks for anti collision CSN
if ( chip_state = = SELECTED | | chip_state = = ACTIVATED ) {
modulated_response = resp_anticoll ;
modulated_response_size = resp_anticoll_len ;
trace_data = anticoll_data ;
trace_data_size = sizeof ( anticoll_data ) ;
}
goto send ;
2020-08-03 23:42:05 +08:00
} else if ( cmd = = ICLASS_CMD_SELECT & & len = = 9 ) {
// Reader selects anticollision CSN.
// Tag sends the corresponding real CSN
if ( chip_state = = ACTIVATED | | chip_state = = SELECTED ) {
if ( ! memcmp ( receivedCmd + 1 , anticoll_data , 8 ) ) {
modulated_response = resp_csn ;
modulated_response_size = resp_csn_len ;
trace_data = csn_data ;
trace_data_size = sizeof ( csn_data ) ;
chip_state = SELECTED ;
} else {
chip_state = IDLE ;
}
} else if ( chip_state = = HALTED ) {
// RESELECT with CSN
if ( ! memcmp ( receivedCmd + 1 , csn_data , 8 ) ) {
modulated_response = resp_csn ;
modulated_response_size = resp_csn_len ;
trace_data = csn_data ;
trace_data_size = sizeof ( csn_data ) ;
chip_state = SELECTED ;
}
}
2020-07-29 21:29:30 +08:00
goto send ;
} else if ( cmd = = ICLASS_CMD_READ_OR_IDENTIFY & & len = = 4 ) { // 0x0C
if ( chip_state ! = SELECTED ) {
goto send ;
}
switch ( block ) {
case 0 : { // csn (0c 00)
modulated_response = resp_csn ;
modulated_response_size = resp_csn_len ;
trace_data = csn_data ;
trace_data_size = sizeof ( csn_data ) ;
goto send ;
}
case 1 : { // configuration (0c 01)
modulated_response = resp_conf ;
modulated_response_size = resp_conf_len ;
trace_data = conf_block ;
trace_data_size = sizeof ( conf_block ) ;
goto send ;
}
case 2 : { // Application Issuer Area (0c 02)
modulated_response = resp_aia ;
modulated_response_size = resp_aia_len ;
trace_data = aia_data ;
trace_data_size = sizeof ( aia_data ) ;
goto send ;
}
default : {
memcpy ( data_generic_trace , emulator + ( block < < 3 ) , 8 ) ;
AddCrc ( data_generic_trace , 8 ) ;
trace_data = data_generic_trace ;
trace_data_size = 10 ;
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
memcpy ( data_response , ts - > buf , ts - > max ) ;
modulated_response = data_response ;
modulated_response_size = ts - > max ;
goto send ;
}
} // swith
} else if ( cmd = = ICLASS_CMD_READCHECK ) { // 0x88
goto send ;
2020-08-13 18:25:04 +08:00
} else if ( cmd = = ICLASS_CMD_CHECK & & len = = 9 ) { // 0x05
2020-07-29 21:29:30 +08:00
goto send ;
} else if ( cmd = = ICLASS_CMD_HALT & & options = = 0 & & len = = 1 ) {
if ( chip_state ! = SELECTED ) {
goto send ;
}
// Reader ends the session
modulated_response = resp_sof ;
modulated_response_size = resp_sof_len ;
chip_state = HALTED ;
goto send ;
} else if ( cmd = = ICLASS_CMD_READ4 & & len = = 4 ) { // 0x06
if ( chip_state ! = SELECTED ) {
goto send ;
}
//Read block
memcpy ( data_generic_trace , emulator + ( current_page * page_size ) + ( block * 8 ) , 8 * 4 ) ;
AddCrc ( data_generic_trace , 8 * 4 ) ;
trace_data = data_generic_trace ;
trace_data_size = 34 ;
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
memcpy ( data_response , ts - > buf , ts - > max ) ;
modulated_response = data_response ;
modulated_response_size = ts - > max ;
goto send ;
} else if ( cmd = = ICLASS_CMD_UPDATE & & ( len = = 12 | | len = = 14 ) ) {
// We're expected to respond with the data+crc, exactly what's already in the receivedCmd
// receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b
if ( chip_state ! = SELECTED ) {
goto send ;
}
// update emulator memory
memcpy ( emulator + ( current_page * page_size ) + ( 8 * block ) , receivedCmd + 2 , 8 ) ;
memcpy ( data_generic_trace , receivedCmd + 2 , 8 ) ;
AddCrc ( data_generic_trace , 8 ) ;
trace_data = data_generic_trace ;
trace_data_size = 10 ;
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
memcpy ( data_response , ts - > buf , ts - > max ) ;
modulated_response = data_response ;
modulated_response_size = ts - > max ;
goto send ;
} else if ( cmd = = ICLASS_CMD_PAGESEL & & len = = 4 ) { // 0x84
// Pagesel,
// - enables to select a page in the selected chip memory and return its configuration block
// Chips with a single page will not answer to this command
// Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC
if ( chip_state ! = SELECTED ) {
goto send ;
}
if ( max_page > 0 ) {
current_page = receivedCmd [ 1 ] ;
memcpy ( data_generic_trace , emulator + ( current_page * page_size ) + ( 8 * 1 ) , 8 ) ;
AddCrc ( data_generic_trace , 8 ) ;
trace_data = data_generic_trace ;
trace_data_size = 10 ;
CodeIso15693AsTag ( trace_data , trace_data_size ) ;
memcpy ( data_response , ts - > buf , ts - > max ) ;
modulated_response = data_response ;
modulated_response_size = ts - > max ;
}
goto send ;
2020-08-13 18:25:04 +08:00
2020-07-29 21:29:30 +08:00
// } else if(cmd == ICLASS_CMD_DETECT) { // 0x0F
} else if ( cmd = = 0x26 & & len = = 5 ) {
// standard ISO15693 INVENTORY command. Ignore.
} else {
// Never seen this command before
if ( DBGLEVEL > = DBG_EXTENDED )
print_result ( " Unhandled command received " , receivedCmd , len ) ;
}
send :
/**
A legit tag has about 330u s delay between reader EOT and tag SOF .
* */
if ( modulated_response_size > 0 ) {
uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM ;
TransmitTo15693Reader ( modulated_response , modulated_response_size , & response_time , 0 , false ) ;
LogTrace_ISO15693 ( trace_data , trace_data_size , response_time * 32 , ( response_time * 32 ) + ( modulated_response_size * 32 * 64 ) , NULL , false ) ;
}
}
LEDsoff ( ) ;
if ( button_pressed )
DbpString ( " button pressed " ) ;
return button_pressed ;
}
2012-06-28 21:38:40 +08:00
2020-07-10 01:41:52 +08:00
// THE READER CODE
2020-07-14 04:05:38 +08:00
static void iclass_send_as_reader ( uint8_t * frame , int len , uint32_t * start_time , uint32_t * end_time ) {
2020-07-13 18:21:42 +08:00
CodeIso15693AsReader ( frame , len ) ;
2020-07-13 23:56:19 +08:00
tosend_t * ts = get_tosend ( ) ;
TransmitTo15693Tag ( ts - > buf , ts - > max , start_time ) ;
2020-07-14 04:05:38 +08:00
* end_time = * start_time + ( 32 * ( ( 8 * ts - > max ) - 4 ) ) ; // substract the 4 padding bits after EOF
2020-07-21 16:50:23 +08:00
LogTrace_ISO15693 ( frame , len , ( * start_time * 4 ) , ( * end_time * 4 ) , NULL , true ) ;
2012-06-28 21:38:40 +08:00
}
2020-08-13 18:25:04 +08:00
static bool iclass_send_cmd_with_retries ( uint8_t * cmd , size_t cmdsize , uint8_t * resp , size_t max_resp_size ,
uint8_t expected_size , uint8_t tries , uint32_t * start_time ,
uint16_t timeout , uint32_t * eof_time ) {
2020-07-04 03:33:17 +08:00
while ( tries - - > 0 ) {
CHG: added some more debugstatements,
CHG: 'hf iclass sim 2' use another e-purse value {0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff}
CHG: testing to see if we can skip the parity array when logging, we don't seem to use that information anywere anyway.
CHG: no need to check tracing global var, it is done inside of LogTrace function anyway
2017-08-21 23:22:22 +08:00
2020-07-29 17:02:30 +08:00
iclass_send_as_reader ( cmd , cmdsize , start_time , eof_time ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
if ( resp = = NULL ) {
2020-08-13 18:25:04 +08:00
return true ;
2020-07-29 17:02:30 +08:00
}
2020-07-10 01:41:52 +08:00
2020-07-13 18:21:42 +08:00
if ( expected_size = = GetIso15693AnswerFromTag ( resp , max_resp_size , timeout , eof_time ) ) {
2019-03-10 03:34:41 +08:00
return true ;
2020-07-04 03:33:17 +08:00
}
2019-03-10 03:34:41 +08:00
}
return false ;
2015-01-05 09:01:24 +08:00
}
/**
* @ brief Talks to an iclass tag , sends the commands to get CSN and CC .
2020-07-04 03:33:17 +08:00
* @ param card_data where the CSN , CONFIG , CC are stored for return
* 8 bytes csn + 8 bytes config + 8 bytes CC
* @ return false = fail
* true = Got all .
2015-01-05 09:01:24 +08:00
*/
2020-10-14 23:41:34 +08:00
static bool select_iclass_tag_ex ( picopass_hdr * hdr , bool use_credit_key , uint32_t * eof_time , uint8_t * status ) {
CHG: added some more debugstatements,
CHG: 'hf iclass sim 2' use another e-purse value {0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff}
CHG: testing to see if we can skip the parity array when logging, we don't seem to use that information anywere anyway.
CHG: no need to check tracing global var, it is done inside of LogTrace function anyway
2017-08-21 23:22:22 +08:00
2020-07-04 03:33:17 +08:00
static uint8_t act_all [ ] = { ICLASS_CMD_ACTALL } ;
static uint8_t identify [ ] = { ICLASS_CMD_READ_OR_IDENTIFY , 0x00 , 0x73 , 0x33 } ;
2020-07-13 18:21:42 +08:00
static uint8_t read_conf [ ] = { ICLASS_CMD_READ_OR_IDENTIFY , 0x01 , 0xfa , 0x22 } ;
2020-08-06 22:19:42 +08:00
uint8_t select [ ] = { 0x80 | ICLASS_CMD_SELECT , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
2020-07-29 17:02:30 +08:00
uint8_t read_aia [ ] = { ICLASS_CMD_READ_OR_IDENTIFY , 0x05 , 0xde , 0x64 } ;
2020-07-20 02:45:47 +08:00
uint8_t read_check_cc [ ] = { 0x80 | ICLASS_CMD_READCHECK , 0x02 } ;
2020-07-10 01:41:52 +08:00
uint8_t resp [ ICLASS_BUFFER_SIZE ] = { 0 } ;
CHG: added some more debugstatements,
CHG: 'hf iclass sim 2' use another e-purse value {0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff}
CHG: testing to see if we can skip the parity array when logging, we don't seem to use that information anywere anyway.
CHG: no need to check tracing global var, it is done inside of LogTrace function anyway
2017-08-21 23:22:22 +08:00
2020-07-10 01:41:52 +08:00
// Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) will be used
2019-09-13 22:25:33 +08:00
// bit 7: parity.
2019-09-12 03:14:46 +08:00
if ( use_credit_key )
2020-07-06 21:16:00 +08:00
read_check_cc [ 0 ] = 0x10 | ICLASS_CMD_READCHECK ;
2019-09-12 04:51:13 +08:00
2020-07-13 23:56:19 +08:00
// wakeup
uint32_t start_time = GetCountSspClk ( ) ;
2020-07-14 04:05:38 +08:00
iclass_send_as_reader ( act_all , 1 , & start_time , eof_time ) ;
2020-07-13 23:56:19 +08:00
int len = GetIso15693AnswerFromTag ( resp , sizeof ( resp ) , ICLASS_READER_TIMEOUT_ACTALL , eof_time ) ;
if ( len < 0 )
return false ;
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
// send Identify
start_time = * eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2020-07-14 04:05:38 +08:00
iclass_send_as_reader ( identify , 1 , & start_time , eof_time ) ;
CHG: added some more debugstatements,
CHG: 'hf iclass sim 2' use another e-purse value {0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff}
CHG: testing to see if we can skip the parity array when logging, we don't seem to use that information anywere anyway.
CHG: no need to check tracing global var, it is done inside of LogTrace function anyway
2017-08-21 23:22:22 +08:00
2020-07-04 03:33:17 +08:00
// expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC
2020-07-10 01:41:52 +08:00
len = GetIso15693AnswerFromTag ( resp , sizeof ( resp ) , ICLASS_READER_TIMEOUT_OTHERS , eof_time ) ;
2020-08-13 18:25:04 +08:00
if ( len ! = 10 )
2020-07-04 03:33:17 +08:00
return false ;
2015-01-05 09:01:24 +08:00
2020-07-04 03:33:17 +08:00
// copy the Anti-collision CSN to our select-packet
2019-03-10 03:34:41 +08:00
memcpy ( & select [ 1 ] , resp , 8 ) ;
2019-03-09 15:59:13 +08:00
2020-07-04 03:33:17 +08:00
// select the card
2020-07-13 18:21:42 +08:00
start_time = * eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2020-07-14 04:05:38 +08:00
iclass_send_as_reader ( select , sizeof ( select ) , & start_time , eof_time ) ;
2015-01-05 09:01:24 +08:00
2020-07-04 03:33:17 +08:00
// expect a 10-byte response here, 8 byte CSN and 2 byte CRC
2020-07-13 18:21:42 +08:00
len = GetIso15693AnswerFromTag ( resp , sizeof ( resp ) , ICLASS_READER_TIMEOUT_OTHERS , eof_time ) ;
if ( len ! = 10 )
2020-07-04 03:33:17 +08:00
return false ;
2019-09-13 22:25:33 +08:00
2020-08-06 22:19:42 +08:00
// save CSN
memcpy ( hdr - > csn , resp , sizeof ( hdr - > csn ) ) ;
2019-09-13 22:25:33 +08:00
2020-07-04 03:33:17 +08:00
// card selected, now read config (block1) (only 8 bytes no CRC)
2020-07-13 18:21:42 +08:00
start_time = * eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2020-07-14 04:05:38 +08:00
iclass_send_as_reader ( read_conf , sizeof ( read_conf ) , & start_time , eof_time ) ;
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
// expect a 8-byte response here
2020-07-13 18:21:42 +08:00
len = GetIso15693AnswerFromTag ( resp , sizeof ( resp ) , ICLASS_READER_TIMEOUT_OTHERS , eof_time ) ;
if ( len ! = 10 )
2020-07-04 03:33:17 +08:00
return false ;
2019-09-13 22:25:33 +08:00
2020-08-06 22:19:42 +08:00
// save CONF
2020-08-13 18:25:04 +08:00
memcpy ( ( uint8_t * ) & hdr - > conf , resp , sizeof ( hdr - > conf ) ) ;
if ( status )
* status | = ( FLAG_ICLASS_CSN | FLAG_ICLASS_CONF ) ;
2019-09-12 04:51:13 +08:00
2020-08-03 23:42:05 +08:00
uint8_t pagemap = get_pagemap ( hdr ) ;
2020-07-29 17:02:30 +08:00
if ( pagemap ! = PICOPASS_NON_SECURE_PAGEMODE ) {
2020-07-04 03:33:17 +08:00
2020-08-06 22:19:42 +08:00
// read App Issuer Area block 5
2020-07-29 17:02:30 +08:00
start_time = * eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
iclass_send_as_reader ( read_aia , sizeof ( read_aia ) , & start_time , eof_time ) ;
2020-08-13 18:25:04 +08:00
// expect a 10-byte response here
2020-07-29 17:02:30 +08:00
len = GetIso15693AnswerFromTag ( resp , sizeof ( resp ) , ICLASS_READER_TIMEOUT_OTHERS , eof_time ) ;
if ( len ! = 10 )
return false ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
if ( status ) {
* status | = FLAG_ICLASS_AIA ;
2020-08-06 22:19:42 +08:00
memcpy ( hdr - > app_issuer_area , resp , sizeof ( hdr - > app_issuer_area ) ) ;
2020-07-29 17:02:30 +08:00
}
// card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC)
start_time = * eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
iclass_send_as_reader ( read_check_cc , sizeof ( read_check_cc ) , & start_time , eof_time ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// expect a 8-byte response here
len = GetIso15693AnswerFromTag ( resp , sizeof ( resp ) , ICLASS_READER_TIMEOUT_OTHERS , eof_time ) ;
if ( len ! = 8 )
return false ;
2020-08-06 22:19:42 +08:00
memcpy ( hdr - > epurse , resp , sizeof ( hdr - > epurse ) ) ;
2020-07-29 17:02:30 +08:00
* status | = FLAG_ICLASS_CC ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
} else {
2020-10-20 07:00:23 +08:00
2020-10-14 23:41:34 +08:00
// on NON_SECURE_PAGEMODE cards, AIA is on block2..
2020-07-29 17:02:30 +08:00
2020-08-06 22:19:42 +08:00
// read App Issuer Area block 2
2020-07-29 17:02:30 +08:00
read_aia [ 1 ] = 0x02 ;
read_aia [ 2 ] = 0x61 ;
read_aia [ 3 ] = 0x10 ;
start_time = * eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
iclass_send_as_reader ( read_aia , sizeof ( read_aia ) , & start_time , eof_time ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// expect a 10-byte response here
len = GetIso15693AnswerFromTag ( resp , sizeof ( resp ) , ICLASS_READER_TIMEOUT_OTHERS , eof_time ) ;
if ( len ! = 10 )
return false ;
2020-08-13 18:25:04 +08:00
if ( status ) {
2020-07-29 17:02:30 +08:00
* status | = FLAG_ICLASS_AIA ;
2020-10-14 23:41:34 +08:00
memcpy ( hdr - > epurse , resp , sizeof ( hdr - > epurse ) ) ;
2020-07-29 17:02:30 +08:00
}
2020-08-13 18:25:04 +08:00
}
2020-07-04 03:33:17 +08:00
return true ;
2015-10-08 05:00:46 +08:00
}
2015-01-05 09:01:24 +08:00
2020-10-14 23:41:34 +08:00
bool select_iclass_tag ( picopass_hdr * hdr , bool use_credit_key , uint32_t * eof_time ) {
2020-07-29 17:02:30 +08:00
uint8_t result = 0 ;
2020-10-14 23:41:34 +08:00
return select_iclass_tag_ex ( hdr , use_credit_key , eof_time , & result ) ;
2020-07-29 17:02:30 +08:00
}
2012-06-28 21:38:40 +08:00
// Reader iClass Anticollission
2017-08-20 01:50:55 +08:00
// turn off afterwards
2020-07-06 21:16:00 +08:00
void ReaderIClass ( uint8_t flags ) {
2014-09-12 05:23:46 +08:00
2020-10-14 23:41:34 +08:00
picopass_hdr hdr = { 0 } ;
2020-07-09 06:20:52 +08:00
// uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0};
2020-07-20 02:45:47 +08:00
uint8_t resp [ ICLASS_BUFFER_SIZE ] = { 0 } ;
2019-03-10 03:34:41 +08:00
memset ( resp , 0xFF , sizeof ( resp ) ) ;
2019-03-09 15:59:13 +08:00
2020-07-29 17:02:30 +08:00
// bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully
bool use_credit_key = flags & FLAG_ICLASS_READER_CREDITKEY ; // flag to use credit key
2019-03-10 03:34:41 +08:00
2020-07-13 18:21:42 +08:00
if ( ( flags & FLAG_ICLASS_READER_INIT ) = = FLAG_ICLASS_READER_INIT ) {
Iso15693InitReader ( ) ;
}
2019-03-10 03:34:41 +08:00
2020-07-13 18:21:42 +08:00
if ( ( flags & FLAG_ICLASS_READER_CLEARTRACE ) = = FLAG_ICLASS_READER_CLEARTRACE ) {
clear_trace ( ) ;
}
2020-07-29 17:02:30 +08:00
uint8_t result_status = 0 ;
2020-07-13 18:21:42 +08:00
uint32_t eof_time = 0 ;
2020-10-14 23:41:34 +08:00
bool status = select_iclass_tag_ex ( & hdr , use_credit_key , & eof_time , & result_status ) ;
2020-07-10 01:41:52 +08:00
if ( status = = false ) {
2020-10-14 23:41:34 +08:00
reply_mix ( CMD_ACK , 0xFF , 0 , 0 , NULL , 0 ) ;
2020-07-04 03:33:17 +08:00
switch_off ( ) ;
return ;
}
2020-07-10 01:41:52 +08:00
2020-08-13 18:25:04 +08:00
// Page mapping for secure mode
2020-07-04 03:33:17 +08:00
// 0 : CSN
// 1 : Configuration
// 2 : e-purse
// 3 : kd / debit / aa2 (write-only)
// 4 : kc / credit / aa1 (write-only)
// 5 : AIA, Application issuer area
//
2020-08-13 18:25:04 +08:00
// Page mapping for non secure mode
2020-07-29 17:02:30 +08:00
// 0 : CSN
// 1 : Configuration
// 2 : AIA, Application issuer area
// Return to client, e 6 * 8 bytes of data.
2020-07-04 03:33:17 +08:00
// with 0xFF:s in block 3 and 4.
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
LED_B_ON ( ) ;
2020-10-20 07:00:23 +08:00
reply_mix ( CMD_ACK , result_status , 0 , 0 , ( uint8_t * ) & hdr , sizeof ( hdr ) ) ;
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
//Send back to client, but don't bother if we already sent this -
// only useful if looping in arm (not try_once && not abort_after_read)
2020-07-09 06:20:52 +08:00
/*
2020-07-04 03:33:17 +08:00
if ( memcmp ( last_csn , card_data , 8 ) ! = 0 ) {
2020-08-13 18:25:04 +08:00
2020-07-06 21:16:00 +08:00
reply_mix ( CMD_ACK , result_status , 0 , 0 , card_data , sizeof ( card_data ) ) ;
if ( flag_readonce ) {
LED_B_OFF ( ) ;
return ;
2019-03-10 03:34:41 +08:00
}
LED_B_OFF ( ) ;
}
2020-07-09 06:20:52 +08:00
*/
2019-03-10 03:34:41 +08:00
2020-07-07 18:32:56 +08:00
// if (userCancelled) {
// reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0);
// switch_off();
// } else {
2020-07-09 06:20:52 +08:00
// reply_mix(CMD_ACK, result_status, 0, 0, card_data, 0);
2020-07-07 18:32:56 +08:00
// }
2020-07-13 18:21:42 +08:00
2020-08-13 18:25:04 +08:00
switch_off ( ) ;
2014-09-12 05:23:46 +08:00
}
2019-03-09 15:59:13 +08:00
// used with function select_and_auth (cmdhficlass.c)
2017-08-20 01:50:55 +08:00
// which needs to authenticate before doing more things like read/write
2020-07-10 22:37:56 +08:00
// selects and authenticate to a card, sends back div_key and mac to client.
2020-07-29 17:02:30 +08:00
void iClass_Authentication ( uint8_t * msg ) {
2020-07-15 05:12:28 +08:00
}
2020-07-29 17:02:30 +08:00
bool authenticate_iclass_tag ( iclass_auth_req_t * payload , picopass_hdr * hdr , uint32_t * start_time , uint32_t * eof_time , uint8_t * mac_out ) {
2020-07-10 22:37:56 +08:00
2020-07-29 17:02:30 +08:00
uint8_t cmd_check [ 9 ] = { ICLASS_CMD_CHECK } ;
uint8_t div_key [ 8 ] = { 0 } ;
uint8_t mac [ 4 ] = { 0 } ;
uint8_t resp_auth [ 4 ] = { 0 } ;
uint8_t ccnr [ 12 ] = { 0 } ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
uint8_t * pmac = mac ;
if ( mac_out )
pmac = mac_out ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
memcpy ( ccnr , hdr - > epurse , sizeof ( hdr - > epurse ) ) ;
2020-07-10 22:37:56 +08:00
2020-10-20 07:00:23 +08:00
if ( payload - > use_replay ) {
2020-07-10 22:37:56 +08:00
2020-10-20 07:00:23 +08:00
memcpy ( pmac , payload - > key + 4 , 4 ) ;
2020-10-13 17:54:38 +08:00
memcpy ( cmd_check + 1 , payload - > key , 8 ) ;
2019-03-10 03:34:41 +08:00
2020-10-13 17:54:38 +08:00
} else {
if ( payload - > use_raw )
memcpy ( div_key , payload - > key , 8 ) ;
else
iclass_calc_div_key ( hdr - > csn , payload - > key , div_key , payload - > use_elite ) ;
2020-07-15 05:12:28 +08:00
2020-10-13 17:54:38 +08:00
if ( payload - > use_credit_key )
memcpy ( hdr - > key_c , div_key , sizeof ( hdr - > key_c ) ) ;
else
memcpy ( hdr - > key_d , div_key , sizeof ( hdr - > key_d ) ) ;
opt_doReaderMAC ( ccnr , div_key , pmac ) ;
2020-07-29 17:02:30 +08:00
2020-10-13 17:54:38 +08:00
// copy MAC to check command (readersignature)
cmd_check [ 5 ] = pmac [ 0 ] ;
cmd_check [ 6 ] = pmac [ 1 ] ;
cmd_check [ 7 ] = pmac [ 2 ] ;
cmd_check [ 8 ] = pmac [ 3 ] ;
}
2020-08-06 22:19:42 +08:00
return iclass_send_cmd_with_retries ( cmd_check , sizeof ( cmd_check ) , resp_auth , sizeof ( resp_auth ) , 4 , 2 , start_time , ICLASS_READER_TIMEOUT_OTHERS , eof_time ) ;
2015-10-08 05:00:46 +08:00
}
2017-08-20 01:50:55 +08:00
2018-01-02 18:17:31 +08:00
typedef struct iclass_premac {
2019-03-10 03:34:41 +08:00
uint8_t mac [ 4 ] ;
2018-01-02 18:17:31 +08:00
} iclass_premac_t ;
2017-12-21 17:13:40 +08:00
/* this function works on the following assumptions.
2019-03-09 15:59:13 +08:00
* - one select first , to get CSN / CC ( e - purse )
2017-12-21 17:13:40 +08:00
* - calculate before diversified keys and precalc mac based on CSN / KEY .
* - data in contains of diversified keys , mac
* - key loop only test one type of authtication key . Ie two calls needed
* to cover debit and credit key . ( AA1 / AA2 )
*/
2019-03-10 18:20:22 +08:00
void iClass_Authentication_fast ( uint64_t arg0 , uint64_t arg1 , uint8_t * datain ) {
2020-07-07 04:43:11 +08:00
2019-03-10 03:34:41 +08:00
uint8_t i = 0 , isOK = 0 ;
2020-07-20 02:45:47 +08:00
// uint8_t lastChunk = ((arg0 >> 8) & 0xFF);
2019-03-10 07:00:59 +08:00
bool use_credit_key = ( ( arg0 > > 16 ) & 0xFF ) ;
2019-03-10 03:34:41 +08:00
uint8_t keyCount = arg1 & 0xFF ;
2020-07-10 01:41:52 +08:00
uint8_t check [ 9 ] = { ICLASS_CMD_CHECK } ;
2020-07-20 02:45:47 +08:00
uint8_t resp [ ICLASS_BUFFER_SIZE ] = { 0 } ;
2019-09-11 22:24:16 +08:00
uint8_t readcheck_cc [ ] = { 0x80 | ICLASS_CMD_READCHECK , 0x02 } ;
2017-12-21 17:13:40 +08:00
2019-09-12 03:14:46 +08:00
if ( use_credit_key )
readcheck_cc [ 0 ] = 0x10 | ICLASS_CMD_READCHECK ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// select card / e-purse
2020-10-14 23:41:34 +08:00
picopass_hdr hdr = { 0 } ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
iclass_premac_t * keys = ( iclass_premac_t * ) datain ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
LED_A_ON ( ) ;
2019-03-09 15:59:13 +08:00
2020-07-13 18:21:42 +08:00
// fresh start
2019-03-10 03:34:41 +08:00
switch_off ( ) ;
SpinDelay ( 20 ) ;
2020-08-13 18:25:04 +08:00
2020-07-13 18:21:42 +08:00
Iso15693InitReader ( ) ;
2019-03-09 15:59:13 +08:00
2020-07-20 02:45:47 +08:00
uint32_t start_time = 0 , eof_time = 0 ;
2020-10-14 23:41:34 +08:00
if ( select_iclass_tag ( & hdr , use_credit_key , & eof_time ) = = false )
2020-07-07 19:18:53 +08:00
goto out ;
2020-08-13 18:25:04 +08:00
2020-07-13 18:21:42 +08:00
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2019-03-09 15:59:13 +08:00
2020-07-07 19:18:53 +08:00
// since select_iclass_tag call sends s readcheck, we start with sending first response.
uint16_t checked = 0 ;
2019-09-09 03:21:30 +08:00
2019-03-10 03:34:41 +08:00
// Keychunk loop
for ( i = 0 ; i < keyCount ; i + + ) {
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// Allow button press / usb cmd to interrupt device
2019-10-04 16:24:04 +08:00
if ( checked = = 1000 ) {
2020-07-13 18:21:42 +08:00
if ( BUTTON_PRESS ( ) | | data_available ( ) ) goto out ;
2019-08-05 00:31:52 +08:00
checked = 0 ;
}
2019-10-04 16:24:04 +08:00
+ + checked ;
2017-12-21 17:13:40 +08:00
2019-03-10 03:34:41 +08:00
WDT_HIT ( ) ;
LED_B_ON ( ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// copy MAC to check command (readersignature)
check [ 5 ] = keys [ i ] . mac [ 0 ] ;
check [ 6 ] = keys [ i ] . mac [ 1 ] ;
check [ 7 ] = keys [ i ] . mac [ 2 ] ;
check [ 8 ] = keys [ i ] . mac [ 3 ] ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
// expect 4bytes, 3 retries times..
2020-08-03 23:42:05 +08:00
isOK = iclass_send_cmd_with_retries ( check , sizeof ( check ) , resp , sizeof ( resp ) , 4 , 2 , & start_time , ICLASS_READER_TIMEOUT_OTHERS , & eof_time ) ;
2019-03-10 07:00:59 +08:00
if ( isOK )
2019-03-10 03:34:41 +08:00
goto out ;
2019-03-09 15:59:13 +08:00
2020-07-07 19:18:53 +08:00
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2019-03-10 03:34:41 +08:00
// Auth Sequence MUST begin with reading e-purse. (block2)
// Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC)
2020-07-14 04:05:38 +08:00
iclass_send_as_reader ( readcheck_cc , sizeof ( readcheck_cc ) , & start_time , & eof_time ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 03:34:41 +08:00
LED_B_OFF ( ) ;
}
2019-03-09 15:59:13 +08:00
out :
2019-03-10 03:34:41 +08:00
// send keyindex.
2020-07-13 18:21:42 +08:00
reply_mix ( CMD_HF_ICLASS_CHKKEYS , isOK , i , 0 , 0 , 0 ) ;
switch_off ( ) ;
2017-12-21 17:13:40 +08:00
}
2018-02-01 22:19:47 +08:00
// Tries to read block.
2020-08-03 23:42:05 +08:00
// retries 3times.
2020-07-29 17:02:30 +08:00
// reply 8 bytes block
2020-08-06 22:19:42 +08:00
bool iclass_read_block ( uint16_t blockno , uint8_t * data , uint32_t * start_time , uint32_t * eof_time ) {
2019-03-10 03:34:41 +08:00
uint8_t resp [ 10 ] ;
2020-07-07 18:32:56 +08:00
uint8_t c [ ] = { ICLASS_CMD_READ_OR_IDENTIFY , blockno , 0x00 , 0x00 } ;
AddCrc ( c + 1 , 1 ) ;
2020-08-06 22:19:42 +08:00
bool isOK = iclass_send_cmd_with_retries ( c , sizeof ( c ) , resp , sizeof ( resp ) , 10 , 2 , start_time , ICLASS_READER_TIMEOUT_OTHERS , eof_time ) ;
2020-07-29 17:02:30 +08:00
if ( isOK )
memcpy ( data , resp , 8 ) ;
2019-03-10 03:34:41 +08:00
return isOK ;
2015-10-08 05:00:46 +08:00
}
2012-06-28 21:38:40 +08:00
2017-08-20 01:50:55 +08:00
// turn off afterwards
2020-07-29 17:02:30 +08:00
// send in authentication needed data, if to use auth.
// reply 8 bytes block if send_reply (for client)
void iClass_ReadBlock ( uint8_t * msg ) {
2019-09-11 22:24:16 +08:00
2020-07-29 17:02:30 +08:00
iclass_auth_req_t * payload = ( iclass_auth_req_t * ) msg ;
iclass_readblock_resp_t response = { . isOK = true } ;
memset ( response . data , 0 , sizeof ( response . data ) ) ;
uint8_t cmd_read [ ] = { ICLASS_CMD_READ_OR_IDENTIFY , payload - > blockno , 0x00 , 0x00 } ;
AddCrc ( cmd_read + 1 , 1 ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
Iso15693InitReader ( ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// select tag.
uint32_t eof_time = 0 ;
picopass_hdr hdr = { 0 } ;
2020-10-14 23:41:34 +08:00
bool res = select_iclass_tag ( & hdr , payload - > use_credit_key , & eof_time ) ;
2020-07-29 17:02:30 +08:00
if ( res = = false ) {
if ( payload - > send_reply ) {
response . isOK = res ;
reply_ng ( CMD_HF_ICLASS_READBL , PM3_ETIMEOUT , ( uint8_t * ) & response , sizeof ( response ) ) ;
}
goto out ;
}
uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2020-08-13 18:25:04 +08:00
// authenticate
2020-07-29 17:02:30 +08:00
if ( payload - > do_auth ) {
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
res = authenticate_iclass_tag ( payload , & hdr , & start_time , & eof_time , NULL ) ;
if ( res = = false ) {
if ( payload - > send_reply ) {
response . isOK = res ;
reply_ng ( CMD_HF_ICLASS_READBL , PM3_ETIMEOUT , ( uint8_t * ) & response , sizeof ( response ) ) ;
}
goto out ;
}
}
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
// read data
uint8_t resp [ 10 ] ;
2020-08-03 23:42:05 +08:00
res = iclass_send_cmd_with_retries ( cmd_read , sizeof ( cmd_read ) , resp , sizeof ( resp ) , 10 , 3 , & start_time , ICLASS_READER_TIMEOUT_OTHERS , & eof_time ) ;
2020-07-29 17:02:30 +08:00
if ( res ) {
2020-08-13 18:25:04 +08:00
memcpy ( response . data , resp , sizeof ( response . data ) ) ;
if ( payload - > send_reply ) {
2020-07-29 17:02:30 +08:00
reply_ng ( CMD_HF_ICLASS_READBL , PM3_SUCCESS , ( uint8_t * ) & response , sizeof ( response ) ) ;
}
} else {
if ( payload - > send_reply ) {
response . isOK = res ;
reply_ng ( CMD_HF_ICLASS_READBL , PM3_ETIMEOUT , ( uint8_t * ) & response , sizeof ( response ) ) ;
}
}
out :
2020-08-13 18:25:04 +08:00
switch_off ( ) ;
2015-10-08 05:00:46 +08:00
}
2020-08-13 18:25:04 +08:00
2020-07-07 18:32:56 +08:00
// Dump command seems to dump a block related portion of card memory.
// I suppose it will need to do an authentatication to AA1, read its blocks by calling this.
// then authenticate AA2, and read those blocks by calling this.
// By the looks at it only 2K cards is supported, or first page dumps on larger cards.
2020-08-13 18:25:04 +08:00
// turn off afterwards
2020-07-29 17:02:30 +08:00
void iClass_Dump ( uint8_t * msg ) {
2020-08-13 18:25:04 +08:00
2020-07-13 18:21:42 +08:00
BigBuf_free ( ) ;
2020-07-07 18:32:56 +08:00
2020-08-03 23:42:05 +08:00
iclass_dump_req_t * cmd = ( iclass_dump_req_t * ) msg ;
iclass_auth_req_t * req = & cmd - > req ;
2020-08-12 05:46:11 +08:00
uint8_t * dataout = BigBuf_malloc ( ICLASS_16KS_SIZE ) ;
2019-03-10 07:00:59 +08:00
if ( dataout = = NULL ) {
2020-07-07 18:32:56 +08:00
DbpString ( " fail to allocate memory " ) ;
2020-08-03 23:42:05 +08:00
if ( req - > send_reply ) {
reply_ng ( CMD_HF_ICLASS_DUMP , PM3_EMALLOC , NULL , 0 ) ;
}
2020-07-13 18:21:42 +08:00
switch_off ( ) ;
2019-03-10 03:34:41 +08:00
return ;
}
2020-08-12 05:46:11 +08:00
memset ( dataout , 0xFF , ICLASS_16KS_SIZE ) ;
2019-03-10 03:34:41 +08:00
2020-07-29 17:02:30 +08:00
Iso15693InitReader ( ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// select tag.
uint32_t eof_time = 0 ;
picopass_hdr hdr = { 0 } ;
2020-10-14 23:41:34 +08:00
bool res = select_iclass_tag ( & hdr , req - > use_credit_key , & eof_time ) ;
2020-07-29 17:02:30 +08:00
if ( res = = false ) {
if ( req - > send_reply ) {
reply_ng ( CMD_HF_ICLASS_DUMP , PM3_ETIMEOUT , NULL , 0 ) ;
}
switch_off ( ) ;
return ;
}
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2020-08-13 18:25:04 +08:00
// authenticate
2020-07-29 17:02:30 +08:00
if ( req - > do_auth ) {
res = authenticate_iclass_tag ( req , & hdr , & start_time , & eof_time , NULL ) ;
if ( res = = false ) {
if ( req - > send_reply ) {
reply_ng ( CMD_HF_ICLASS_DUMP , PM3_ETIMEOUT , NULL , 0 ) ;
}
switch_off ( ) ;
return ;
}
}
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2020-08-03 23:42:05 +08:00
bool dumpsuccess = true ;
2020-07-29 17:02:30 +08:00
// main read loop
2020-08-06 14:13:29 +08:00
uint16_t i ;
2020-08-03 23:42:05 +08:00
for ( i = cmd - > start_block ; i < = cmd - > end_block ; i + + ) {
uint8_t resp [ 10 ] ;
uint8_t c [ ] = { ICLASS_CMD_READ_OR_IDENTIFY , i , 0x00 , 0x00 } ;
AddCrc ( c + 1 , 1 ) ;
res = iclass_send_cmd_with_retries ( c , sizeof ( c ) , resp , sizeof ( resp ) , 10 , 3 , & start_time , ICLASS_READER_TIMEOUT_OTHERS , & eof_time ) ;
if ( res ) {
memcpy ( dataout + ( 8 * i ) , resp , 8 ) ;
} else {
Dbprintf ( " failed to read block %u ( 0x%02x) " , i , i ) ;
dumpsuccess = false ;
2020-07-29 17:02:30 +08:00
}
2019-03-10 03:34:41 +08:00
}
2019-09-11 22:24:16 +08:00
2020-08-13 18:25:04 +08:00
switch_off ( ) ;
2020-07-29 17:02:30 +08:00
// copy diversified key back.
if ( req - > do_auth ) {
if ( req - > use_credit_key )
memcpy ( dataout + ( 8 * 4 ) , hdr . key_c , 8 ) ;
else
memcpy ( dataout + ( 8 * 3 ) , hdr . key_d , 8 ) ;
}
2020-07-07 18:32:56 +08:00
2020-07-29 17:02:30 +08:00
if ( req - > send_reply ) {
struct p {
bool isOK ;
2020-08-06 14:13:29 +08:00
uint16_t block_cnt ;
2020-07-29 17:02:30 +08:00
uint32_t bb_offset ;
} PACKED response ;
2020-08-03 23:42:05 +08:00
response . isOK = dumpsuccess ;
2020-07-29 17:02:30 +08:00
response . block_cnt = i ;
response . bb_offset = dataout - BigBuf_get_addr ( ) ;
reply_ng ( CMD_HF_ICLASS_DUMP , PM3_SUCCESS , ( uint8_t * ) & response , sizeof ( response ) ) ;
}
2020-08-03 23:42:05 +08:00
2019-03-10 03:34:41 +08:00
BigBuf_free ( ) ;
2015-10-08 05:00:46 +08:00
}
2020-11-09 16:36:27 +08:00
static bool iclass_writeblock_ext ( uint8_t blockno , uint8_t * data , uint8_t * mac , bool use_mac ) {
2020-07-04 03:33:17 +08:00
2020-10-14 23:41:34 +08:00
// write command: cmd, 1 blockno, 8 data, 4 mac
2020-11-09 16:36:27 +08:00
uint8_t write [ 14 ] = { 0x80 | ICLASS_CMD_UPDATE , blockno } ;
uint8_t write_len = 14 ;
2020-10-20 07:00:23 +08:00
memcpy ( write + 2 , data , 8 ) ;
2020-11-09 16:36:27 +08:00
if ( use_mac ) {
memcpy ( write + 10 , mac , 4 ) ;
} else {
AddCrc ( write + 1 , 9 ) ;
write_len - = 2 ;
}
2020-07-04 03:33:17 +08:00
2020-08-13 18:25:04 +08:00
uint8_t resp [ 10 ] = { 0 } ;
2020-07-29 17:02:30 +08:00
uint32_t eof_time = 0 , start_time = 0 ;
bool isOK = iclass_send_cmd_with_retries ( write , sizeof ( write ) , resp , sizeof ( resp ) , 10 , 3 , & start_time , ICLASS_READER_TIMEOUT_UPDATE , & eof_time ) ;
2020-07-13 18:21:42 +08:00
if ( isOK = = false ) {
return false ;
}
2020-08-13 18:25:04 +08:00
2020-07-13 18:21:42 +08:00
uint8_t all_ff [ 8 ] = { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ;
if ( blockno = = 2 ) {
2020-07-04 03:33:17 +08:00
// check response. e-purse update swaps first and second half
2020-08-13 18:25:04 +08:00
if ( memcmp ( data + 4 , resp , 4 ) | | memcmp ( data , resp + 4 , 4 ) ) {
2020-07-13 18:21:42 +08:00
return false ;
}
} else if ( blockno = = 3 | | blockno = = 4 ) {
2020-07-04 03:33:17 +08:00
// check response. Key updates always return 0xffffffffffffffff
2020-08-13 18:25:04 +08:00
if ( memcmp ( all_ff , resp , 8 ) ) {
2020-07-13 18:21:42 +08:00
return false ;
}
} else {
2020-07-04 03:33:17 +08:00
// check response. All other updates return unchanged data
2020-08-13 18:25:04 +08:00
if ( memcmp ( data , resp , 8 ) ) {
2020-07-13 18:21:42 +08:00
return false ;
}
}
2020-07-04 03:33:17 +08:00
return true ;
2015-10-08 05:00:46 +08:00
}
2017-08-20 01:50:55 +08:00
// turn off afterwards
2020-07-29 17:02:30 +08:00
void iClass_WriteBlock ( uint8_t * msg ) {
2020-08-13 18:25:04 +08:00
2020-07-13 18:21:42 +08:00
LED_A_ON ( ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
iclass_writeblock_req_t * payload = ( iclass_writeblock_req_t * ) msg ;
2020-11-09 16:36:27 +08:00
uint8_t write [ 14 ] = { 0x80 | ICLASS_CMD_UPDATE , payload - > req . blockno } ;
uint8_t write_len = 14 ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
Iso15693InitReader ( ) ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
// select tag.
uint32_t eof_time = 0 ;
picopass_hdr hdr = { 0 } ;
2020-10-14 23:41:34 +08:00
bool res = select_iclass_tag ( & hdr , payload - > req . use_credit_key , & eof_time ) ;
2020-07-29 17:02:30 +08:00
if ( res = = false ) {
goto out ;
}
uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
uint8_t mac [ 4 ] = { 0 } ;
2020-08-13 18:25:04 +08:00
// authenticate
2020-07-29 17:02:30 +08:00
if ( payload - > req . do_auth ) {
res = authenticate_iclass_tag ( & payload - > req , & hdr , & start_time , & eof_time , mac ) ;
if ( res = = false ) {
goto out ;
}
}
2020-11-09 16:36:27 +08:00
// new block data
memcpy ( write + 2 , payload - > data , 8 ) ;
2020-11-23 08:39:54 +08:00
2020-11-09 16:36:27 +08:00
uint8_t pagemap = get_pagemap ( & hdr ) ;
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
// Unsecured tags uses CRC16, but don't include the UPDATE operation code
// byte0 = update op
// byte1 = block no
// byte2..9 = new block data
AddCrc ( write + 1 , 9 ) ;
write_len - = 2 ;
2020-10-13 17:54:38 +08:00
} else {
2020-11-09 16:36:27 +08:00
// Secure tags uses MAC
uint8_t wb [ 9 ] ;
wb [ 0 ] = payload - > req . blockno ;
memcpy ( wb + 1 , payload - > data , 8 ) ;
2020-10-13 17:54:38 +08:00
if ( payload - > req . use_credit_key )
doMAC_N ( wb , sizeof ( wb ) , hdr . key_c , mac ) ;
else
doMAC_N ( wb , sizeof ( wb ) , hdr . key_d , mac ) ;
2020-08-13 18:25:04 +08:00
2020-11-09 16:36:27 +08:00
memcpy ( write + 10 , mac , sizeof ( mac ) ) ;
}
2020-07-29 17:02:30 +08:00
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
2020-08-13 18:25:04 +08:00
2020-07-29 17:02:30 +08:00
uint8_t resp [ 10 ] = { 0 } ;
2020-10-14 04:43:28 +08:00
uint8_t tries = 3 ;
while ( tries - - > 0 ) {
2020-11-09 16:36:27 +08:00
iclass_send_as_reader ( write , write_len , & start_time , & eof_time ) ;
2020-10-14 04:43:28 +08:00
if ( tearoff_hook ( ) = = PM3_ETEAROFF ) { // tearoff occured
2020-10-20 07:00:23 +08:00
res = false ;
switch_off ( ) ;
if ( payload - > req . send_reply )
reply_ng ( CMD_HF_ICLASS_WRITEBL , PM3_ETEAROFF , ( uint8_t * ) & res , sizeof ( uint8_t ) ) ;
return ;
2020-10-14 04:43:28 +08:00
} else {
if ( GetIso15693AnswerFromTag ( resp , sizeof ( resp ) , ICLASS_READER_TIMEOUT_UPDATE , & eof_time ) = = 10 ) {
res = true ;
break ;
}
}
}
2020-10-20 07:00:23 +08:00
2020-10-14 04:43:28 +08:00
if ( tries = = 0 ) {
res = false ;
2020-07-29 17:02:30 +08:00
goto out ;
}
// verify write
uint8_t all_ff [ 8 ] = { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ;
if ( payload - > req . blockno = = 2 ) {
// check response. e-purse update swaps first and second half
2020-08-13 18:25:04 +08:00
if ( memcmp ( payload - > data + 4 , resp , 4 ) | | memcmp ( payload - > data , resp + 4 , 4 ) ) {
2020-07-29 17:02:30 +08:00
res = false ;
goto out ;
}
} else if ( payload - > req . blockno = = 3 | | payload - > req . blockno = = 4 ) {
// check response. Key updates always return 0xffffffffffffffff
2020-08-13 18:25:04 +08:00
if ( memcmp ( all_ff , resp , 8 ) ) {
2020-07-29 17:02:30 +08:00
res = false ;
goto out ;
}
} else {
// check response. All other updates return unchanged data
2020-08-13 18:25:04 +08:00
if ( memcmp ( payload - > data , resp , 8 ) ) {
2020-07-29 17:02:30 +08:00
res = false ;
goto out ;
}
}
out :
2019-03-10 03:34:41 +08:00
switch_off ( ) ;
2020-07-29 17:02:30 +08:00
if ( payload - > req . send_reply )
reply_ng ( CMD_HF_ICLASS_WRITEBL , PM3_SUCCESS , ( uint8_t * ) & res , sizeof ( uint8_t ) ) ;
2015-10-08 05:00:46 +08:00
}
2014-09-12 05:23:46 +08:00
2020-10-14 23:41:34 +08:00
void iClass_Restore ( iclass_restore_req_t * msg ) {
2020-07-29 17:02:30 +08:00
2020-10-14 23:41:34 +08:00
// sanitation
if ( msg = = NULL ) {
reply_ng ( CMD_HF_ICLASS_RESTORE , PM3_ESOFT , NULL , 0 ) ;
return ;
}
2020-08-13 18:25:04 +08:00
2020-10-14 23:41:34 +08:00
if ( msg - > item_cnt = = 0 ) {
if ( msg - > req . send_reply ) {
reply_ng ( CMD_HF_ICLASS_RESTORE , PM3_ESOFT , NULL , 0 ) ;
}
2020-10-20 07:00:23 +08:00
return ;
2020-10-14 23:41:34 +08:00
}
2020-08-13 18:25:04 +08:00
2020-07-04 03:33:17 +08:00
LED_A_ON ( ) ;
2020-10-14 23:41:34 +08:00
Iso15693InitReader ( ) ;
2020-07-04 03:33:17 +08:00
uint16_t written = 0 ;
2020-10-14 23:41:34 +08:00
uint32_t eof_time = 0 ;
picopass_hdr hdr = { 0 } ;
2020-07-04 03:33:17 +08:00
2020-10-20 07:00:23 +08:00
// select
2020-10-14 23:41:34 +08:00
bool res = select_iclass_tag ( & hdr , msg - > req . use_credit_key , & eof_time ) ;
if ( res = = false ) {
goto out ;
}
// authenticate
uint8_t mac [ 4 ] = { 0 } ;
uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER ;
// authenticate
if ( msg - > req . do_auth ) {
res = authenticate_iclass_tag ( & msg - > req , & hdr , & start_time , & eof_time , mac ) ;
if ( res = = false ) {
goto out ;
}
}
// main loop
2020-11-09 16:36:27 +08:00
bool use_mac ;
2020-10-14 23:41:34 +08:00
for ( uint8_t i = 0 ; i < msg - > item_cnt ; i + + ) {
iclass_restore_item_t item = msg - > blocks [ i ] ;
2020-11-09 16:36:27 +08:00
uint8_t pagemap = get_pagemap ( & hdr ) ;
if ( pagemap = = PICOPASS_NON_SECURE_PAGEMODE ) {
// Unsecured tags uses CRC16
use_mac = false ;
} else {
// Secure tags uses MAC
use_mac = true ;
uint8_t wb [ 9 ] = { 0 } ;
wb [ 0 ] = item . blockno ;
memcpy ( wb + 1 , item . data , 8 ) ;
if ( msg - > req . use_credit_key )
doMAC_N ( wb , sizeof ( wb ) , hdr . key_c , mac ) ;
else
doMAC_N ( wb , sizeof ( wb ) , hdr . key_d , mac ) ;
}
2020-10-20 07:00:23 +08:00
2020-10-14 23:41:34 +08:00
// data + mac
2020-11-09 16:36:27 +08:00
if ( iclass_writeblock_ext ( item . blockno , item . data , mac , use_mac ) ) {
2020-11-26 07:42:22 +08:00
Dbprintf ( " Write block [%3d/0x%02X] " _GREEN_ ( " successful " ) , item . blockno , item . blockno ) ;
2019-03-10 03:34:41 +08:00
written + + ;
} else {
2020-11-26 07:42:22 +08:00
Dbprintf ( " Write block [%3d/0x%02X] " _RED_ ( " failed " ) , item . blockno , item . blockno ) ;
2019-03-10 03:34:41 +08:00
}
}
2020-10-20 07:00:23 +08:00
2020-10-14 23:41:34 +08:00
out :
2019-09-11 22:24:16 +08:00
2019-03-10 03:34:41 +08:00
switch_off ( ) ;
2020-10-14 23:41:34 +08:00
if ( msg - > req . send_reply ) {
int isOK = ( written = = msg - > item_cnt ) ? PM3_SUCCESS : PM3_ESOFT ;
reply_ng ( CMD_HF_ICLASS_RESTORE , isOK , NULL , 0 ) ;
}
2019-03-12 07:12:26 +08:00
}