2017-11-25 10:29:26 +08:00
//-----------------------------------------------------------------------------
// Copyright (C) 2017 Merlok
2017-12-06 04:07:03 +08:00
// modified 2017 iceman
2017-11-25 10:29:26 +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.
//-----------------------------------------------------------------------------
// EMV commands
//-----------------------------------------------------------------------------
2018-11-20 22:26:54 +08:00
# include <ctype.h>
# include "mifare.h"
2017-11-25 10:29:26 +08:00
# include "cmdemv.h"
2018-11-20 22:26:54 +08:00
# include "emvjson.h"
# include "emv_pki.h"
2017-12-24 17:26:47 +08:00
# include "test/cryptotest.h"
2018-11-20 22:26:54 +08:00
# include "cliparser/cliparser.h"
# include <jansson.h>
2017-11-25 10:29:26 +08:00
2017-11-25 15:18:30 +08:00
static int CmdHelp ( const char * Cmd ) ;
2018-11-20 22:26:54 +08:00
# define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) )
void ParamLoadDefaults ( struct tlvdb * tlvRoot ) {
//9F02:(Amount, authorized (Numeric)) len:6
TLV_ADD ( 0x9F02 , " \x00 \x00 \x00 \x00 \x01 \x00 " ) ;
//9F1A:(Terminal Country Code) len:2
TLV_ADD ( 0x9F1A , " ru " ) ;
//5F2A:(Transaction Currency Code) len:2
// USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999
TLV_ADD ( 0x5F2A , " \x09 \x80 " ) ;
//9A:(Transaction Date) len:3
TLV_ADD ( 0x9A , " \x00 \x00 \x00 " ) ;
//9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash
TLV_ADD ( 0x9C , " \x00 " ) ;
// 9F37 Unpredictable Number len:4
TLV_ADD ( 0x9F37 , " \x01 \x02 \x03 \x04 " ) ;
// 9F6A Unpredictable Number (MSD for UDOL) len:4
TLV_ADD ( 0x9F6A , " \x01 \x02 \x03 \x04 " ) ;
//9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
TLV_ADD ( 0x9F66 , " \x26 \x00 \x00 \x00 " ) ; // qVSDC
2019-01-10 22:22:32 +08:00
//95:(Terminal Verification Results) len:5
// all OK TVR
TLV_ADD ( 0x95 , " \x00 \x00 \x00 \x00 \x00 " ) ;
2017-11-25 10:29:26 +08:00
}
2019-01-09 01:49:31 +08:00
void PrintChannel ( EMVCommandChannel channel ) {
switch ( channel ) {
case ECC_CONTACTLESS :
PrintAndLogEx ( INFO , " Channel: CONTACTLESS " ) ;
break ;
case ECC_CONTACT :
PrintAndLogEx ( INFO , " Channel: CONTACT " ) ;
break ;
}
}
2018-12-04 00:16:25 +08:00
int CmdEMVSelect ( const char * cmd ) {
2017-11-25 10:29:26 +08:00
uint8_t data [ APDU_AID_LEN ] = { 0 } ;
int datalen = 0 ;
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv select " ,
2018-11-20 22:26:54 +08:00
" Executes select applet command " ,
2018-12-04 00:21:05 +08:00
" Usage: \n \t emv select -s a00000000101 -> select card, select applet \n \t emv select -st a00000000101 -> select card, select applet, show result in TLV \n " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " sS " , " select " , " activate field and select card " ) ,
arg_lit0 ( " kK " , " keep " , " keep field for next command " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_strx0 ( NULL , NULL , " <HEX applet AID> " , NULL ) ,
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool activateField = arg_get_lit ( 1 ) ;
bool leaveSignalON = arg_get_lit ( 2 ) ;
bool APDULogging = arg_get_lit ( 3 ) ;
bool decodeTLV = arg_get_lit ( 4 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 5 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-12-04 01:29:31 +08:00
CLIGetHexWithReturn ( 6 , data , & datalen ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
2017-11-25 10:29:26 +08:00
// exec
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2018-12-04 01:29:31 +08:00
int res = EMVSelect ( channel , activateField , leaveSignalON , data , datalen , buf , sizeof ( buf ) , & len , & sw , NULL ) ;
2017-11-25 10:29:26 +08:00
if ( sw )
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2017-11-25 10:29:26 +08:00
if ( res )
return res ;
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVSearch ( const char * cmd ) {
2017-11-25 10:29:26 +08:00
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv search " ,
2018-11-20 22:26:54 +08:00
" Tries to select all applets from applet list: \n " ,
2018-12-04 00:21:05 +08:00
" Usage: \n \t emv search -s -> select card and search \n \t emv search -st -> select card, search and show result in TLV \n " ) ;
2017-11-25 10:29:26 +08:00
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " sS " , " select " , " activate field and select card " ) ,
arg_lit0 ( " kK " , " keep " , " keep field ON for next command " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results of selected applets " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool activateField = arg_get_lit ( 1 ) ;
bool leaveSignalON = arg_get_lit ( 2 ) ;
bool APDULogging = arg_get_lit ( 3 ) ;
bool decodeTLV = arg_get_lit ( 4 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 5 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
2017-11-25 10:29:26 +08:00
struct tlvdb * t = NULL ;
const char * al = " Applets list " ;
t = tlvdb_fixed ( 1 , strlen ( al ) , ( const unsigned char * ) al ) ;
2018-12-04 01:29:31 +08:00
if ( EMVSearch ( channel , activateField , leaveSignalON , decodeTLV , t ) ) {
2017-11-25 10:29:26 +08:00
tlvdb_free ( t ) ;
return 2 ;
}
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( SUCCESS , " Search completed. " ) ;
2017-11-25 10:29:26 +08:00
// print list here
if ( ! decodeTLV ) {
TLVPrintAIDlistFromSelectTLV ( t ) ;
}
tlvdb_free ( t ) ;
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVPPSE ( const char * cmd ) {
2018-11-20 22:26:54 +08:00
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv pse " ,
2018-11-20 22:26:54 +08:00
" Executes PSE/PPSE select command. It returns list of applet on the card: \n " ,
2018-12-04 00:21:05 +08:00
" Usage: \n \t emv pse -s1 -> select, get pse \n \t emv pse -st2 -> select, get ppse, show result in TLV \n " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " sS " , " select " , " activate field and select card " ) ,
arg_lit0 ( " kK " , " keep " , " keep field ON for next command " ) ,
arg_lit0 ( " 1 " , " pse " , " pse (1PAY.SYS.DDF01) mode " ) ,
arg_lit0 ( " 2 " , " ppse " , " ppse (2PAY.SYS.DDF01) mode (default mode) " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results of selected applets " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool activateField = arg_get_lit ( 1 ) ;
bool leaveSignalON = arg_get_lit ( 2 ) ;
uint8_t PSENum = 2 ;
if ( arg_get_lit ( 3 ) )
PSENum = 1 ;
if ( arg_get_lit ( 4 ) )
PSENum = 2 ;
bool APDULogging = arg_get_lit ( 5 ) ;
bool decodeTLV = arg_get_lit ( 6 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 7 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
// exec
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2018-12-04 01:29:31 +08:00
int res = EMVSelectPSE ( channel , activateField , leaveSignalON , PSENum , buf , sizeof ( buf ) , & len , & sw ) ;
2018-11-20 22:26:54 +08:00
if ( sw )
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2018-11-20 22:26:54 +08:00
if ( res )
return res ;
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
2017-11-25 10:29:26 +08:00
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVGPO ( const char * cmd ) {
2018-11-20 22:26:54 +08:00
uint8_t data [ APDU_RES_LEN ] = { 0 } ;
int datalen = 0 ;
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv gpo " ,
2018-11-20 22:26:54 +08:00
" Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1). \n Needs a EMV applet to be selected. " ,
2018-12-04 00:21:05 +08:00
" Usage: \n \t emv gpo -k -> execute GPO \n "
" \t emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV \n "
" \t emv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV \n " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " kK " , " keep " , " keep field ON for next command " ) ,
arg_lit0 ( " pP " , " params " , " load parameters from `emv/defparams.json` file for PDOLdata making from PDOL and parameters " ) ,
arg_lit0 ( " mM " , " make " , " make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters) " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results of selected applets " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_strx0 ( NULL , NULL , " <HEX PDOLdata/PDOL> " , NULL ) ,
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
2017-11-25 10:29:26 +08:00
2018-11-20 22:26:54 +08:00
bool leaveSignalON = arg_get_lit ( 1 ) ;
bool paramsLoadFromFile = arg_get_lit ( 2 ) ;
bool dataMakeFromPDOL = arg_get_lit ( 3 ) ;
bool APDULogging = arg_get_lit ( 4 ) ;
bool decodeTLV = arg_get_lit ( 5 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 6 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-12-04 01:29:31 +08:00
CLIGetHexWithReturn ( 7 , data , & datalen ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
// Init TLV tree
const char * alr = " Root terminal TLV tree " ;
struct tlvdb * tlvRoot = tlvdb_fixed ( 1 , strlen ( alr ) , ( const unsigned char * ) alr ) ;
// calc PDOL
struct tlv * pdol_data_tlv = NULL ;
struct tlv data_tlv = {
. tag = 0x83 ,
. len = datalen ,
. value = ( uint8_t * ) data ,
} ;
if ( dataMakeFromPDOL ) {
ParamLoadDefaults ( tlvRoot ) ;
if ( paramsLoadFromFile ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " Params loading from file... " ) ;
2018-11-20 22:26:54 +08:00
ParamLoadFromJson ( tlvRoot ) ;
} ;
pdol_data_tlv = dol_process ( ( const struct tlv * ) tlvdb_external ( 0x9f38 , datalen , data ) , tlvRoot , 0x83 ) ;
if ( ! pdol_data_tlv ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't create PDOL TLV. " ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvRoot ) ;
return 4 ;
}
} else {
if ( paramsLoadFromFile ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( WARNING , " Don't need to load parameters. Sending plain PDOL data... " ) ;
2017-11-25 10:29:26 +08:00
}
2018-11-20 22:26:54 +08:00
pdol_data_tlv = & data_tlv ;
2017-11-25 10:29:26 +08:00
}
2018-11-20 22:26:54 +08:00
size_t pdol_data_tlv_data_len = 0 ;
unsigned char * pdol_data_tlv_data = tlv_encode ( pdol_data_tlv , & pdol_data_tlv_data_len ) ;
if ( ! pdol_data_tlv_data ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't create PDOL data. " ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvRoot ) ;
return 4 ;
}
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " PDOL data[%d]: %s " , pdol_data_tlv_data_len , sprint_hex ( pdol_data_tlv_data , pdol_data_tlv_data_len ) ) ;
2017-11-25 10:29:26 +08:00
// exec
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2018-12-04 01:29:31 +08:00
int res = EMVGPO ( channel , leaveSignalON , pdol_data_tlv_data , pdol_data_tlv_data_len , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2018-11-20 22:26:54 +08:00
if ( pdol_data_tlv ! = & data_tlv )
free ( pdol_data_tlv ) ;
tlvdb_free ( tlvRoot ) ;
2017-11-25 10:29:26 +08:00
if ( sw )
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2017-11-25 10:29:26 +08:00
if ( res )
2018-11-20 22:26:54 +08:00
return res ;
2017-11-25 10:29:26 +08:00
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVReadRecord ( const char * cmd ) {
2018-11-20 22:26:54 +08:00
uint8_t data [ APDU_RES_LEN ] = { 0 } ;
int datalen = 0 ;
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv readrec " ,
2018-11-20 22:26:54 +08:00
" Executes Read Record command. It returns data in TLV format. \n Needs a bank applet to be selected and sometimes needs GPO to be executed. " ,
2018-12-04 00:21:05 +08:00
" Usage: \n \t emv readrec -k 0101 -> read file SFI=01, SFIrec=01 \n \t emv readrec -kt 0201-> read file 0201 and show result in TLV \n " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " kK " , " keep " , " keep field ON for next command " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results of selected applets " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-12-26 19:50:11 +08:00
arg_strx1 ( NULL , NULL , " <SFI 1byte HEX><SFIrecord 1byte HEX> " , NULL ) ,
2018-11-20 22:26:54 +08:00
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool leaveSignalON = arg_get_lit ( 1 ) ;
bool APDULogging = arg_get_lit ( 2 ) ;
bool decodeTLV = arg_get_lit ( 3 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 4 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-12-04 01:29:31 +08:00
CLIGetHexWithReturn ( 5 , data , & datalen ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
if ( datalen ! = 2 ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Command needs to have 2 bytes of data " ) ;
2018-11-20 22:26:54 +08:00
return 1 ;
}
SetAPDULogging ( APDULogging ) ;
// exec
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2018-12-04 01:29:31 +08:00
int res = EMVReadRecord ( channel , leaveSignalON , data [ 0 ] , data [ 1 ] , buf , sizeof ( buf ) , & len , & sw , NULL ) ;
2018-11-20 22:26:54 +08:00
if ( sw )
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2018-11-20 22:26:54 +08:00
if ( res )
return res ;
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVAC ( const char * cmd ) {
2018-11-20 22:26:54 +08:00
uint8_t data [ APDU_RES_LEN ] = { 0 } ;
int datalen = 0 ;
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv genac " ,
2018-11-20 22:26:54 +08:00
" Generate Application Cryptogram command. It returns data in TLV format . \n Needs a EMV applet to be selected and GPO to be executed. " ,
2018-12-04 00:21:05 +08:00
" Usage: \n \t emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command \n "
" \t emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV \n "
" \t emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined' \n "
" \t emv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " kK " , " keep " , " keep field ON for next command " ) ,
arg_lit0 ( " cC " , " cda " , " executes CDA transaction. Needs to get SDAD in results. " ) ,
arg_str0 ( " dD " , " decision " , " <aac|tc|arqc> " , " Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested " ) ,
arg_lit0 ( " pP " , " params " , " load parameters from `emv/defparams.json` file for CDOLdata making from CDOL and parameters " ) ,
arg_lit0 ( " mM " , " make " , " make CDOLdata from CDOL (tag 8C and 8D) and parameters (by default uses default parameters) " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results of selected applets " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_strx1 ( NULL , NULL , " <HEX CDOLdata/CDOL> " , NULL ) ,
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , false ) ;
bool leaveSignalON = arg_get_lit ( 1 ) ;
bool trTypeCDA = arg_get_lit ( 2 ) ;
uint8_t termDecision = 0xff ;
if ( arg_get_str_len ( 3 ) ) {
if ( ! strncmp ( arg_get_str ( 3 ) - > sval [ 0 ] , " aac " , 4 ) )
termDecision = EMVAC_AAC ;
if ( ! strncmp ( arg_get_str ( 3 ) - > sval [ 0 ] , " tc " , 4 ) )
termDecision = EMVAC_TC ;
if ( ! strncmp ( arg_get_str ( 3 ) - > sval [ 0 ] , " arqc " , 4 ) )
termDecision = EMVAC_ARQC ;
if ( termDecision = = 0xff ) {
PrintAndLog ( " ERROR: can't find terminal decision '%s' " , arg_get_str ( 3 ) - > sval [ 0 ] ) ;
return 1 ;
}
} else {
termDecision = EMVAC_TC ;
}
if ( trTypeCDA )
termDecision = termDecision | EMVAC_CDAREQ ;
bool paramsLoadFromFile = arg_get_lit ( 4 ) ;
bool dataMakeFromCDOL = arg_get_lit ( 5 ) ;
bool APDULogging = arg_get_lit ( 6 ) ;
bool decodeTLV = arg_get_lit ( 7 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 8 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-12-04 01:29:31 +08:00
CLIGetHexWithReturn ( 9 , data , & datalen ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
// Init TLV tree
const char * alr = " Root terminal TLV tree " ;
struct tlvdb * tlvRoot = tlvdb_fixed ( 1 , strlen ( alr ) , ( const unsigned char * ) alr ) ;
// calc CDOL
struct tlv * cdol_data_tlv = NULL ;
struct tlv data_tlv = {
. tag = 0x01 ,
. len = datalen ,
. value = ( uint8_t * ) data ,
} ;
if ( dataMakeFromCDOL ) {
ParamLoadDefaults ( tlvRoot ) ;
if ( paramsLoadFromFile ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " Params loading from file... " ) ;
2018-11-20 22:26:54 +08:00
ParamLoadFromJson ( tlvRoot ) ;
} ;
cdol_data_tlv = dol_process ( ( const struct tlv * ) tlvdb_external ( 0x8c , datalen , data ) , tlvRoot , 0x01 ) ; // 0x01 - dummy tag
if ( ! cdol_data_tlv ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't create CDOL TLV. " ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvRoot ) ;
return 4 ;
}
} else {
if ( paramsLoadFromFile ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( WARNING , " Don't need to load parameters. Sending plain CDOL data... " ) ;
2018-11-20 22:26:54 +08:00
}
cdol_data_tlv = & data_tlv ;
}
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " CDOL data[%d]: %s " , cdol_data_tlv - > len , sprint_hex ( cdol_data_tlv - > value , cdol_data_tlv - > len ) ) ;
2018-11-20 22:26:54 +08:00
// exec
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2018-12-04 01:29:31 +08:00
int res = EMVAC ( channel , leaveSignalON , termDecision , ( uint8_t * ) cdol_data_tlv - > value , cdol_data_tlv - > len , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2018-11-20 22:26:54 +08:00
if ( cdol_data_tlv ! = & data_tlv )
free ( cdol_data_tlv ) ;
tlvdb_free ( tlvRoot ) ;
if ( sw )
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2018-11-20 22:26:54 +08:00
if ( res )
return res ;
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVGenerateChallenge ( const char * cmd ) {
2018-11-20 22:26:54 +08:00
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv challenge " ,
2018-11-20 22:26:54 +08:00
" Executes Generate Challenge command. It returns 4 or 8-byte random number from card. \n Needs a EMV applet to be selected and GPO to be executed. " ,
2018-12-04 00:21:05 +08:00
" Usage: \n \t emv challenge -> get challenge \n \t emv challenge -k -> get challenge, keep fileld ON \n " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " kK " , " keep " , " keep field ON for next command " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool leaveSignalON = arg_get_lit ( 1 ) ;
bool APDULogging = arg_get_lit ( 2 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 3 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
// exec
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2018-12-04 01:29:31 +08:00
int res = EMVGenerateChallenge ( channel , leaveSignalON , buf , sizeof ( buf ) , & len , & sw , NULL ) ;
2018-11-20 22:26:54 +08:00
if ( sw )
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2018-11-20 22:26:54 +08:00
if ( res )
return res ;
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( SUCCESS , " Challenge: %s " , sprint_hex ( buf , len ) ) ;
2018-11-20 22:26:54 +08:00
if ( len ! = 4 & & len ! = 8 )
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( WARNING , " Length of challenge must be 4 or 8, but it %d " , len ) ;
2018-11-20 22:26:54 +08:00
2017-11-25 10:29:26 +08:00
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVInternalAuthenticate ( const char * cmd ) {
2018-11-20 22:26:54 +08:00
uint8_t data [ APDU_RES_LEN ] = { 0 } ;
int datalen = 0 ;
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv intauth " ,
2018-12-26 19:50:11 +08:00
" Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format . \n "
" Needs a EMV applet to be selected and GPO to be executed. " ,
" Usage: \n "
" \t emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command \n "
2018-12-04 00:21:05 +08:00
" \t emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV \n "
" \t emv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " kK " , " keep " , " keep field ON for next command " ) ,
arg_lit0 ( " pP " , " params " , " load parameters from `emv/defparams.json` file for DDOLdata making from DDOL and parameters " ) ,
arg_lit0 ( " mM " , " make " , " make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters) " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results of selected applets " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_strx1 ( NULL , NULL , " <HEX DDOLdata/DDOL> " , NULL ) ,
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , false ) ;
bool leaveSignalON = arg_get_lit ( 1 ) ;
bool paramsLoadFromFile = arg_get_lit ( 2 ) ;
bool dataMakeFromDDOL = arg_get_lit ( 3 ) ;
bool APDULogging = arg_get_lit ( 4 ) ;
bool decodeTLV = arg_get_lit ( 5 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 6 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-12-04 01:29:31 +08:00
CLIGetHexWithReturn ( 7 , data , & datalen ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
// Init TLV tree
const char * alr = " Root terminal TLV tree " ;
struct tlvdb * tlvRoot = tlvdb_fixed ( 1 , strlen ( alr ) , ( const unsigned char * ) alr ) ;
// calc DDOL
struct tlv * ddol_data_tlv = NULL ;
struct tlv data_tlv = {
. tag = 0x01 ,
. len = datalen ,
. value = ( uint8_t * ) data ,
} ;
if ( dataMakeFromDDOL ) {
ParamLoadDefaults ( tlvRoot ) ;
if ( paramsLoadFromFile ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " Params loading from file... " ) ;
2018-11-20 22:26:54 +08:00
ParamLoadFromJson ( tlvRoot ) ;
} ;
ddol_data_tlv = dol_process ( ( const struct tlv * ) tlvdb_external ( 0x9f49 , datalen , data ) , tlvRoot , 0x01 ) ; // 0x01 - dummy tag
if ( ! ddol_data_tlv ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't create DDOL TLV. " ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvRoot ) ;
return 4 ;
}
} else {
if ( paramsLoadFromFile ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( WARNING , " Don't need to load parameters. Sending plain DDOL data... " ) ;
2018-11-20 22:26:54 +08:00
}
ddol_data_tlv = & data_tlv ;
}
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " DDOL data[%d]: %s " , ddol_data_tlv - > len , sprint_hex ( ddol_data_tlv - > value , ddol_data_tlv - > len ) ) ;
2018-11-20 22:26:54 +08:00
// exec
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2018-12-04 01:29:31 +08:00
int res = EMVInternalAuthenticate ( channel , leaveSignalON , data , datalen , buf , sizeof ( buf ) , & len , & sw , NULL ) ;
2018-11-20 22:26:54 +08:00
if ( ddol_data_tlv ! = & data_tlv )
free ( ddol_data_tlv ) ;
tlvdb_free ( tlvRoot ) ;
if ( sw )
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2018-11-20 22:26:54 +08:00
if ( res )
return res ;
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
return 0 ;
}
2017-12-24 17:26:47 +08:00
# define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;}
2018-11-20 22:26:54 +08:00
void InitTransactionParameters ( struct tlvdb * tlvRoot , bool paramLoadJSON , enum TransactionType TrType , bool GenACGPO ) {
ParamLoadDefaults ( tlvRoot ) ;
if ( paramLoadJSON ) {
PrintAndLog ( " * * Transaction parameters loading from JSON... " ) ;
ParamLoadFromJson ( tlvRoot ) ;
}
//9F66:(Terminal Transaction Qualifiers (TTQ)) len:4
char * qVSDC = " \x26 \x00 \x00 \x00 " ;
if ( GenACGPO ) {
qVSDC = " \x26 \x80 \x00 \x00 " ;
}
switch ( TrType ) {
case TT_MSD :
TLV_ADD ( 0x9F66 , " \x86 \x00 \x00 \x00 " ) ; // MSD
break ;
// not standard for contactless. just for test.
case TT_VSDC :
TLV_ADD ( 0x9F66 , " \x46 \x00 \x00 \x00 " ) ; // VSDC
break ;
case TT_QVSDCMCHIP :
TLV_ADD ( 0x9F66 , qVSDC ) ; // qVSDC
break ;
case TT_CDA :
TLV_ADD ( 0x9F66 , qVSDC ) ; // qVSDC (VISA CDA not enabled)
break ;
default :
break ;
}
}
2017-11-25 10:29:26 +08:00
2018-11-20 22:26:54 +08:00
void ProcessGPOResponseFormat1 ( struct tlvdb * tlvRoot , uint8_t * buf , size_t len , bool decodeTLV ) {
if ( buf [ 0 ] = = 0x80 ) {
if ( decodeTLV ) {
PrintAndLog ( " GPO response format1: " ) ;
TLVPrintFromBuffer ( buf , len ) ;
}
if ( len < 4 | | ( len - 4 ) % 4 ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " GPO response format1 parsing error. length=%d " , len ) ;
2018-11-20 22:26:54 +08:00
} else {
// AIP
struct tlvdb * f1AIP = tlvdb_fixed ( 0x82 , 2 , buf + 2 ) ;
tlvdb_add ( tlvRoot , f1AIP ) ;
if ( decodeTLV ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " \n * * Decode response format 1 (0x80) AIP and AFL: " ) ;
2018-11-20 22:26:54 +08:00
TLVPrintFromTLV ( f1AIP ) ;
}
// AFL
struct tlvdb * f1AFL = tlvdb_fixed ( 0x94 , len - 4 , buf + 2 + 2 ) ;
tlvdb_add ( tlvRoot , f1AFL ) ;
if ( decodeTLV )
TLVPrintFromTLV ( f1AFL ) ;
}
} else {
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
}
}
2019-01-10 22:22:32 +08:00
void ProcessACResponseFormat1 ( struct tlvdb * tlvRoot , uint8_t * buf , size_t len , bool decodeTLV ) {
if ( buf [ 0 ] = = 0x80 ) {
if ( decodeTLV ) {
PrintAndLog ( " GPO response format1: " ) ;
TLVPrintFromBuffer ( buf , len ) ;
}
uint8_t elmlen = len - 2 ; // wo 0x80XX
if ( len < 4 + 2 | | ( elmlen - 2 ) % 4 | | elmlen ! = buf [ 1 ] ) {
PrintAndLogEx ( ERR , " GPO response format1 parsing error. length=%d " , len ) ;
} else {
struct tlvdb * tlvElm = NULL ;
if ( decodeTLV )
PrintAndLog ( " \n ------------ Format1 decoded ------------ " ) ;
// CID (Cryptogram Information Data)
tlvdb_change_or_add_node_ex ( tlvRoot , 0x9f27 , 1 , & buf [ 2 ] , & tlvElm ) ;
if ( decodeTLV )
TLVPrintFromTLV ( tlvElm ) ;
// ATC (Application Transaction Counter)
tlvdb_change_or_add_node_ex ( tlvRoot , 0x9f36 , 2 , & buf [ 3 ] , & tlvElm ) ;
if ( decodeTLV )
TLVPrintFromTLV ( tlvElm ) ;
// AC (Application Cryptogram)
2019-01-11 19:56:59 +08:00
tlvdb_change_or_add_node_ex ( tlvRoot , 0x9f26 , MIN ( 8 , elmlen - 3 ) , & buf [ 5 ] , & tlvElm ) ;
2019-01-10 22:22:32 +08:00
if ( decodeTLV )
TLVPrintFromTLV ( tlvElm ) ;
// IAD (Issuer Application Data) - optional
if ( len > 11 + 2 ) {
tlvdb_change_or_add_node_ex ( tlvRoot , 0x9f10 , elmlen - 11 , & buf [ 13 ] , & tlvElm ) ;
if ( decodeTLV )
TLVPrintFromTLV ( tlvElm ) ;
}
}
} else {
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
}
}
2018-12-04 00:16:25 +08:00
int CmdEMVExec ( const char * cmd ) {
2017-11-25 10:29:26 +08:00
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
uint8_t AID [ APDU_AID_LEN ] = { 0 } ;
size_t AIDlen = 0 ;
2017-12-24 17:26:47 +08:00
uint8_t ODAiList [ 4096 ] ;
size_t ODAiListLen = 0 ;
2017-11-25 10:29:26 +08:00
int res ;
2017-12-24 17:26:47 +08:00
struct tlvdb * tlvSelect = NULL ;
struct tlvdb * tlvRoot = NULL ;
struct tlv * pdol_data_tlv = NULL ;
2018-11-20 22:26:54 +08:00
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv exec " ,
2018-11-20 22:26:54 +08:00
" Executes EMV contactless transaction " ,
2018-12-26 19:50:11 +08:00
" Usage: \n "
" \t emv exec -sat -> select card, execute MSD transaction, show APDU and TLV \n "
2018-12-04 00:21:05 +08:00
" \t emv exec -satc -> select card, execute CDA transaction, show APDU and TLV \n " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " sS " , " select " , " activate field and select card. " ) ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses. " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results. " ) ,
arg_lit0 ( " jJ " , " jload " , " Load transaction parameters from `emv/defparams.json` file. " ) ,
arg_lit0 ( " fF " , " forceaid " , " Force search AID. Search AID instead of execute PPSE. " ) ,
arg_rem ( " By default: " , " Transaction type - MSD " ) ,
arg_lit0 ( " vV " , " qvsdc " , " Transaction type - qVSDC or M/Chip. " ) ,
arg_lit0 ( " cC " , " qvsdccda " , " Transaction type - qVSDC or M/Chip plus CDA (SDAD generation). " ) ,
arg_lit0 ( " xX " , " vsdc " , " Transaction type - VSDC. For test only. Not a standart behavior. " ) ,
arg_lit0 ( " gG " , " acgpo " , " VISA. generate AC from GPO. " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool activateField = arg_get_lit ( 1 ) ;
bool showAPDU = arg_get_lit ( 2 ) ;
bool decodeTLV = arg_get_lit ( 3 ) ;
bool paramLoadJSON = arg_get_lit ( 4 ) ;
bool forceSearch = arg_get_lit ( 5 ) ;
enum TransactionType TrType = TT_MSD ;
if ( arg_get_lit ( 7 ) )
2019-01-10 19:06:56 +08:00
TrType = TT_QVSDCMCHIP ;
2018-11-20 22:26:54 +08:00
if ( arg_get_lit ( 8 ) )
2019-01-10 19:06:56 +08:00
TrType = TT_CDA ;
2018-12-07 01:47:57 +08:00
if ( arg_get_lit ( 9 ) )
2018-11-20 22:26:54 +08:00
TrType = TT_VSDC ;
2018-12-07 01:47:57 +08:00
bool GenACGPO = arg_get_lit ( 10 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
2018-12-07 01:47:57 +08:00
if ( arg_get_lit ( 11 ) )
2018-12-04 01:29:31 +08:00
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-12-29 02:48:45 +08:00
uint8_t psenum = ( channel = = ECC_CONTACT ) ? 1 : 2 ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( showAPDU ) ;
2017-11-25 10:29:26 +08:00
// init applets list tree
const char * al = " Applets list " ;
tlvSelect = tlvdb_fixed ( 1 , strlen ( al ) , ( const unsigned char * ) al ) ;
// Application Selection
// https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html
if ( ! forceSearch ) {
// PPSE
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * PPSE. " ) ;
2017-11-25 10:29:26 +08:00
SetAPDULogging ( showAPDU ) ;
2018-12-29 02:48:45 +08:00
res = EMVSearchPSE ( channel , activateField , true , psenum , decodeTLV , tlvSelect ) ;
2017-11-25 10:29:26 +08:00
// check PPSE and select application id
if ( ! res ) {
TLVPrintAIDlistFromSelectTLV ( tlvSelect ) ;
EMVSelectApplication ( tlvSelect , AID , & AIDlen ) ;
}
}
// Search
if ( ! AIDlen ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * Search AID in list. " ) ;
2017-11-25 10:29:26 +08:00
SetAPDULogging ( false ) ;
2018-12-04 01:29:31 +08:00
if ( EMVSearch ( channel , activateField , true , decodeTLV , tlvSelect ) ) {
2017-12-24 17:26:47 +08:00
dreturn ( 2 ) ;
2017-11-25 10:29:26 +08:00
}
// check search and select application id
TLVPrintAIDlistFromSelectTLV ( tlvSelect ) ;
EMVSelectApplication ( tlvSelect , AID , & AIDlen ) ;
}
// Init TLV tree
const char * alr = " Root terminal TLV tree " ;
tlvRoot = tlvdb_fixed ( 1 , strlen ( alr ) , ( const unsigned char * ) alr ) ;
// check if we found EMV application on card
if ( ! AIDlen ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Can't select AID. EMV AID not found " ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 2 ) ;
2017-11-25 10:29:26 +08:00
}
// Select
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * Selecting AID:%s " , sprint_hex_inrow ( AID , AIDlen ) ) ;
2017-11-25 10:29:26 +08:00
SetAPDULogging ( showAPDU ) ;
2018-12-04 01:29:31 +08:00
res = EMVSelect ( channel , false , true , AID , AIDlen , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2017-11-25 10:29:26 +08:00
if ( res ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Can't select AID (%d). Exit... " , res ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 3 ) ;
2017-11-25 10:29:26 +08:00
}
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
2018-11-20 22:26:54 +08:00
PrintAndLog ( " * Selected. " ) ;
2017-11-25 10:29:26 +08:00
2018-11-20 22:26:54 +08:00
PrintAndLog ( " \n * Init transaction parameters. " ) ;
InitTransactionParameters ( tlvRoot , paramLoadJSON , TrType , GenACGPO ) ;
2017-12-03 01:58:33 +08:00
TLVPrintFromTLV ( tlvRoot ) ; // TODO delete!!!
2017-11-25 10:29:26 +08:00
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * Calc PDOL. " ) ;
2017-12-24 17:26:47 +08:00
pdol_data_tlv = dol_process ( tlvdb_get ( tlvRoot , 0x9f38 , NULL ) , tlvRoot , 0x83 ) ;
2017-11-25 10:29:26 +08:00
if ( ! pdol_data_tlv ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error: can't create PDOL TLV. " ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 4 ) ;
2017-11-25 10:29:26 +08:00
}
size_t pdol_data_tlv_data_len ;
unsigned char * pdol_data_tlv_data = tlv_encode ( pdol_data_tlv , & pdol_data_tlv_data_len ) ;
if ( ! pdol_data_tlv_data ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error: can't create PDOL data. " ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 4 ) ;
2017-11-25 10:29:26 +08:00
}
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " PDOL data[%d]: %s " , pdol_data_tlv_data_len , sprint_hex ( pdol_data_tlv_data , pdol_data_tlv_data_len ) ) ;
2017-11-25 10:29:26 +08:00
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * GPO. " ) ;
2018-12-04 01:29:31 +08:00
res = EMVGPO ( channel , true , pdol_data_tlv_data , pdol_data_tlv_data_len , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2017-11-25 10:29:26 +08:00
2017-12-05 22:44:35 +08:00
free ( pdol_data_tlv_data ) ;
2017-12-24 17:26:47 +08:00
//free(pdol_data_tlv); --- free on exit.
2017-11-25 10:29:26 +08:00
if ( res ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " GPO error(%d): %4x. Exit... " , res , sw ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 5 ) ;
2017-11-25 10:29:26 +08:00
}
// process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV]
2018-11-20 22:26:54 +08:00
ProcessGPOResponseFormat1 ( tlvRoot , buf , len , decodeTLV ) ;
2017-11-25 10:29:26 +08:00
2017-12-03 01:58:33 +08:00
// extract PAN from track2
{
const struct tlv * track2 = tlvdb_get ( tlvRoot , 0x57 , NULL ) ;
if ( ! tlvdb_get ( tlvRoot , 0x5a , NULL ) & & track2 & & track2 - > len > = 8 ) {
struct tlvdb * pan = GetPANFromTrack2 ( track2 ) ;
if ( pan ) {
tlvdb_add ( tlvRoot , pan ) ;
const struct tlv * pantlv = tlvdb_get ( tlvRoot , 0x5a , NULL ) ;
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * * Extracted PAN from track2: %s " , sprint_hex ( pantlv - > value , pantlv - > len ) ) ;
2017-12-03 01:58:33 +08:00
} else {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * * WARNING: Can't extract PAN from track2. " ) ;
2017-12-03 01:58:33 +08:00
}
}
}
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * Read records from AFL. " ) ;
2017-11-25 10:29:26 +08:00
const struct tlv * AFL = tlvdb_get ( tlvRoot , 0x94 , NULL ) ;
2017-12-06 04:07:03 +08:00
if ( ! AFL | | ! AFL - > len )
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " WARNING: AFL not found. " ) ;
2017-11-25 10:29:26 +08:00
2017-12-06 04:07:03 +08:00
while ( AFL & & AFL - > len ) {
2017-11-25 10:29:26 +08:00
if ( AFL - > len % 4 ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error: Wrong AFL length: %d " , AFL - > len ) ;
2017-11-25 10:29:26 +08:00
break ;
}
for ( int i = 0 ; i < AFL - > len / 4 ; i + + ) {
uint8_t SFI = AFL - > value [ i * 4 + 0 ] > > 3 ;
uint8_t SFIstart = AFL - > value [ i * 4 + 1 ] ;
uint8_t SFIend = AFL - > value [ i * 4 + 2 ] ;
uint8_t SFIoffline = AFL - > value [ i * 4 + 3 ] ;
2019-01-09 23:08:23 +08:00
PrintAndLogEx ( NORMAL , " * * SFI[%02x] start:%02x end:%02x offline count:%02x " , SFI , SFIstart , SFIend , SFIoffline ) ;
2017-11-25 10:29:26 +08:00
if ( SFI = = 0 | | SFI = = 31 | | SFIstart = = 0 | | SFIstart > SFIend ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " SFI ERROR! Skipped... " ) ;
2017-11-25 10:29:26 +08:00
continue ;
}
2017-12-06 04:07:03 +08:00
for ( int n = SFIstart ; n < = SFIend ; n + + ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " * * * SFI[%02x] %d " , SFI , n ) ;
2017-11-25 10:29:26 +08:00
2018-12-04 01:29:31 +08:00
res = EMVReadRecord ( channel , true , SFI , n , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2017-11-25 10:29:26 +08:00
if ( res ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error SFI[%02x]. APDU error %4x " , SFI , sw ) ;
2017-11-25 10:29:26 +08:00
continue ;
}
if ( decodeTLV ) {
TLVPrintFromBuffer ( buf , len ) ;
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2017-11-25 10:29:26 +08:00
}
2017-12-24 17:26:47 +08:00
// Build Input list for Offline Data Authentication
// EMV 4.3 book3 10.3, page 96
2019-01-09 23:04:21 +08:00
if ( SFIoffline > 0 ) {
2017-12-24 17:26:47 +08:00
if ( SFI < 11 ) {
const unsigned char * abuf = buf ;
size_t elmlen = len ;
struct tlv e ;
if ( tlv_parse_tl ( & abuf , & elmlen , & e ) ) {
memcpy ( & ODAiList [ ODAiListLen ] , & buf [ len - elmlen ] , elmlen ) ;
ODAiListLen + = elmlen ;
} else {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error SFI[%02x]. Creating input list for Offline Data Authentication error. " , SFI ) ;
2017-12-24 17:26:47 +08:00
}
} else {
memcpy ( & ODAiList [ ODAiListLen ] , buf , len ) ;
ODAiListLen + = len ;
}
2019-01-09 23:04:21 +08:00
SFIoffline - - ;
2017-11-25 10:29:26 +08:00
}
}
2017-12-24 17:26:47 +08:00
}
2017-11-25 10:29:26 +08:00
break ;
}
2017-12-24 17:26:47 +08:00
// copy Input list for Offline Data Authentication
if ( ODAiListLen ) {
struct tlvdb * oda = tlvdb_fixed ( 0x21 , ODAiListLen , ODAiList ) ; // not a standard tag
tlvdb_add ( tlvRoot , oda ) ;
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " * Input list for Offline Data Authentication added to TLV. len=%d \n " , ODAiListLen ) ;
2018-11-20 22:26:54 +08:00
}
2018-12-07 02:04:28 +08:00
2017-12-05 22:44:35 +08:00
// get AIP
2018-12-07 02:04:28 +08:00
uint16_t AIP = 0 ;
2017-12-03 01:58:33 +08:00
const struct tlv * AIPtlv = tlvdb_get ( tlvRoot , 0x82 , NULL ) ;
2018-12-07 02:04:28 +08:00
if ( AIPtlv ) {
AIP = AIPtlv - > value [ 0 ] + AIPtlv - > value [ 1 ] * 0x100 ;
PrintAndLogEx ( NORMAL , " * * AIP=%04x " , AIP ) ;
} else {
PrintAndLogEx ( ERR , " Can't found AIP. " ) ;
}
2017-12-05 22:44:35 +08:00
// SDA
if ( AIP & 0x0040 ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * SDA " ) ;
2017-12-24 17:26:47 +08:00
trSDA ( tlvRoot ) ;
2017-12-05 22:44:35 +08:00
}
// DDA
if ( AIP & 0x0020 ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * DDA " ) ;
2018-12-04 01:29:31 +08:00
trDDA ( channel , decodeTLV , tlvRoot ) ;
2017-12-05 22:44:35 +08:00
}
2017-12-24 17:26:47 +08:00
2017-12-05 22:44:35 +08:00
// transaction check
2017-12-03 01:58:33 +08:00
// qVSDC
2018-12-26 19:50:11 +08:00
if ( TrType = = TT_QVSDCMCHIP | | TrType = = TT_CDA ) {
2017-12-03 01:58:33 +08:00
// 9F26: Application Cryptogram
const struct tlv * AC = tlvdb_get ( tlvRoot , 0x9F26 , NULL ) ;
if ( AC ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n --> qVSDC transaction. " ) ;
PrintAndLogEx ( NORMAL , " * AC path " ) ;
2017-12-03 01:58:33 +08:00
// 9F36: Application Transaction Counter (ATC)
const struct tlv * ATC = tlvdb_get ( tlvRoot , 0x9F36 , NULL ) ;
if ( ATC ) {
// 9F10: Issuer Application Data - optional
const struct tlv * IAD = tlvdb_get ( tlvRoot , 0x9F10 , NULL ) ;
// print AC data
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " ATC: %s " , sprint_hex ( ATC - > value , ATC - > len ) ) ;
PrintAndLogEx ( NORMAL , " AC: %s " , sprint_hex ( AC - > value , AC - > len ) ) ;
2018-12-26 19:50:11 +08:00
if ( IAD ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " IAD: %s " , sprint_hex ( IAD - > value , IAD - > len ) ) ;
2017-12-03 01:58:33 +08:00
if ( IAD - > len > = IAD - > value [ 0 ] + 1 ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \t Key index: 0x%02x " , IAD - > value [ 1 ] ) ;
PrintAndLogEx ( NORMAL , " \t Crypto ver: 0x%02x(%03d) " , IAD - > value [ 2 ] , IAD - > value [ 2 ] ) ;
PrintAndLogEx ( NORMAL , " \t CVR: " , sprint_hex ( & IAD - > value [ 3 ] , IAD - > value [ 0 ] - 2 ) ) ;
2017-12-03 01:58:33 +08:00
struct tlvdb * cvr = tlvdb_fixed ( 0x20 , IAD - > value [ 0 ] - 2 , & IAD - > value [ 3 ] ) ;
TLVPrintFromTLVLev ( cvr , 1 ) ;
}
} else {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " WARNING: IAD not found. " ) ;
2017-12-03 01:58:33 +08:00
}
} else {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error AC: Application Transaction Counter (ATC) not found. " ) ;
2017-12-03 01:58:33 +08:00
}
}
}
2017-12-05 22:44:35 +08:00
// Mastercard M/CHIP
if ( GetCardPSVendor ( AID , AIDlen ) = = CV_MASTERCARD & & ( TrType = = TT_QVSDCMCHIP | | TrType = = TT_CDA ) ) {
2017-12-03 01:58:33 +08:00
const struct tlv * CDOL1 = tlvdb_get ( tlvRoot , 0x8c , NULL ) ;
if ( CDOL1 & & GetCardPSVendor ( AID , AIDlen ) = = CV_MASTERCARD ) { // and m/chip transaction flag
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n --> Mastercard M/Chip transaction. " ) ;
2017-12-05 22:44:35 +08:00
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " * * Generate challenge " ) ;
2018-12-04 01:29:31 +08:00
res = EMVGenerateChallenge ( channel , true , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2017-12-05 22:44:35 +08:00
if ( res ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error GetChallenge. APDU error %4x " , sw ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 6 ) ;
2017-12-05 22:44:35 +08:00
}
if ( len < 4 ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error GetChallenge. Wrong challenge length %d " , len ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 6 ) ;
2017-12-05 22:44:35 +08:00
}
// ICC Dynamic Number
struct tlvdb * ICCDynN = tlvdb_fixed ( 0x9f4c , len , buf ) ;
tlvdb_add ( tlvRoot , ICCDynN ) ;
2018-12-26 19:50:11 +08:00
if ( decodeTLV ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * * ICC Dynamic Number: " ) ;
2017-12-05 22:44:35 +08:00
TLVPrintFromTLV ( ICCDynN ) ;
}
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " * * Calc CDOL1 " ) ;
2017-12-05 22:44:35 +08:00
struct tlv * cdol_data_tlv = dol_process ( tlvdb_get ( tlvRoot , 0x8c , NULL ) , tlvRoot , 0x01 ) ; // 0x01 - dummy tag
2018-12-26 19:50:11 +08:00
if ( ! cdol_data_tlv ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error: can't create CDOL1 TLV. " ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 6 ) ;
2017-12-05 22:44:35 +08:00
}
2018-12-26 19:50:11 +08:00
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " CDOL1 data[%d]: %s " , cdol_data_tlv - > len , sprint_hex ( cdol_data_tlv - > value , cdol_data_tlv - > len ) ) ;
2017-12-05 22:44:35 +08:00
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " * * AC1 " ) ;
2017-12-05 22:44:35 +08:00
// EMVAC_TC + EMVAC_CDAREQ --- to get SDAD
2018-12-04 01:29:31 +08:00
res = EMVAC ( channel , true , ( TrType = = TT_CDA ) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC , ( uint8_t * ) cdol_data_tlv - > value , cdol_data_tlv - > len , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2017-12-05 22:44:35 +08:00
if ( res ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " AC1 error(%d): %4x. Exit... " , res , sw ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 7 ) ;
2017-12-05 22:44:35 +08:00
}
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
2017-12-24 17:26:47 +08:00
// CDA
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * CDA: " ) ;
2017-12-24 17:26:47 +08:00
struct tlvdb * ac_tlv = tlvdb_parse_multi ( buf , len ) ;
res = trCDA ( tlvRoot , ac_tlv , pdol_data_tlv , cdol_data_tlv ) ;
if ( res ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " CDA error (%d) " , res ) ;
2017-12-24 17:26:47 +08:00
}
2018-12-26 19:50:11 +08:00
2017-12-24 17:26:47 +08:00
free ( ac_tlv ) ;
free ( cdol_data_tlv ) ;
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * M/Chip transaction result: " ) ;
2017-12-05 22:44:35 +08:00
// 9F27: Cryptogram Information Data (CID)
const struct tlv * CID = tlvdb_get ( tlvRoot , 0x9F27 , NULL ) ;
if ( CID ) {
emv_tag_dump ( CID , stdout , 0 ) ;
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " ------------------------------ " ) ;
2017-12-05 22:44:35 +08:00
if ( CID - > len > 0 ) {
switch ( CID - > value [ 0 ] & EMVAC_AC_MASK ) {
case EMVAC_AAC :
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " Transaction DECLINED. " ) ;
2017-12-05 22:44:35 +08:00
break ;
case EMVAC_TC :
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " Transaction approved OFFLINE. " ) ;
2017-12-05 22:44:35 +08:00
break ;
case EMVAC_ARQC :
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " Transaction approved ONLINE. " ) ;
2017-12-05 22:44:35 +08:00
break ;
default :
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error: CID transaction code error %2x " , CID - > value [ 0 ] & EMVAC_AC_MASK ) ;
2017-12-05 22:44:35 +08:00
break ;
}
} else {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error: Wrong CID length %d " , CID - > len ) ;
2017-12-05 22:44:35 +08:00
}
} else {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error: CID(9F27) not found. " ) ;
2017-12-05 22:44:35 +08:00
}
2017-12-03 01:58:33 +08:00
}
}
// MSD
2017-12-05 22:44:35 +08:00
if ( AIP & 0x8000 & & TrType = = TT_MSD ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n --> MSD transaction. " ) ;
2017-12-03 01:58:33 +08:00
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " * MSD dCVV path. Check dCVV " ) ;
2017-12-03 01:58:33 +08:00
const struct tlv * track2 = tlvdb_get ( tlvRoot , 0x57 , NULL ) ;
if ( track2 ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " Track2: %s " , sprint_hex ( track2 - > value , track2 - > len ) ) ;
2017-12-03 01:58:33 +08:00
struct tlvdb * dCVV = GetdCVVRawFromTrack2 ( track2 ) ;
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " dCVV raw data: " ) ;
2017-12-03 01:58:33 +08:00
TLVPrintFromTLV ( dCVV ) ;
if ( GetCardPSVendor ( AID , AIDlen ) = = CV_MASTERCARD ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * Mastercard calculate UDOL " ) ;
2017-12-03 01:58:33 +08:00
// UDOL (9F69)
const struct tlv * UDOL = tlvdb_get ( tlvRoot , 0x9F69 , NULL ) ;
// UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes
const struct tlv defUDOL = {
. tag = 0x01 ,
. len = 3 ,
. value = ( uint8_t * ) " \x9f \x6a \x04 " ,
} ;
if ( ! UDOL )
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " Use default UDOL. " ) ;
2017-12-03 01:58:33 +08:00
2017-12-05 22:44:35 +08:00
struct tlv * udol_data_tlv = dol_process ( UDOL ? UDOL : & defUDOL , tlvRoot , 0x01 ) ; // 0x01 - dummy tag
2018-12-26 19:50:11 +08:00
if ( ! udol_data_tlv ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error: can't create UDOL TLV. " ) ;
2017-12-24 17:26:47 +08:00
dreturn ( 8 ) ;
2017-12-03 01:58:33 +08:00
}
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " UDOL data[%d]: %s " , udol_data_tlv - > len , sprint_hex ( udol_data_tlv - > value , udol_data_tlv - > len ) ) ;
2017-12-03 01:58:33 +08:00
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * Mastercard compute cryptographic checksum(UDOL) " ) ;
2017-12-03 01:58:33 +08:00
2018-12-04 01:29:31 +08:00
res = MSCComputeCryptoChecksum ( channel , true , ( uint8_t * ) udol_data_tlv - > value , udol_data_tlv - > len , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2017-12-03 01:58:33 +08:00
if ( res ) {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error Compute Crypto Checksum. APDU error %4x " , sw ) ;
2017-12-24 17:26:47 +08:00
free ( udol_data_tlv ) ;
dreturn ( 9 ) ;
2017-12-03 01:58:33 +08:00
}
2018-12-04 00:49:54 +08:00
// Mastercard compute cryptographic checksum result
TLVPrintFromBuffer ( buf , len ) ;
PrintAndLogEx ( NORMAL , " " ) ;
2017-12-24 17:26:47 +08:00
free ( udol_data_tlv ) ;
2017-12-03 01:58:33 +08:00
}
} else {
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( WARNING , " Error MSD: Track2 data not found. " ) ;
2017-12-03 01:58:33 +08:00
}
}
2017-12-06 04:07:03 +08:00
2019-01-10 19:06:56 +08:00
// VSDC
2019-01-10 19:26:15 +08:00
if ( GetCardPSVendor ( AID , AIDlen ) = = CV_VISA & & ( TrType = = TT_VSDC | | TrType = = TT_CDA ) ) {
2019-01-10 19:06:56 +08:00
PrintAndLogEx ( NORMAL , " \n --> VSDC transaction. " ) ;
2019-01-10 19:22:27 +08:00
PrintAndLogEx ( NORMAL , " * * Calc CDOL1 " ) ;
struct tlv * cdol_data_tlv = dol_process ( tlvdb_get ( tlvRoot , 0x8c , NULL ) , tlvRoot , 0x01 ) ; // 0x01 - dummy tag
if ( ! cdol_data_tlv ) {
PrintAndLogEx ( WARNING , " Error: can't create CDOL1 TLV. " ) ;
dreturn ( 6 ) ;
}
PrintAndLogEx ( NORMAL , " CDOL1 data[%d]: %s " , cdol_data_tlv - > len , sprint_hex ( cdol_data_tlv - > value , cdol_data_tlv - > len ) ) ;
PrintAndLogEx ( NORMAL , " * * AC1 " ) ;
// EMVAC_TC + EMVAC_CDAREQ --- to get SDAD
res = EMVAC ( channel , true , ( TrType = = TT_CDA ) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC , ( uint8_t * ) cdol_data_tlv - > value , cdol_data_tlv - > len , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
if ( res ) {
PrintAndLogEx ( NORMAL , " AC1 error(%d): %4x. Exit... " , res , sw ) ;
dreturn ( 7 ) ;
}
2019-01-10 22:22:32 +08:00
// process Format1 (0x80) anf print Format2 (0x77)
ProcessACResponseFormat1 ( tlvRoot , buf , len , decodeTLV ) ;
2019-01-10 19:22:27 +08:00
PrintAndLogEx ( NORMAL , " \n * * Processing online request \n " ) ;
// authorization response code from acquirer
2019-01-10 19:26:15 +08:00
const char HostResponse [ ] = " 00 " ; // 0x3030
2019-01-10 19:22:27 +08:00
PrintAndLogEx ( NORMAL , " * * Host Response: `%s` " , HostResponse ) ;
tlvdb_change_or_add_node ( tlvRoot , 0x8a , sizeof ( HostResponse ) - 1 , ( const unsigned char * ) HostResponse ) ;
2019-01-10 19:06:56 +08:00
}
2017-11-25 10:29:26 +08:00
DropField ( ) ;
// Destroy TLV's
2017-12-24 17:26:47 +08:00
free ( pdol_data_tlv ) ;
2017-11-25 10:29:26 +08:00
tlvdb_free ( tlvSelect ) ;
tlvdb_free ( tlvRoot ) ;
2018-02-21 20:13:25 +08:00
PrintAndLogEx ( NORMAL , " \n * Transaction completed. " ) ;
2017-11-25 10:29:26 +08:00
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVScan ( const char * cmd ) {
2018-11-20 22:26:54 +08:00
uint8_t AID [ APDU_AID_LEN ] = { 0 } ;
size_t AIDlen = 0 ;
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
int res ;
json_t * root ;
json_error_t error ;
2018-12-04 00:21:05 +08:00
CLIParserInit ( " emv scan " ,
2018-11-20 22:26:54 +08:00
" Scan EMV card and save it contents to a file. " ,
" It executes EMV contactless transaction and saves result to a file which can be used for emulation \n "
2018-12-04 00:21:05 +08:00
" Usage: \n \t emv scan -at -> scan MSD transaction mode and show APDU and TLV \n "
" \t emv scan -c -> scan CDA transaction mode \n " ) ;
2018-11-20 22:26:54 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses. " ) ,
arg_lit0 ( " tT " , " tlv " , " TLV decode results. " ) ,
arg_lit0 ( " eE " , " extract " , " Extract TLV elements and fill Application Data " ) ,
arg_lit0 ( " jJ " , " jload " , " Load transaction parameters from `emv/defparams.json` file. " ) ,
arg_rem ( " By default: " , " Transaction type - MSD " ) ,
arg_lit0 ( " vV " , " qvsdc " , " Transaction type - qVSDC or M/Chip. " ) ,
arg_lit0 ( " cC " , " qvsdccda " , " Transaction type - qVSDC or M/Chip plus CDA (SDAD generation). " ) ,
arg_lit0 ( " xX " , " vsdc " , " Transaction type - VSDC. For test only. Not a standart behavior. " ) ,
arg_lit0 ( " gG " , " acgpo " , " VISA. generate AC from GPO. " ) ,
arg_lit0 ( " mM " , " merge " , " Merge output file with card's data. (warning: the file may be corrupted!) " ) ,
2018-12-04 01:29:31 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
2018-11-20 22:26:54 +08:00
arg_str1 ( NULL , NULL , " output.json " , " JSON output file name " ) ,
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool showAPDU = arg_get_lit ( 1 ) ;
bool decodeTLV = arg_get_lit ( 2 ) ;
bool extractTLVElements = arg_get_lit ( 3 ) ;
bool paramLoadJSON = arg_get_lit ( 4 ) ;
enum TransactionType TrType = TT_MSD ;
if ( arg_get_lit ( 6 ) )
TrType = TT_QVSDCMCHIP ;
if ( arg_get_lit ( 7 ) )
TrType = TT_CDA ;
if ( arg_get_lit ( 8 ) )
TrType = TT_VSDC ;
bool GenACGPO = arg_get_lit ( 9 ) ;
bool MergeJSON = arg_get_lit ( 10 ) ;
2018-12-04 01:29:31 +08:00
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 11 ) )
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-12-29 02:48:45 +08:00
uint8_t psenum = ( channel = = ECC_CONTACT ) ? 1 : 2 ;
2018-11-20 22:26:54 +08:00
uint8_t relfname [ 250 ] = { 0 } ;
char * crelfname = ( char * ) relfname ;
int relfnamelen = 0 ;
2018-12-04 01:29:31 +08:00
CLIGetStrWithReturn ( 12 , relfname , & relfnamelen ) ;
2018-11-20 22:26:54 +08:00
CLIParserFree ( ) ;
SetAPDULogging ( showAPDU ) ;
2018-12-07 01:47:57 +08:00
// TODO
if ( channel = = ECC_CONTACT ) {
PrintAndLogEx ( ERR , " Do not use contact interface. Exit. " ) ;
return 1 ;
}
2018-11-20 22:26:54 +08:00
// current path + file name
if ( ! strstr ( crelfname , " .json " ) )
strcat ( crelfname , " .json " ) ;
char fname [ strlen ( get_my_executable_directory ( ) ) + strlen ( crelfname ) + 1 ] ;
strcpy ( fname , get_my_executable_directory ( ) ) ;
strcat ( fname , crelfname ) ;
if ( MergeJSON ) {
root = json_load_file ( fname , 0 , & error ) ;
if ( ! root ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Json error on line %d: %s " , error . line , error . text ) ;
2018-11-20 22:26:54 +08:00
return 1 ;
}
if ( ! json_is_object ( root ) ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Invalid json format. root must be an object. " ) ;
2018-11-20 22:26:54 +08:00
return 1 ;
}
} else {
root = json_object ( ) ;
}
// drop field at start
DropField ( ) ;
// iso 14443 select
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( NORMAL , " --> GET UID, ATS. " ) ;
2018-11-20 22:26:54 +08:00
iso14a_card_select_t card ;
if ( Hf14443_4aGetCardData ( & card ) ) {
return 2 ;
}
2018-12-04 00:21:05 +08:00
JsonSaveStr ( root , " $.File.Created " , " proxmark3 `emv scan` " ) ;
2018-11-20 22:26:54 +08:00
JsonSaveStr ( root , " $.Card.Communication " , " iso14443-4a " ) ;
JsonSaveBufAsHex ( root , " $.Card.UID " , ( uint8_t * ) & card . uid , card . uidlen ) ;
JsonSaveHex ( root , " $.Card.ATQA " , card . atqa [ 0 ] + ( card . atqa [ 1 ] < < 2 ) , 2 ) ;
JsonSaveHex ( root , " $.Card.SAK " , card . sak , 0 ) ;
JsonSaveBufAsHex ( root , " $.Card.ATS " , ( uint8_t * ) card . ats , card . ats_len ) ;
// init applets list tree
const char * al = " Applets list " ;
struct tlvdb * tlvSelect = tlvdb_fixed ( 1 , strlen ( al ) , ( const unsigned char * ) al ) ;
// EMV PPSE
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( NORMAL , " --> PPSE. " ) ;
2018-12-04 01:29:31 +08:00
res = EMVSelectPSE ( channel , true , true , 2 , buf , sizeof ( buf ) , & len , & sw ) ;
2018-11-20 22:26:54 +08:00
if ( ! res & & sw = = 0x9000 ) {
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
JsonSaveBufAsHex ( root , " $.PPSE.AID " , ( uint8_t * ) " 2PAY.SYS.DDF01 " , 14 ) ;
struct tlvdb * fci = tlvdb_parse_multi ( buf , len ) ;
if ( extractTLVElements )
JsonSaveTLVTree ( root , root , " $.PPSE.FCITemplate " , fci ) ;
else
JsonSaveTLVTreeElm ( root , " $.PPSE.FCITemplate " , fci , true , true , false ) ;
JsonSaveTLVValue ( root , " $.Application.KernelID " , tlvdb_find_full ( fci , 0x9f2a ) ) ;
tlvdb_free ( fci ) ;
}
2018-12-29 02:48:45 +08:00
res = EMVSearchPSE ( channel , false , true , psenum , decodeTLV , tlvSelect ) ;
2018-11-20 22:26:54 +08:00
// check PPSE and select application id
if ( ! res ) {
TLVPrintAIDlistFromSelectTLV ( tlvSelect ) ;
} else {
// EMV SEARCH with AID list
SetAPDULogging ( false ) ;
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( NORMAL , " --> AID search. " ) ;
2018-12-04 01:29:31 +08:00
if ( EMVSearch ( channel , false , true , decodeTLV , tlvSelect ) ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't found any of EMV AID. Exit... " ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvSelect ) ;
DropField ( ) ;
return 3 ;
}
// check search and select application id
TLVPrintAIDlistFromSelectTLV ( tlvSelect ) ;
}
// EMV SELECT application
SetAPDULogging ( showAPDU ) ;
EMVSelectApplication ( tlvSelect , AID , & AIDlen ) ;
tlvdb_free ( tlvSelect ) ;
if ( ! AIDlen ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " Can't select AID. EMV AID not found. Exit... " ) ;
2018-11-20 22:26:54 +08:00
DropField ( ) ;
return 4 ;
}
JsonSaveBufAsHex ( root , " $.Application.AID " , AID , AIDlen ) ;
// Init TLV tree
const char * alr = " Root terminal TLV tree " ;
struct tlvdb * tlvRoot = tlvdb_fixed ( 1 , strlen ( alr ) , ( const unsigned char * ) alr ) ;
// EMV SELECT applet
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( NORMAL , " \n -->Selecting AID:%s. " , sprint_hex_inrow ( AID , AIDlen ) ) ;
2018-11-20 22:26:54 +08:00
SetAPDULogging ( showAPDU ) ;
2018-12-04 01:29:31 +08:00
res = EMVSelect ( channel , false , true , AID , AIDlen , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2018-11-20 22:26:54 +08:00
if ( res ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't select AID (%d). Exit... " , res ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvRoot ) ;
DropField ( ) ;
return 5 ;
}
if ( decodeTLV )
TLVPrintFromBuffer ( buf , len ) ;
// save mode
if ( tlvdb_get ( tlvRoot , 0x9f38 , NULL ) ) {
JsonSaveStr ( root , " $.Application.Mode " , TransactionTypeStr [ TrType ] ) ;
}
struct tlvdb * fci = tlvdb_parse_multi ( buf , len ) ;
if ( extractTLVElements )
JsonSaveTLVTree ( root , root , " $.Application.FCITemplate " , fci ) ;
else
JsonSaveTLVTreeElm ( root , " $.Application.FCITemplate " , fci , true , true , false ) ;
tlvdb_free ( fci ) ;
// create transaction parameters
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( NORMAL , " -->Init transaction parameters. " ) ;
2018-11-20 22:26:54 +08:00
InitTransactionParameters ( tlvRoot , paramLoadJSON , TrType , GenACGPO ) ;
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( NORMAL , " -->Calc PDOL. " ) ;
2018-11-20 22:26:54 +08:00
struct tlv * pdol_data_tlv = dol_process ( tlvdb_get ( tlvRoot , 0x9f38 , NULL ) , tlvRoot , 0x83 ) ;
if ( ! pdol_data_tlv ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't create PDOL TLV. " ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvRoot ) ;
DropField ( ) ;
return 6 ;
}
size_t pdol_data_tlv_data_len ;
unsigned char * pdol_data_tlv_data = tlv_encode ( pdol_data_tlv , & pdol_data_tlv_data_len ) ;
if ( ! pdol_data_tlv_data ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't create PDOL data. " ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvRoot ) ;
DropField ( ) ;
return 6 ;
}
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " PDOL data[%d]: %s " , pdol_data_tlv_data_len , sprint_hex ( pdol_data_tlv_data , pdol_data_tlv_data_len ) ) ;
2018-11-20 22:26:54 +08:00
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " -->GPO. " ) ;
2018-12-04 01:29:31 +08:00
res = EMVGPO ( channel , true , pdol_data_tlv_data , pdol_data_tlv_data_len , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2018-11-20 22:26:54 +08:00
free ( pdol_data_tlv_data ) ;
free ( pdol_data_tlv ) ;
if ( res ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " GPO error(%d): %4x. Exit... " , res , sw ) ;
2018-11-20 22:26:54 +08:00
tlvdb_free ( tlvRoot ) ;
DropField ( ) ;
return 7 ;
}
ProcessGPOResponseFormat1 ( tlvRoot , buf , len , decodeTLV ) ;
struct tlvdb * gpofci = tlvdb_parse_multi ( buf , len ) ;
if ( extractTLVElements )
JsonSaveTLVTree ( root , root , " $.Application.GPO " , gpofci ) ;
else
JsonSaveTLVTreeElm ( root , " $.Application.GPO " , gpofci , true , true , false ) ;
JsonSaveTLVValue ( root , " $.ApplicationData.AIP " , tlvdb_find_full ( gpofci , 0x82 ) ) ;
JsonSaveTLVValue ( root , " $.ApplicationData.AFL " , tlvdb_find_full ( gpofci , 0x94 ) ) ;
tlvdb_free ( gpofci ) ;
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " -->Read records from AFL. " ) ;
2018-11-20 22:26:54 +08:00
const struct tlv * AFL = tlvdb_get ( tlvRoot , 0x94 , NULL ) ;
while ( AFL & & AFL - > len ) {
if ( AFL - > len % 4 ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Wrong AFL length: %d " , AFL - > len ) ;
2018-11-20 22:26:54 +08:00
break ;
}
json_t * sfijson = json_path_get ( root , " $.Application.Records " ) ;
if ( ! sfijson ) {
json_t * app = json_path_get ( root , " $.Application " ) ;
json_object_set_new ( app , " Records " , json_array ( ) ) ;
sfijson = json_path_get ( root , " $.Application.Records " ) ;
}
if ( ! json_is_array ( sfijson ) ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Internal logic error. `$.Application.Records` is not an array. " ) ;
2018-11-20 22:26:54 +08:00
break ;
}
for ( int i = 0 ; i < AFL - > len / 4 ; i + + ) {
uint8_t SFI = AFL - > value [ i * 4 + 0 ] > > 3 ;
uint8_t SFIstart = AFL - > value [ i * 4 + 1 ] ;
uint8_t SFIend = AFL - > value [ i * 4 + 2 ] ;
uint8_t SFIoffline = AFL - > value [ i * 4 + 3 ] ;
2018-12-26 18:02:00 +08:00
PrintAndLogEx ( INFO , " --->SFI[%02x] start:%02x end:%02x offline:%02x " , SFI , SFIstart , SFIend , SFIoffline ) ;
2018-11-20 22:26:54 +08:00
if ( SFI = = 0 | | SFI = = 31 | | SFIstart = = 0 | | SFIstart > SFIend ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " SFI ERROR! Skipped... " ) ;
2018-11-20 22:26:54 +08:00
continue ;
}
for ( int n = SFIstart ; n < = SFIend ; n + + ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " ---->SFI[%02x] %d " , SFI , n ) ;
2018-11-20 22:26:54 +08:00
2018-12-04 01:29:31 +08:00
res = EMVReadRecord ( channel , true , SFI , n , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2018-11-20 22:26:54 +08:00
if ( res ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " SFI[%02x]. APDU error %4x " , SFI , sw ) ;
2018-11-20 22:26:54 +08:00
continue ;
}
if ( decodeTLV ) {
TLVPrintFromBuffer ( buf , len ) ;
PrintAndLog ( " " ) ;
}
json_t * jsonelm = json_object ( ) ;
json_array_append_new ( sfijson , jsonelm ) ;
JsonSaveHex ( jsonelm , " SFI " , SFI , 1 ) ;
JsonSaveHex ( jsonelm , " RecordNum " , n , 1 ) ;
JsonSaveHex ( jsonelm , " Offline " , SFIoffline , 1 ) ;
struct tlvdb * rsfi = tlvdb_parse_multi ( buf , len ) ;
if ( extractTLVElements )
JsonSaveTLVTree ( root , jsonelm , " $.Data " , rsfi ) ;
else
JsonSaveTLVTreeElm ( jsonelm , " $.Data " , rsfi , true , true , false ) ;
tlvdb_free ( rsfi ) ;
}
}
break ;
}
// getting certificates
if ( tlvdb_get ( tlvRoot , 0x90 , NULL ) ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( INFO , " -->Recovering certificates. " ) ;
2018-11-20 22:26:54 +08:00
PKISetStrictExecution ( false ) ;
RecoveryCertificates ( tlvRoot , root ) ;
PKISetStrictExecution ( true ) ;
}
// free tlv object
tlvdb_free ( tlvRoot ) ;
// DropField
DropField ( ) ;
res = json_dump_file ( root , fname , JSON_INDENT ( 2 ) ) ;
if ( res ) {
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( ERR , " Can't save the file: %s " , fname ) ;
2018-11-20 22:26:54 +08:00
return 200 ;
}
2018-11-20 23:29:17 +08:00
PrintAndLogEx ( SUCCESS , " File `%s` saved. " , fname ) ;
2018-11-20 22:26:54 +08:00
// free json object
json_decref ( root ) ;
return 0 ;
}
2018-12-04 00:16:25 +08:00
int CmdEMVList ( const char * Cmd ) {
2018-03-19 01:00:41 +08:00
return CmdTraceList ( " 7816 " ) ;
2017-12-06 04:07:03 +08:00
}
2018-12-04 00:16:25 +08:00
int CmdEMVTest ( const char * cmd ) {
2017-12-24 17:26:47 +08:00
return ExecuteCryptoTests ( true ) ;
}
2018-11-20 22:26:54 +08:00
2018-12-26 18:02:00 +08:00
int CmdEMVRoca ( const char * cmd ) {
2018-12-28 22:43:15 +08:00
uint8_t AID [ APDU_AID_LEN ] = { 0 } ;
size_t AIDlen = 0 ;
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
int res ;
2018-12-26 18:02:00 +08:00
CLIParserInit ( " emv roca " ,
" Tries to extract public keys and run the ROCA test against them. \n " ,
2018-12-26 19:50:11 +08:00
" Usage: \n "
2019-01-01 17:53:41 +08:00
" \t emv roca -w -> select --CONTACT-- card and run test \n "
" \t emv roca -> select --CONTACTLESS-- card and run test \n "
) ;
2018-12-26 18:02:00 +08:00
void * argtable [ ] = {
arg_param_begin ,
2019-01-01 17:53:41 +08:00
arg_lit0 ( " tT " , " selftest " , " self test " ) ,
2018-12-26 18:02:00 +08:00
arg_lit0 ( " wW " , " wired " , " Send data via contact (iso7816) interface. Contactless interface set by default. " ) ,
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
EMVCommandChannel channel = ECC_CONTACTLESS ;
if ( arg_get_lit ( 1 ) )
2019-01-01 17:53:41 +08:00
return roca_self_test ( ) ;
if ( arg_get_lit ( 2 ) )
2018-12-26 18:02:00 +08:00
channel = ECC_CONTACT ;
2019-01-09 01:49:31 +08:00
PrintChannel ( channel ) ;
2018-12-26 18:02:00 +08:00
2018-12-28 22:43:15 +08:00
// select card
uint8_t psenum = ( channel = = ECC_CONTACT ) ? 1 : 2 ;
2018-12-29 02:48:45 +08:00
SetAPDULogging ( false ) ;
2018-12-28 22:43:15 +08:00
// init applets list tree
const char * al = " Applets list " ;
struct tlvdb * tlvSelect = tlvdb_fixed ( 1 , strlen ( al ) , ( const unsigned char * ) al ) ;
// EMV PPSE
PrintAndLogEx ( NORMAL , " --> PPSE. " ) ;
2018-12-29 02:48:45 +08:00
res = EMVSearchPSE ( channel , false , true , psenum , false , tlvSelect ) ;
2018-12-28 22:43:15 +08:00
// check PPSE and select application id
if ( ! res ) {
TLVPrintAIDlistFromSelectTLV ( tlvSelect ) ;
} else {
// EMV SEARCH with AID list
PrintAndLogEx ( NORMAL , " --> AID search. " ) ;
if ( EMVSearch ( channel , false , true , false , tlvSelect ) ) {
PrintAndLogEx ( ERR , " Can't found any of EMV AID. Exit... " ) ;
tlvdb_free ( tlvSelect ) ;
DropField ( ) ;
return 3 ;
}
// check search and select application id
TLVPrintAIDlistFromSelectTLV ( tlvSelect ) ;
}
// EMV SELECT application
SetAPDULogging ( false ) ;
EMVSelectApplication ( tlvSelect , AID , & AIDlen ) ;
tlvdb_free ( tlvSelect ) ;
if ( ! AIDlen ) {
PrintAndLogEx ( INFO , " Can't select AID. EMV AID not found. Exit... " ) ;
DropField ( ) ;
return 4 ;
}
2018-12-26 18:02:00 +08:00
// Init TLV tree
const char * alr = " Root terminal TLV tree " ;
struct tlvdb * tlvRoot = tlvdb_fixed ( 1 , strlen ( alr ) , ( const unsigned char * ) alr ) ;
2018-12-28 22:43:15 +08:00
// EMV SELECT applet
PrintAndLogEx ( NORMAL , " \n -->Selecting AID:%s. " , sprint_hex_inrow ( AID , AIDlen ) ) ;
res = EMVSelect ( channel , false , true , AID , AIDlen , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
if ( res ) {
PrintAndLogEx ( ERR , " Can't select AID (%d). Exit... " , res ) ;
tlvdb_free ( tlvRoot ) ;
DropField ( ) ;
return 5 ;
}
2018-12-28 22:58:11 +08:00
PrintAndLog ( " \n * Init transaction parameters. " ) ;
InitTransactionParameters ( tlvRoot , true , TT_QVSDCMCHIP , false ) ;
2018-12-28 22:43:15 +08:00
2018-12-28 23:07:58 +08:00
PrintAndLogEx ( NORMAL , " -->Calc PDOL. " ) ;
struct tlv * pdol_data_tlv = dol_process ( tlvdb_get ( tlvRoot , 0x9f38 , NULL ) , tlvRoot , 0x83 ) ;
if ( ! pdol_data_tlv ) {
PrintAndLogEx ( ERR , " Can't create PDOL TLV. " ) ;
tlvdb_free ( tlvRoot ) ;
DropField ( ) ;
return 6 ;
}
size_t pdol_data_tlv_data_len ;
unsigned char * pdol_data_tlv_data = tlv_encode ( pdol_data_tlv , & pdol_data_tlv_data_len ) ;
if ( ! pdol_data_tlv_data ) {
PrintAndLogEx ( ERR , " Can't create PDOL data. " ) ;
tlvdb_free ( tlvRoot ) ;
DropField ( ) ;
return 6 ;
}
PrintAndLogEx ( INFO , " PDOL data[%d]: %s " , pdol_data_tlv_data_len , sprint_hex ( pdol_data_tlv_data , pdol_data_tlv_data_len ) ) ;
2018-12-28 22:43:15 +08:00
2018-12-28 23:07:58 +08:00
PrintAndLogEx ( INFO , " -->GPO. " ) ;
res = EMVGPO ( channel , true , pdol_data_tlv_data , pdol_data_tlv_data_len , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
2018-12-26 18:02:00 +08:00
2018-12-28 23:07:58 +08:00
free ( pdol_data_tlv_data ) ;
free ( pdol_data_tlv ) ;
if ( res ) {
PrintAndLogEx ( ERR , " GPO error(%d): %4x. Exit... " , res , sw ) ;
tlvdb_free ( tlvRoot ) ;
DropField ( ) ;
return 7 ;
}
ProcessGPOResponseFormat1 ( tlvRoot , buf , len , false ) ;
PrintAndLogEx ( INFO , " -->Read records from AFL. " ) ;
const struct tlv * AFL = tlvdb_get ( tlvRoot , 0x94 , NULL ) ;
while ( AFL & & AFL - > len ) {
if ( AFL - > len % 4 ) {
PrintAndLogEx ( ERR , " Wrong AFL length: %d " , AFL - > len ) ;
break ;
}
for ( int i = 0 ; i < AFL - > len / 4 ; i + + ) {
uint8_t SFI = AFL - > value [ i * 4 + 0 ] > > 3 ;
uint8_t SFIstart = AFL - > value [ i * 4 + 1 ] ;
uint8_t SFIend = AFL - > value [ i * 4 + 2 ] ;
uint8_t SFIoffline = AFL - > value [ i * 4 + 3 ] ;
PrintAndLogEx ( INFO , " --->SFI[%02x] start:%02x end:%02x offline:%02x " , SFI , SFIstart , SFIend , SFIoffline ) ;
if ( SFI = = 0 | | SFI = = 31 | | SFIstart = = 0 | | SFIstart > SFIend ) {
PrintAndLogEx ( ERR , " SFI ERROR! Skipped... " ) ;
continue ;
}
for ( int n = SFIstart ; n < = SFIend ; n + + ) {
PrintAndLogEx ( INFO , " ---->SFI[%02x] %d " , SFI , n ) ;
res = EMVReadRecord ( channel , true , SFI , n , buf , sizeof ( buf ) , & len , & sw , tlvRoot ) ;
if ( res ) {
PrintAndLogEx ( ERR , " SFI[%02x]. APDU error %4x " , SFI , sw ) ;
continue ;
}
}
}
break ;
}
2018-12-26 18:02:00 +08:00
// getting certificates
if ( tlvdb_get ( tlvRoot , 0x90 , NULL ) ) {
PrintAndLogEx ( INFO , " -->Recovering certificates. " ) ;
PKISetStrictExecution ( false ) ;
struct emv_pk * pk = get_ca_pk ( tlvRoot ) ;
if ( ! pk ) {
PrintAndLogEx ( ERR , " ERROR: Key not found. Exit. " ) ;
goto out ;
}
struct emv_pk * issuer_pk = emv_pki_recover_issuer_cert ( pk , tlvRoot ) ;
if ( ! issuer_pk ) {
emv_pk_free ( pk ) ;
PrintAndLogEx ( WARNING , " WARNING: Issuer certificate not found. Exit. " ) ;
goto out ;
}
PrintAndLogEx ( SUCCESS , " Issuer PK recovered. RID %s IDX %02hhx CSN %s " ,
sprint_hex ( issuer_pk - > rid , 5 ) ,
issuer_pk - > index ,
sprint_hex ( issuer_pk - > serial , 3 )
) ;
struct emv_pk * icc_pk = emv_pki_recover_icc_cert ( issuer_pk , tlvRoot , NULL ) ;
if ( ! icc_pk ) {
emv_pk_free ( pk ) ;
emv_pk_free ( issuer_pk ) ;
PrintAndLogEx ( WARNING , " WARNING: ICC certificate not found. Exit. " ) ;
goto out ;
}
PrintAndLogEx ( SUCCESS , " ICC PK recovered. RID %s IDX %02hhx CSN %s \n " ,
sprint_hex ( icc_pk - > rid , 5 ) ,
icc_pk - > index ,
sprint_hex ( icc_pk - > serial , 3 )
) ;
2018-12-28 23:07:58 +08:00
PrintAndLogEx ( INFO , " ICC pk modulus: %s " , sprint_hex_inrow ( icc_pk - > modulus , icc_pk - > mlen ) ) ;
// icc_pk->exp, icc_pk->elen
// icc_pk->modulus, icc_pk->mlen
2018-12-28 22:43:15 +08:00
if ( icc_pk - > elen > 0 & & icc_pk - > mlen > 0 ) {
2018-12-29 02:33:24 +08:00
if ( emv_rocacheck ( icc_pk - > modulus , icc_pk - > mlen , true ) ) {
2018-12-28 23:50:51 +08:00
PrintAndLogEx ( INFO , " ICC pk is a subject to ROCA vulnerability, insecure.. " ) ;
2018-12-28 22:43:15 +08:00
} else {
PrintAndLogEx ( INFO , " ICC pk is OK( " ) ;
}
2018-12-29 02:33:24 +08:00
}
2018-12-26 18:02:00 +08:00
PKISetStrictExecution ( true ) ;
}
out :
// free tlv object
tlvdb_free ( tlvRoot ) ;
if ( channel = = ECC_CONTACTLESS )
DropField ( ) ;
return 0 ;
}
2017-11-25 10:29:26 +08:00
static command_t CommandTable [ ] = {
2018-12-04 00:16:25 +08:00
{ " help " , CmdHelp , 1 , " This help " } ,
{ " exec " , CmdEMVExec , 0 , " Executes EMV contactless transaction. " } ,
{ " pse " , CmdEMVPPSE , 0 , " Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory. " } ,
{ " search " , CmdEMVSearch , 0 , " Try to select all applets from applets list and print installed applets. " } ,
{ " select " , CmdEMVSelect , 0 , " Select applet. " } ,
{ " gpo " , CmdEMVGPO , 0 , " Execute GetProcessingOptions. " } ,
{ " readrec " , CmdEMVReadRecord , 0 , " Read files from card. " } ,
{ " genac " , CmdEMVAC , 0 , " Generate ApplicationCryptogram. " } ,
{ " challenge " , CmdEMVGenerateChallenge , 0 , " Generate challenge. " } ,
{ " intauth " , CmdEMVInternalAuthenticate , 0 , " Internal authentication. " } ,
{ " scan " , CmdEMVScan , 0 , " Scan EMV card and save it contents to json file for emulator. " } ,
{ " test " , CmdEMVTest , 0 , " Crypto logic test. " } ,
2017-12-06 04:07:03 +08:00
/*
2018-12-26 18:02:00 +08:00
{ " getrng " , CmdEMVGetrng , 0 , " get random number from terminal " } ,
{ " eload " , CmdEmvELoad , 0 , " load EMV tag into device " } ,
{ " dump " , CmdEmvDump , 0 , " dump EMV tag values " } ,
{ " sim " , CmdEmvSim , 0 , " simulate EMV tag " } ,
{ " clone " , CmdEmvClone , 0 , " clone an EMV tag " } ,
2017-12-06 04:07:03 +08:00
*/
2018-12-26 18:02:00 +08:00
{ " list " , CmdEMVList , 0 , " [Deprecated] List ISO7816 history " } ,
{ " roca " , CmdEMVRoca , 0 , " Extract public keys and run ROCA test " } ,
2017-11-25 10:29:26 +08:00
{ NULL , NULL , 0 , NULL }
} ;
2018-12-04 00:16:25 +08:00
int CmdEMV ( const char * Cmd ) {
2017-11-25 15:18:30 +08:00
clearCommandBuffer ( ) ;
2017-11-25 10:29:26 +08:00
CmdsParse ( CommandTable , Cmd ) ;
return 0 ;
}
int CmdHelp ( const char * Cmd ) {
CmdsHelp ( CommandTable ) ;
return 0 ;
}