2021-05-28 23:54:44 +08:00
//-----------------------------------------------------------------------------
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency FIDO U2F and FIDO2 contactless authenticators
//-----------------------------------------------------------------------------
//
// JAVA implementation here:
//
// https://github.com/duychuongvn/cipurse-card-core
//-----------------------------------------------------------------------------
# include "cmdhffido.h"
# include <unistd.h>
# include "cmdparser.h" // command_t
# include "commonutil.h"
# include "comms.h"
# include "proxmark3.h"
# include "emv/emvcore.h"
# include "emv/emvjson.h"
# include "cliparser.h"
# include "cmdhfcipurse.h"
# include "cipurse/cipursecore.h"
2021-05-29 20:21:53 +08:00
# include "cipurse/cipursecrypto.h"
2021-05-28 23:54:44 +08:00
# include "ui.h"
# include "cmdhf14a.h"
# include "cmdtrace.h"
# include "util.h"
# include "fileutils.h" // laodFileJSONroot
static int CmdHelp ( const char * Cmd ) ;
static int CmdHFCipurseInfo ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf cipurse info " ,
" Get info from cipurse tags " ,
" hf cipurse info " ) ;
void * argtable [ ] = {
arg_param_begin ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
CLIParserFree ( ctx ) ;
// info about 14a part
infoHF14A ( false , false , false ) ;
// CIPURSE info
PrintAndLogEx ( INFO , " ----------- " _CYAN_ ( " CIPURSE Info " ) " --------------------------------- " ) ;
SetAPDULogging ( false ) ;
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
int res = CIPURSESelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ) {
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
if ( sw )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( INFO , " Not a CIPURSE card. APDU response: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2021-05-28 23:54:44 +08:00
else
2021-06-20 14:41:18 +08:00
PrintAndLogEx ( ERR , " APDU exchange error. Card returns 0x0000 " ) ;
2021-05-28 23:54:44 +08:00
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-06-20 14:01:01 +08:00
PrintAndLogEx ( INFO , " Cipurse card ( " _GREEN_ ( " ok " ) " ) " ) ;
2021-05-28 23:54:44 +08:00
2021-06-03 00:50:35 +08:00
res = CIPURSESelectFile ( 0x2ff7 , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-05-28 23:54:44 +08:00
2021-06-03 00:50:35 +08:00
res = CIPURSEReadBinary ( 0 , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 00:50:35 +08:00
if ( len > 0 ) {
2021-06-20 14:01:01 +08:00
PrintAndLogEx ( INFO , " Info file ( " _GREEN_ ( " ok " ) " ) " ) ;
PrintAndLogEx ( INFO , " [%zu]: %s " , len , sprint_hex ( buf , len ) ) ;
2021-06-03 00:59:41 +08:00
CIPURSEPrintInfoFile ( buf , len ) ;
2021-06-03 00:50:35 +08:00
}
2021-05-28 23:54:44 +08:00
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-05-29 00:57:43 +08:00
static int CmdHFCipurseAuth ( const char * Cmd ) {
2021-05-29 23:37:23 +08:00
CLIParserContext * ctx ;
2021-05-30 02:28:19 +08:00
CLIParserInit ( & ctx , " hf cipurse auth " ,
2021-06-20 14:38:33 +08:00
" Authenticate with key ID and key. If no key is supplied, default key of 737373...7373 will be used " ,
" hf cipurse auth -> Authenticate with keyID 1, default key \n "
" hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> Authenticate keyID 2 with key \n " ) ;
2021-05-29 23:37:23 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " a " , " apdu " , " show APDU requests and responses " ) ,
arg_lit0 ( " v " , " verbose " , " show technical data " ) ,
2021-06-20 14:38:33 +08:00
arg_int0 ( " n " , NULL , " <dec> " , " key ID " ) ,
arg_str0 ( " k " , " key " , " <hex> " , " Auth key " ) ,
2021-05-29 23:37:23 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
2021-06-04 00:13:11 +08:00
2021-05-29 23:37:23 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
2021-06-20 14:38:33 +08:00
uint8_t keyId = arg_get_int_def ( ctx , 3 , 1 ) ;
2021-06-04 00:13:11 +08:00
2021-05-29 23:37:23 +08:00
uint8_t hdata [ 250 ] = { 0 } ;
2021-06-04 00:13:11 +08:00
int hdatalen = sizeof ( hdata ) ;
2021-05-29 23:37:23 +08:00
CLIGetHexWithReturn ( ctx , 4 , hdata , & hdatalen ) ;
if ( hdatalen & & hdatalen ! = 16 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " key length for AES128 must be 16 bytes only " ) ;
2021-05-29 23:37:23 +08:00
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
2021-06-23 21:18:05 +08:00
2021-06-20 14:38:33 +08:00
uint8_t key [ ] = { 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 , 0x73 } ;
2021-05-29 23:37:23 +08:00
if ( hdatalen )
memcpy ( key , hdata , CIPURSE_AES_KEY_LENGTH ) ;
2021-06-04 00:13:11 +08:00
2021-05-29 23:37:23 +08:00
SetAPDULogging ( APDULogging ) ;
2021-05-30 02:46:22 +08:00
CLIParserFree ( ctx ) ;
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
size_t len = 0 ;
uint16_t sw = 0 ;
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
2021-05-29 00:57:43 +08:00
int res = CIPURSESelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " Cipurse select " _RED_ ( " error " ) " . Card returns 0x%04x " , sw ) ;
2021-05-29 00:57:43 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-05-29 20:51:08 +08:00
uint8_t kvv [ CIPURSE_KVV_LENGTH ] = { 0 } ;
CipurseCGetKVV ( key , kvv ) ;
2021-06-20 14:38:33 +08:00
if ( verbose ) {
PrintAndLogEx ( INFO , " Key id " _YELLOW_ ( " %d " ) " key " _YELLOW_ ( " %s " ) " KVV " _YELLOW_ ( " %s " )
2021-06-23 21:18:05 +08:00
, keyId
, sprint_hex ( key , CIPURSE_AES_KEY_LENGTH )
, sprint_hex_inrow ( kvv , CIPURSE_KVV_LENGTH )
) ;
2021-06-20 14:38:33 +08:00
}
2021-05-29 00:57:43 +08:00
2021-05-30 02:46:22 +08:00
bool bres = CIPURSEChannelAuthenticate ( keyId , key , verbose ) ;
2021-06-04 00:13:11 +08:00
2021-05-30 02:46:22 +08:00
if ( verbose = = false ) {
if ( bres )
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( INFO , " Authentication ( " _GREEN_ ( " ok " ) " ) " ) ;
2021-05-30 02:46:22 +08:00
else
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( ERR , " Authentication ( " _RED_ ( " fail " ) " ) " ) ;
2021-05-29 21:09:34 +08:00
}
2021-06-04 00:13:11 +08:00
2021-05-29 00:57:43 +08:00
DropField ( ) ;
2021-06-20 14:38:33 +08:00
return ( bres ) ? PM3_SUCCESS : PM3_ESOFT ;
2021-05-29 00:57:43 +08:00
}
2021-06-03 20:29:46 +08:00
static int CLIParseKeyAndSecurityLevels ( CLIParserContext * ctx , size_t keyid , size_t sreqid , size_t srespid , uint8_t * key , CipurseChannelSecurityLevel * sreq , CipurseChannelSecurityLevel * sresp ) {
uint8_t hdata [ 250 ] = { 0 } ;
2021-06-04 00:13:11 +08:00
int hdatalen = sizeof ( hdata ) ;
2021-06-23 22:24:27 +08:00
if ( CLIParamHexToBuf ( arg_get_str ( ctx , keyid ) , hdata , hdatalen , & hdatalen ) )
return PM3_ESOFT ;
2021-06-03 20:29:46 +08:00
if ( hdatalen & & hdatalen ! = 16 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " key length for AES128 must be 16 bytes only " ) ;
2021-06-03 20:29:46 +08:00
return PM3_EINVARG ;
}
if ( hdatalen )
memcpy ( key , hdata , CIPURSE_AES_KEY_LENGTH ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 20:29:46 +08:00
* sreq = CPSMACed ;
* sresp = CPSMACed ;
char cdata [ 250 ] = { 0 } ;
2021-06-04 00:13:11 +08:00
int cdatalen = sizeof ( cdata ) ;
2021-06-03 20:29:46 +08:00
cdatalen - - ; // for trailer 0x00
2021-06-23 22:24:27 +08:00
if ( CLIParamStrToBuf ( arg_get_str ( ctx , sreqid ) , ( uint8_t * ) cdata , cdatalen , & cdatalen ) )
return PM3_ESOFT ;
2021-06-03 20:29:46 +08:00
if ( cdatalen ) {
str_lower ( cdata ) ;
if ( strcmp ( cdata , " plain " ) = = 0 )
* sreq = CPSPlain ;
else if ( strcmp ( cdata , " mac " ) = = 0 )
* sreq = CPSMACed ;
else if ( strcmp ( cdata , " enc " ) = = 0 | | strcmp ( cdata , " encode " ) = = 0 | | strcmp ( cdata , " encrypted " ) = = 0 )
* sreq = CPSEncrypted ;
else {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " security level can be only: plain | mac | encode " ) ;
2021-06-03 20:29:46 +08:00
return PM3_EINVARG ;
}
}
2021-06-04 00:13:11 +08:00
cdatalen = sizeof ( cdata ) ;
2021-06-03 20:29:46 +08:00
memset ( cdata , 0 , cdatalen ) ;
cdatalen - - ; // for trailer 0x00
2021-06-23 22:24:27 +08:00
if ( CLIParamStrToBuf ( arg_get_str ( ctx , srespid ) , ( uint8_t * ) cdata , cdatalen , & cdatalen ) )
return PM3_ESOFT ;
2021-06-03 20:29:46 +08:00
if ( cdatalen ) {
str_lower ( cdata ) ;
if ( strcmp ( cdata , " plain " ) = = 0 )
* sresp = CPSPlain ;
else if ( strcmp ( cdata , " mac " ) = = 0 )
* sresp = CPSMACed ;
else if ( strcmp ( cdata , " enc " ) = = 0 | | strcmp ( cdata , " encode " ) = = 0 | | strcmp ( cdata , " encrypted " ) = = 0 )
* sresp = CPSEncrypted ;
else {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " security level can be only: plain | mac | encode " ) ;
2021-06-03 20:29:46 +08:00
return PM3_EINVARG ;
}
}
2021-06-04 00:13:11 +08:00
2021-06-03 20:29:46 +08:00
return PM3_SUCCESS ;
}
2021-06-03 00:42:04 +08:00
static int CmdHFCipurseReadFile ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf cipurse read " ,
2021-06-20 14:38:33 +08:00
" Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used " ,
2021-06-23 22:24:27 +08:00
" hf cipurse read --fid 2ff7 -> Authenticate with keyID 1, read file with id 2ff7 \n "
2021-06-20 14:38:33 +08:00
" hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and read file \n " ) ;
2021-06-03 00:42:04 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " a " , " apdu " , " show APDU requests and responses " ) ,
arg_lit0 ( " v " , " verbose " , " show technical data " ) ,
2021-06-20 14:38:33 +08:00
arg_int0 ( " n " , NULL , " <dec> " , " key ID " ) ,
arg_str0 ( " k " , " key " , " <hex> " , " Auth key " ) ,
2021-06-23 22:24:27 +08:00
arg_str0 ( NULL , " fid " , " <hex> " , " file ID " ) ,
2021-06-03 00:42:04 +08:00
arg_int0 ( " o " , " offset " , " <dec> " , " offset for reading data from file " ) ,
2021-06-03 19:53:51 +08:00
arg_lit0 ( NULL , " noauth " , " read file without authentication " ) ,
2021-06-03 20:29:46 +08:00
arg_str0 ( NULL , " sreq " , " <plain|mac(default)|encode> " , " communication reader-PICC security level " ) ,
arg_str0 ( NULL , " sresp " , " <plain|mac(default)|encode> " , " communication PICC-reader security level " ) ,
2021-06-03 00:42:04 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
2021-06-03 00:42:04 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
uint8_t keyId = arg_get_int_def ( ctx , 3 , 1 ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 20:29:46 +08:00
CipurseChannelSecurityLevel sreq = CPSMACed ;
CipurseChannelSecurityLevel sresp = CPSMACed ;
2021-06-20 14:38:33 +08:00
uint8_t key [ ] = CIPURSE_DEFAULT_KEY ;
2021-06-03 20:29:46 +08:00
int res = CLIParseKeyAndSecurityLevels ( ctx , 4 , 8 , 9 , key , & sreq , & sresp ) ;
if ( res ) {
2021-06-03 00:42:04 +08:00
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
2021-06-03 20:29:46 +08:00
uint8_t hdata [ 250 ] = { 0 } ;
2021-06-04 00:13:11 +08:00
int hdatalen = sizeof ( hdata ) ;
2021-06-03 00:42:04 +08:00
CLIGetHexWithReturn ( ctx , 5 , hdata , & hdatalen ) ;
if ( hdatalen & & hdatalen ! = 2 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " file id length must be 2 bytes only " ) ;
2021-06-03 00:42:04 +08:00
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
2021-06-20 14:38:33 +08:00
uint16_t fileId = 0x2ff7 ;
2021-06-03 00:42:04 +08:00
if ( hdatalen )
fileId = ( hdata [ 0 ] < < 8 ) + hdata [ 1 ] ;
2021-06-04 00:13:11 +08:00
2021-06-03 00:42:04 +08:00
size_t offset = arg_get_int_def ( ctx , 6 , 0 ) ;
2021-06-03 19:53:51 +08:00
bool noAuth = arg_get_lit ( ctx , 7 ) ;
2021-06-03 00:42:04 +08:00
SetAPDULogging ( APDULogging ) ;
CLIParserFree ( ctx ) ;
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
size_t len = 0 ;
uint16_t sw = 0 ;
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
2021-06-03 20:29:46 +08:00
res = CIPURSESelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
2021-06-03 00:42:04 +08:00
if ( res ! = 0 | | sw ! = 0x9000 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " Cipurse select " _RED_ ( " error " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 00:42:04 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 00:42:04 +08:00
if ( verbose )
2021-06-20 14:02:33 +08:00
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %x " ) " offset " _YELLOW_ ( " %zu " ) " key id " _YELLOW_ ( " %d " ) " key " _YELLOW_ ( " %s " ) , fileId , offset , keyId , sprint_hex ( key , CIPURSE_AES_KEY_LENGTH ) ) ;
2021-06-03 00:42:04 +08:00
2021-06-03 19:53:51 +08:00
if ( noAuth = = false ) {
bool bres = CIPURSEChannelAuthenticate ( keyId , key , verbose ) ;
if ( bres = = false ) {
if ( verbose = = false )
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( ERR , " Authentication ( " _RED_ ( " fail " ) " ) " ) ;
2021-06-03 19:53:51 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 20:29:46 +08:00
// set channel security levels
CIPURSECSetActChannelSecurityLevels ( sreq , sresp ) ;
2021-06-03 00:42:04 +08:00
}
2021-06-04 00:13:11 +08:00
2021-06-03 00:42:04 +08:00
res = CIPURSESelectFile ( fileId , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
if ( verbose = = false )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " File select " _RED_ ( " ERROR " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 00:42:04 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
if ( verbose )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( INFO , " Select file 0x%x ( " _GREEN_ ( " ok " ) " ) " , fileId ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 00:42:04 +08:00
res = CIPURSEReadBinary ( offset , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
if ( verbose = = false )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " File read " _RED_ ( " ERROR " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 00:42:04 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 00:42:04 +08:00
if ( len = = 0 )
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %x " ) " is empty " , fileId ) ;
2021-06-03 00:42:04 +08:00
else
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %x " ) " data[%zu]: %s " , fileId , len , sprint_hex ( buf , len ) ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 00:42:04 +08:00
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-05-29 00:57:43 +08:00
2021-06-03 21:03:11 +08:00
static int CmdHFCipurseWriteFile ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf cipurse write " ,
2021-06-20 14:38:33 +08:00
" Write file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used " ,
2021-06-23 22:24:27 +08:00
" hf cipurse write --fid 2ff7 -> Authenticate with keyID 1, write file with id 2ff7 \n "
2021-06-20 14:38:33 +08:00
" hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and write file \n " ) ;
2021-06-03 21:03:11 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " a " , " apdu " , " show APDU requests and responses " ) ,
arg_lit0 ( " v " , " verbose " , " show technical data " ) ,
2021-06-20 14:38:33 +08:00
arg_int0 ( " n " , NULL , " <dec> " , " key ID " ) ,
arg_str0 ( " k " , " key " , " <hex> " , " Auth key " ) ,
2021-06-23 22:24:27 +08:00
arg_str0 ( NULL , " fid " , " <hex> " , " file ID " ) ,
2021-06-03 21:03:11 +08:00
arg_int0 ( " o " , " offset " , " <dec> " , " offset for reading data from file " ) ,
arg_lit0 ( NULL , " noauth " , " read file without authentication " ) ,
arg_str0 ( NULL , " sreq " , " <plain|mac(default)|encode> " , " communication reader-PICC security level " ) ,
arg_str0 ( NULL , " sresp " , " <plain|mac(default)|encode> " , " communication PICC-reader security level " ) ,
2021-06-20 14:38:33 +08:00
arg_str0 ( " d " , " data " , " <hex> " , " hex data to write to new file " ) ,
2021-06-03 21:03:11 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
uint8_t keyId = arg_get_int_def ( ctx , 3 , 1 ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
CipurseChannelSecurityLevel sreq = CPSMACed ;
CipurseChannelSecurityLevel sresp = CPSMACed ;
2021-06-20 14:38:33 +08:00
uint8_t key [ ] = CIPURSE_DEFAULT_KEY ;
2021-06-03 21:03:11 +08:00
int res = CLIParseKeyAndSecurityLevels ( ctx , 4 , 8 , 9 , key , & sreq , & sresp ) ;
if ( res ) {
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
uint16_t fileId = 0x2ff7 ;
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
uint8_t hdata [ 250 ] = { 0 } ;
2021-06-04 00:13:11 +08:00
int hdatalen = sizeof ( hdata ) ;
2021-06-03 21:03:11 +08:00
CLIGetHexWithReturn ( ctx , 5 , hdata , & hdatalen ) ;
if ( hdatalen & & hdatalen ! = 2 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " file id length must be 2 bytes only " ) ;
2021-06-03 21:03:11 +08:00
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
if ( hdatalen )
fileId = ( hdata [ 0 ] < < 8 ) + hdata [ 1 ] ;
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
size_t offset = arg_get_int_def ( ctx , 6 , 0 ) ;
bool noAuth = arg_get_lit ( ctx , 7 ) ;
2021-06-04 00:13:11 +08:00
hdatalen = sizeof ( hdata ) ;
2021-06-03 21:03:11 +08:00
CLIGetHexWithReturn ( ctx , 10 , hdata , & hdatalen ) ;
if ( hdatalen = = 0 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " file content length must be more 0 " ) ;
2021-06-03 21:03:11 +08:00
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
SetAPDULogging ( APDULogging ) ;
CLIParserFree ( ctx ) ;
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
size_t len = 0 ;
uint16_t sw = 0 ;
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
2021-06-03 21:03:11 +08:00
res = CIPURSESelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " Cipurse select " _RED_ ( " error " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 21:03:11 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
if ( verbose ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %x " ) " offset " _YELLOW_ ( " %zu " ) " key id " _YELLOW_ ( " %d " ) " key " _YELLOW_ ( " %s " )
2021-06-23 21:18:05 +08:00
, fileId
, offset
, keyId
, sprint_hex ( key , CIPURSE_AES_KEY_LENGTH )
) ;
2021-06-03 21:03:11 +08:00
PrintAndLogEx ( INFO , " data[%d]: %s " , hdatalen , sprint_hex ( hdata , hdatalen ) ) ;
}
if ( noAuth = = false ) {
bool bres = CIPURSEChannelAuthenticate ( keyId , key , verbose ) ;
if ( bres = = false ) {
if ( verbose = = false )
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( ERR , " Authentication ( " _RED_ ( " fail " ) " ) " ) ;
2021-06-03 21:03:11 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
// set channel security levels
CIPURSECSetActChannelSecurityLevels ( sreq , sresp ) ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
res = CIPURSESelectFile ( fileId , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
if ( verbose = = false )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " File select " _RED_ ( " ERROR " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 21:03:11 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
if ( verbose )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( INFO , " Select file 0x%x ( " _GREEN_ ( " ok " ) " ) " , fileId ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
res = CIPURSEUpdateBinary ( offset , hdata , hdatalen , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
if ( verbose = = false )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " File write " _RED_ ( " ERROR " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 21:03:11 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %x " ) " successfully written " , fileId ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 21:03:11 +08:00
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-05-29 00:57:43 +08:00
2021-06-03 22:27:25 +08:00
static int CmdHFCipurseReadFileAttr ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf cipurse aread " ,
2021-06-20 14:38:33 +08:00
" Read file attributes by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used " ,
2021-06-23 22:24:27 +08:00
" hf cipurse aread --fid 2ff7 -> Authenticate with keyID 1, read file attributes with id 2ff7 \n "
2021-06-20 14:38:33 +08:00
" hf cipurse aread -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2, read file attributes \n " ) ;
2021-06-03 22:27:25 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " a " , " apdu " , " show APDU requests and responses " ) ,
arg_lit0 ( " v " , " verbose " , " show technical data " ) ,
2021-06-20 14:38:33 +08:00
arg_int0 ( " n " , NULL , " <dec> " , " key ID " ) ,
arg_str0 ( " k " , " key " , " <hex> " , " Auth key " ) ,
arg_str0 ( NULL , " fid " , " <hex> " , " file ID " ) ,
2021-06-03 22:27:25 +08:00
arg_lit0 ( NULL , " noauth " , " read file attributes without authentication " ) ,
arg_str0 ( NULL , " sreq " , " <plain|mac(default)|encode> " , " communication reader-PICC security level " ) ,
arg_str0 ( NULL , " sresp " , " <plain|mac(default)|encode> " , " communication PICC-reader security level " ) ,
2021-06-20 14:38:33 +08:00
arg_lit0 ( NULL , " sel-adf " , " show info about ADF itself " ) ,
arg_lit0 ( NULL , " sel-mf " , " show info about master file " ) ,
2021-06-03 22:27:25 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
uint8_t keyId = arg_get_int_def ( ctx , 3 , 1 ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
CipurseChannelSecurityLevel sreq = CPSMACed ;
CipurseChannelSecurityLevel sresp = CPSMACed ;
2021-06-20 14:38:33 +08:00
uint8_t key [ ] = CIPURSE_DEFAULT_KEY ;
2021-06-03 22:27:25 +08:00
int res = CLIParseKeyAndSecurityLevels ( ctx , 4 , 7 , 8 , key , & sreq , & sresp ) ;
if ( res ) {
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
uint8_t hdata [ 250 ] = { 0 } ;
2021-06-04 00:13:11 +08:00
int hdatalen = sizeof ( hdata ) ;
2021-06-03 22:27:25 +08:00
CLIGetHexWithReturn ( ctx , 5 , hdata , & hdatalen ) ;
if ( hdatalen & & hdatalen ! = 2 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " file id length must be 2 bytes only " ) ;
2021-06-03 22:27:25 +08:00
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
2021-06-20 14:38:33 +08:00
uint16_t fileId = 0x2ff7 ;
2021-06-03 22:27:25 +08:00
if ( hdatalen )
fileId = ( hdata [ 0 ] < < 8 ) + hdata [ 1 ] ;
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
bool noAuth = arg_get_lit ( ctx , 6 ) ;
bool seladf = arg_get_lit ( ctx , 9 ) ;
bool selmf = arg_get_lit ( ctx , 10 ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
SetAPDULogging ( APDULogging ) ;
CLIParserFree ( ctx ) ;
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2021-06-03 22:27:25 +08:00
res = CIPURSESelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " Cipurse select " _RED_ ( " error " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 22:27:25 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
if ( verbose ) {
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %x " ) " key id " _YELLOW_ ( " %d " ) " key " _YELLOW_ ( " %s " )
2021-06-23 21:18:05 +08:00
, fileId
, keyId
, sprint_hex ( key , CIPURSE_AES_KEY_LENGTH )
) ;
2021-06-20 14:38:33 +08:00
}
2021-06-03 22:27:25 +08:00
if ( noAuth = = false ) {
bool bres = CIPURSEChannelAuthenticate ( keyId , key , verbose ) ;
if ( bres = = false ) {
if ( verbose = = false )
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( ERR , " Authentication ( " _RED_ ( " fail " ) " ) " ) ;
2021-06-03 22:27:25 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
// set channel security levels
CIPURSECSetActChannelSecurityLevels ( sreq , sresp ) ;
}
if ( seladf = = false ) {
if ( selmf )
res = CIPURSESelectMFFile ( buf , sizeof ( buf ) , & len , & sw ) ;
else
res = CIPURSESelectFile ( fileId , buf , sizeof ( buf ) , & len , & sw ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
if ( res ! = 0 | | sw ! = 0x9000 ) {
if ( verbose = = false )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " File select " _RED_ ( " ERROR " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 22:27:25 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
}
if ( verbose )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( INFO , " Select file 0x%x ( " _GREEN_ ( " ok " ) " ) " , fileId ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
res = CIPURSEReadFileAttributes ( buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
if ( verbose = = false )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " File read " _RED_ ( " ERROR " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 22:27:25 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
if ( len = = 0 ) {
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( WARNING , " File id " _YELLOW_ ( " %x " ) " attributes is empty " , fileId ) ;
2021-06-03 22:27:25 +08:00
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
if ( verbose )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %x " ) " attributes[%zu]: %s " , fileId , len , sprint_hex ( buf , len ) ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
CIPURSEPrintFileAttr ( buf , len ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 22:27:25 +08:00
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-05-29 00:57:43 +08:00
2021-06-03 23:21:47 +08:00
static int CmdHFCipurseDeleteFile ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf cipurse delete " ,
2021-06-20 14:38:33 +08:00
" Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used " ,
2021-06-23 22:24:27 +08:00
" hf cipurse delete --fid 2ff7 -> Authenticate with keyID 1, delete file with id 2ff7 \n "
2021-06-20 14:38:33 +08:00
" hf cipurse delete -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and delete file \n " ) ;
2021-06-03 23:21:47 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_lit0 ( " a " , " apdu " , " show APDU requests and responses " ) ,
arg_lit0 ( " v " , " verbose " , " show technical data " ) ,
2021-06-20 14:38:33 +08:00
arg_int0 ( " n " , NULL , " <dec> " , " key ID " ) ,
arg_str0 ( " k " , " key " , " <hex> " , " Auth key " ) ,
arg_str0 ( NULL , " fid " , " <hex> " , " file ID " ) ,
2021-06-03 23:21:47 +08:00
arg_str0 ( NULL , " sreq " , " <plain|mac(default)|encode> " , " communication reader-PICC security level " ) ,
arg_str0 ( NULL , " sresp " , " <plain|mac(default)|encode> " , " communication PICC-reader security level " ) ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 23:21:47 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
uint8_t keyId = arg_get_int_def ( ctx , 3 , 1 ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 23:21:47 +08:00
CipurseChannelSecurityLevel sreq = CPSMACed ;
CipurseChannelSecurityLevel sresp = CPSMACed ;
2021-06-20 14:38:33 +08:00
uint8_t key [ ] = CIPURSE_DEFAULT_KEY ;
2021-06-03 23:21:47 +08:00
int res = CLIParseKeyAndSecurityLevels ( ctx , 4 , 6 , 7 , key , & sreq , & sresp ) ;
if ( res ) {
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
uint8_t hdata [ 250 ] = { 0 } ;
2021-06-04 00:13:11 +08:00
int hdatalen = sizeof ( hdata ) ;
2021-06-03 23:21:47 +08:00
CLIGetHexWithReturn ( ctx , 5 , hdata , & hdatalen ) ;
if ( hdatalen & & hdatalen ! = 2 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , _RED_ ( " ERROR: " ) " file id length must be 2 bytes only " ) ;
2021-06-03 23:21:47 +08:00
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
}
2021-06-20 14:38:33 +08:00
uint16_t fileId = 0x2ff7 ;
2021-06-03 23:21:47 +08:00
if ( hdatalen )
fileId = ( hdata [ 0 ] < < 8 ) + hdata [ 1 ] ;
2021-06-04 00:13:11 +08:00
2021-06-03 23:21:47 +08:00
SetAPDULogging ( APDULogging ) ;
CLIParserFree ( ctx ) ;
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
2021-06-03 23:21:47 +08:00
res = CIPURSESelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " Cipurse select " _RED_ ( " error " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 23:21:47 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-20 14:38:33 +08:00
if ( verbose ) {
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %x " ) " key id " _YELLOW_ ( " %d " ) " key " _YELLOW_ ( " %s " )
2021-06-23 21:18:05 +08:00
, fileId
, keyId
, sprint_hex ( key , CIPURSE_AES_KEY_LENGTH )
) ;
2021-06-20 14:38:33 +08:00
}
2021-06-03 23:21:47 +08:00
bool bres = CIPURSEChannelAuthenticate ( keyId , key , verbose ) ;
if ( bres = = false ) {
if ( verbose = = false )
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( ERR , " Authentication ( " _RED_ ( " fail " ) " ) " ) ;
2021-06-03 23:21:47 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-04 00:13:11 +08:00
2021-06-03 23:21:47 +08:00
// set channel security levels
CIPURSECSetActChannelSecurityLevels ( sreq , sresp ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 23:21:47 +08:00
res = CIPURSEDeleteFile ( fileId , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ! = 0 | | sw ! = 0x9000 ) {
if ( verbose = = false )
2021-06-20 14:38:33 +08:00
PrintAndLogEx ( ERR , " File select " _RED_ ( " ERROR " ) " . Card returns 0x%04x " , sw ) ;
2021-06-03 23:21:47 +08:00
DropField ( ) ;
return PM3_ESOFT ;
}
2021-06-20 13:58:29 +08:00
PrintAndLogEx ( INFO , " File id " _YELLOW_ ( " %04x " ) " deleted " _GREEN_ ( " succesfully " ) , fileId ) ;
2021-06-04 00:13:11 +08:00
2021-06-03 23:21:47 +08:00
DropField ( ) ;
return PM3_SUCCESS ;
}
2021-05-29 00:05:00 +08:00
bool CheckCardCipurse ( void ) {
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
int res = CIPURSESelect ( true , false , buf , sizeof ( buf ) , & len , & sw ) ;
return ( res = = 0 & & sw = = 0x9000 ) ;
}
2021-05-28 23:54:44 +08:00
static command_t CommandTable [ ] = {
{ " help " , CmdHelp , AlwaysAvailable , " This help. " } ,
2021-06-20 14:38:33 +08:00
{ " info " , CmdHFCipurseInfo , IfPm3Iso14443a , " Get info about CIPURSE tag " } ,
{ " auth " , CmdHFCipurseAuth , IfPm3Iso14443a , " Authenticate CIPURSE tag " } ,
{ " read " , CmdHFCipurseReadFile , IfPm3Iso14443a , " Read binary file " } ,
{ " write " , CmdHFCipurseWriteFile , IfPm3Iso14443a , " Write binary file " } ,
{ " aread " , CmdHFCipurseReadFileAttr , IfPm3Iso14443a , " Read file attributes " } ,
{ " delete " , CmdHFCipurseDeleteFile , IfPm3Iso14443a , " Delete file " } ,
2021-05-28 23:54:44 +08:00
{ NULL , NULL , 0 , NULL }
} ;
int CmdHFCipurse ( const char * Cmd ) {
clearCommandBuffer ( ) ;
return CmdsParse ( CommandTable , Cmd ) ;
}
int CmdHelp ( const char * Cmd ) {
( void ) Cmd ; // Cmd is not used so far
CmdsHelp ( CommandTable ) ;
return PM3_SUCCESS ;
}