2018-11-08 23:29:58 +08:00
//-----------------------------------------------------------------------------
// Copyright (C) 2018 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 MIFARE Plus commands
//-----------------------------------------------------------------------------
//
// Documentation here:
//
// FIDO Alliance specifications
// https://fidoalliance.org/download/
// FIDO NFC Protocol Specification v1.0
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
// FIDO U2F Raw Message Formats
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
//-----------------------------------------------------------------------------
# include "cmdhffido.h"
# include <unistd.h>
2019-08-08 22:57:33 +08:00
# include "cmdparser.h" // command_t
# include "commonutil.h"
2018-11-08 23:29:58 +08:00
# include "comms.h"
# include "proxmark3.h"
# include "emv/emvcore.h"
# include "emv/emvjson.h"
2020-04-19 16:56:16 +08:00
# include "cliparser.h"
2020-04-19 18:53:46 +08:00
# include "crypto/asn1utils.h"
# include "crypto/libpcrypto.h"
2018-11-17 00:33:10 +08:00
# include "fido/cbortools.h"
2018-11-17 22:19:09 +08:00
# include "fido/fidocore.h"
2019-08-08 22:57:33 +08:00
# include "ui.h"
# include "cmdhf14a.h"
2020-10-10 02:19:36 +08:00
# include "cmdtrace.h"
2020-11-13 01:43:03 +08:00
# include "util.h"
2018-11-08 23:29:58 +08:00
static int CmdHelp ( const char * Cmd ) ;
2020-10-10 02:19:36 +08:00
static int cmd_hf_fido_list ( const char * Cmd ) {
2020-10-10 02:46:48 +08:00
char args [ 128 ] = { 0 } ;
2020-10-10 02:19:36 +08:00
if ( strlen ( Cmd ) = = 0 ) {
snprintf ( args , sizeof ( args ) , " -t 14a " ) ;
2020-10-10 02:46:48 +08:00
} else {
strncpy ( args , Cmd , sizeof ( args ) - 1 ) ;
2020-10-10 02:19:36 +08:00
}
return CmdTraceList ( args ) ;
}
2020-12-23 09:09:19 +08:00
static int cmd_hf_fido_info ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf fido info " ,
" Get info from Fido tags " ,
" hf fido info " ) ;
2019-03-09 15:59:13 +08:00
2020-12-23 09:09:19 +08:00
void * argtable [ ] = {
arg_param_begin ,
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
CLIParserFree ( ctx ) ;
2019-03-10 06:35:06 +08:00
// info about 14a part
2019-11-05 01:54:51 +08:00
infoHF14A ( false , false , false ) ;
2019-03-10 06:35:06 +08:00
// FIDO info
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " ----------- " _CYAN_ ( " FIDO Info " ) " --------------------------------- " ) ;
2019-03-10 06:35:06 +08:00
SetAPDULogging ( false ) ;
uint8_t buf [ APDU_RES_LEN ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
int res = FIDOSelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ) {
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
if ( sw )
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( INFO , " Not a FIDO card! APDU response: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2019-03-10 06:35:06 +08:00
else
2019-04-17 20:33:09 +08:00
PrintAndLogEx ( ERR , " APDU exchange error. Card returns 0x0000. " ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
2019-03-10 06:35:06 +08:00
}
if ( ! strncmp ( ( char * ) buf , " U2F_V2 " , 7 ) ) {
if ( ! strncmp ( ( char * ) buf , " FIDO_2_0 " , 8 ) ) {
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( INFO , " FIDO2 authenticator detected. Version: %.*s " , ( int ) len , buf ) ;
2019-03-10 06:35:06 +08:00
} else {
2019-09-03 04:46:06 +08:00
PrintAndLogEx ( INFO , " FIDO authenticator detected (not standard U2F). " ) ;
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( INFO , " Non U2F authenticator version: " ) ;
2020-11-13 01:43:03 +08:00
print_buffer ( ( const unsigned char * ) buf , len , 1 ) ;
2019-03-10 06:35:06 +08:00
}
} else {
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( INFO , " FIDO U2F authenticator detected. Version: %.*s " , ( int ) len , buf ) ;
2019-03-10 06:35:06 +08:00
}
res = FIDO2GetInfo ( buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
return res ;
}
if ( sw ! = 0x9000 ) {
2019-09-03 04:46:06 +08:00
PrintAndLogEx ( ERR , " FIDO2 version doesn't exist (%04x - %s). " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
if ( buf [ 0 ] ) {
2019-09-03 04:46:06 +08:00
PrintAndLogEx ( ERR , " FIDO2 get version error: %d - %s " , buf [ 0 ] , fido2GetCmdErrorDescription ( buf [ 0 ] ) ) ;
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
2019-03-10 06:35:06 +08:00
}
if ( len > 1 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " FIDO2 version CBOR decoded: " ) ;
2019-03-10 06:35:06 +08:00
TinyCborPrintFIDOPackage ( fido2CmdGetInfo , true , & buf [ 1 ] , len - 1 ) ;
} else {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " FIDO2 version length error " ) ;
2019-03-10 06:35:06 +08:00
}
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
2018-11-08 23:29:58 +08:00
}
2020-06-01 23:30:33 +08:00
static json_t * OpenJson ( CLIParserContext * ctx , int paramnum , char * fname , void * argtable [ ] , bool * err ) {
2019-03-10 06:35:06 +08:00
json_t * root = NULL ;
json_error_t error ;
* err = false ;
2020-06-03 20:52:27 +08:00
uint8_t jsonname [ FILE_PATH_SIZE ] = { 0 } ;
2019-03-10 06:35:06 +08:00
char * cjsonname = ( char * ) jsonname ;
int jsonnamelen = 0 ;
2020-06-01 23:30:33 +08:00
// CLIGetStrWithReturn(ctx, paramnum, jsonname, &jsonnamelen);
2020-06-10 17:34:33 +08:00
if ( CLIParamStrToBuf ( arg_get_str ( ctx , paramnum ) , jsonname , sizeof ( jsonname ) , & jsonnamelen ) ) {
2019-03-10 06:35:06 +08:00
return NULL ;
}
// current path + file name
if ( ! strstr ( cjsonname , " .json " ) )
strcat ( cjsonname , " .json " ) ;
if ( jsonnamelen ) {
strcpy ( fname , get_my_executable_directory ( ) ) ;
strcat ( fname , cjsonname ) ;
if ( access ( fname , F_OK ) ! = - 1 ) {
root = json_load_file ( fname , 0 , & error ) ;
if ( ! root ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: json error on line %d: %s " , error . line , error . text ) ;
2019-03-10 06:35:06 +08:00
* err = true ;
return NULL ;
}
if ( ! json_is_object ( root ) ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: Invalid json format. root must be an object. " ) ;
2019-03-10 06:35:06 +08:00
json_decref ( root ) ;
* err = true ;
return NULL ;
}
} else {
root = json_object ( ) ;
}
}
return root ;
2018-11-08 23:29:58 +08:00
}
2020-10-10 02:19:36 +08:00
static int cmd_hf_fido_register ( const char * cmd ) {
2019-03-10 06:35:06 +08:00
uint8_t data [ 64 ] = { 0 } ;
int chlen = 0 ;
uint8_t cdata [ 250 ] = { 0 } ;
int applen = 0 ;
uint8_t adata [ 250 ] = { 0 } ;
2020-06-01 23:30:33 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf fido reg " ,
2019-09-03 04:46:06 +08:00
" Initiate a U2F token registration. Needs two 32-byte hash numbers. \n challenge parameter (32b) and application parameter (32b). " ,
2020-09-30 14:39:08 +08:00
" hf fido reg -> execute command with 2 parameters, filled 0x00 \n "
" hf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters \n "
" hf fido reg -p s0 s1 -> execute command with plain parameters " ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
void * argtable [ ] = {
2019-03-10 06:35:06 +08:00
arg_param_begin ,
2020-09-30 14:39:08 +08:00
arg_lit0 ( " a " , " apdu " , " show APDU requests and responses " ) ,
arg_litn ( " v " , " verbose " , 0 , 2 , " show technical data. vv - show full certificates data " ) ,
arg_lit0 ( " p " , " plain " , " send plain ASCII to challenge and application parameters instead of HEX " ) ,
arg_lit0 ( " t " , " tlv " , " Show DER certificate contents in TLV representation " ) ,
arg_str0 ( " j " , " json " , " fido.json " , " JSON input / output file name for parameters. " ) ,
2019-03-10 06:35:06 +08:00
arg_str0 ( NULL , NULL , " <HEX/ASCII challenge parameter (32b HEX/1..16 chars)> " , NULL ) ,
arg_str0 ( NULL , NULL , " <HEX/ASCII application parameter (32b HEX/1..16 chars)> " , NULL ) ,
arg_param_end
} ;
2020-06-01 23:30:33 +08:00
CLIExecWithReturn ( ctx , cmd , argtable , true ) ;
2019-03-10 06:35:06 +08:00
2020-06-10 17:34:33 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
bool verbose2 = arg_get_lit ( ctx , 2 ) > 1 ;
bool paramsPlain = arg_get_lit ( ctx , 3 ) ;
bool showDERTLV = arg_get_lit ( ctx , 4 ) ;
2019-03-10 06:35:06 +08:00
2020-06-03 20:52:27 +08:00
char fname [ FILE_PATH_SIZE ] = { 0 } ;
2019-03-10 06:35:06 +08:00
bool err ;
2020-06-03 20:52:27 +08:00
json_t * root = OpenJson ( ctx , 5 , fname , argtable , & err ) ;
2020-06-03 17:18:17 +08:00
if ( err ) {
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2020-06-03 17:18:17 +08:00
}
2019-03-10 06:35:06 +08:00
if ( root ) {
size_t jlen ;
JsonLoadBufAsHex ( root , " $.ChallengeParam " , data , 32 , & jlen ) ;
JsonLoadBufAsHex ( root , " $.ApplicationParam " , & data [ 32 ] , 32 , & jlen ) ;
}
if ( paramsPlain ) {
memset ( cdata , 0x00 , 32 ) ;
2020-10-17 06:20:33 +08:00
chlen = sizeof ( cdata ) ;
2020-06-01 23:30:33 +08:00
CLIGetStrWithReturn ( ctx , 6 , cdata , & chlen ) ;
2019-08-05 06:31:51 +08:00
if ( chlen > 16 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d " , chlen ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
} else {
2020-10-17 06:20:33 +08:00
chlen = sizeof ( cdata ) ;
2020-06-01 23:30:33 +08:00
CLIGetHexWithReturn ( ctx , 6 , cdata , & chlen ) ;
2019-03-10 06:35:06 +08:00
if ( chlen & & chlen ! = 32 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: challenge parameter length must be 32 bytes only. " ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
}
if ( chlen )
memmove ( data , cdata , 32 ) ;
if ( paramsPlain ) {
memset ( adata , 0x00 , 32 ) ;
2020-10-17 06:20:33 +08:00
applen = sizeof ( adata ) ;
2020-06-01 23:30:33 +08:00
CLIGetStrWithReturn ( ctx , 7 , adata , & applen ) ;
2019-08-05 06:31:51 +08:00
if ( applen > 16 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d " , applen ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
} else {
2020-10-17 06:20:33 +08:00
applen = sizeof ( adata ) ;
2020-06-01 23:30:33 +08:00
CLIGetHexWithReturn ( ctx , 7 , adata , & applen ) ;
2019-03-10 06:35:06 +08:00
if ( applen & & applen ! = 32 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: application parameter length must be 32 bytes only. " ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
}
if ( applen )
memmove ( & data [ 32 ] , adata , 32 ) ;
2020-06-01 23:30:33 +08:00
CLIParserFree ( ctx ) ;
2019-03-10 06:35:06 +08:00
SetAPDULogging ( APDULogging ) ;
// challenge parameter [32 bytes] - The challenge parameter is the SHA-256 hash of the Client Data, a stringified JSON data structure that the FIDO Client prepares
// application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
uint8_t buf [ 2048 ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
DropField ( ) ;
int res = FIDOSelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't select authenticator. res=%x. Exit... " , res ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't select FIDO application. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
res = FIDORegister ( data , buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't execute register command. res=%x. Exit... " , res ) ;
2019-03-10 06:35:06 +08:00
return res ;
}
if ( sw ! = 0x9000 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR execute register command. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
2019-03-10 06:35:06 +08:00
if ( APDULogging )
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " --------------------------------------------------------------- " ) ;
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " data len: %zu " , len ) ;
2020-06-03 20:52:27 +08:00
2019-03-10 06:35:06 +08:00
if ( verbose2 ) {
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " ------------ " _CYAN_ ( " data " ) " ---------------------- " ) ;
2020-11-13 01:43:03 +08:00
print_buffer ( ( const unsigned char * ) buf , len , 1 ) ;
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " ------------- " _CYAN_ ( " data " ) " ---------------------- " ) ;
2019-03-10 06:35:06 +08:00
}
if ( buf [ 0 ] ! = 0x05 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: First byte must be 0x05, but it %2x " , buf [ 0 ] ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " User public key: %s " , sprint_hex ( & buf [ 1 ] , 65 ) ) ;
2019-03-10 06:35:06 +08:00
uint8_t keyHandleLen = buf [ 66 ] ;
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " Key handle[%d]: %s " , keyHandleLen , sprint_hex ( & buf [ 67 ] , keyHandleLen ) ) ;
2019-03-10 06:35:06 +08:00
int derp = 67 + keyHandleLen ;
int derLen = ( buf [ derp + 2 ] < < 8 ) + buf [ derp + 3 ] + 4 ;
if ( verbose2 ) {
2020-11-12 01:05:05 +08:00
PrintAndLogEx ( INFO , " DER certificate[%d]: " , derLen ) ;
PrintAndLogEx ( INFO , " ------------------DER------------------- " ) ;
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " %s " , sprint_hex ( buf + derp , derLen ) ) ;
PrintAndLogEx ( INFO , " ----------------DER--------------------- " ) ;
2019-03-10 06:35:06 +08:00
} else {
if ( verbose )
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " ------------------DER------------------- " ) ;
PrintAndLogEx ( INFO , " DER certificate[%d]: %s... " , derLen , sprint_hex ( & buf [ derp ] , 20 ) ) ;
2019-03-10 06:35:06 +08:00
}
// check and print DER certificate
uint8_t public_key [ 65 ] = { 0 } ;
// print DER certificate in TLV view
if ( showDERTLV ) {
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " ----------------DER TLV----------------- " ) ;
2019-03-10 06:35:06 +08:00
asn1_print ( & buf [ derp ] , derLen , " " ) ;
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " ----------------DER TLV----------------- " ) ;
2019-03-10 06:35:06 +08:00
}
2019-03-09 15:59:13 +08:00
2018-11-22 19:41:44 +08:00
FIDOCheckDERAndGetKey ( & buf [ derp ] , derLen , verbose , public_key , sizeof ( public_key ) ) ;
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
// get hash
int hashp = 1 + 65 + 1 + keyHandleLen + derLen ;
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( SUCCESS , " Hash[%zu]: %s " , len - hashp , sprint_hex ( & buf [ hashp ] , len - hashp ) ) ;
2019-03-10 06:35:06 +08:00
// check ANSI X9.62 format ECDSA signature (on P-256)
uint8_t rval [ 300 ] = { 0 } ;
uint8_t sval [ 300 ] = { 0 } ;
res = ecdsa_asn1_get_signature ( & buf [ hashp ] , len - hashp , rval , sval ) ;
2020-11-13 04:51:05 +08:00
if ( res = = PM3_SUCCESS ) {
2019-03-10 06:35:06 +08:00
if ( verbose ) {
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " r: %s " , sprint_hex ( rval , 32 ) ) ;
PrintAndLogEx ( INFO , " s: %s " , sprint_hex ( sval , 32 ) ) ;
2019-03-10 06:35:06 +08:00
}
uint8_t xbuf [ 4096 ] = { 0 } ;
size_t xbuflen = 0 ;
res = FillBuffer ( xbuf , sizeof ( xbuf ) , & xbuflen ,
2019-03-10 07:00:59 +08:00
" \x00 " , 1 ,
& data [ 32 ] , 32 , // application parameter
& data [ 0 ] , 32 , // challenge parameter
& buf [ 67 ] , keyHandleLen , // keyHandle
& buf [ 1 ] , 65 , // user public key
NULL , 0 ) ;
2020-05-28 04:43:48 +08:00
( void ) res ;
2020-11-12 01:01:04 +08:00
//PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
2019-07-28 05:44:23 +08:00
res = ecdsa_signature_verify ( MBEDTLS_ECP_DP_SECP256R1 , public_key , xbuf , xbuflen , & buf [ hashp ] , len - hashp , true ) ;
2019-03-10 06:35:06 +08:00
if ( res ) {
2019-07-28 05:44:23 +08:00
if ( res = = MBEDTLS_ERR_ECP_VERIFY_FAILED ) {
2019-07-28 05:48:52 +08:00
PrintAndLogEx ( WARNING , " Signature is " _RED_ ( " NOT VALID " ) ) ;
2019-03-10 06:35:06 +08:00
} else {
2019-07-14 18:17:34 +08:00
PrintAndLogEx ( WARNING , " Other signature check error: %x %s " , ( res < 0 ) ? - res : res , ecdsa_get_error ( res ) ) ;
2019-03-10 06:35:06 +08:00
}
} else {
2019-07-28 05:44:23 +08:00
PrintAndLogEx ( SUCCESS , " Signature is " _GREEN_ ( " OK " ) ) ;
2019-03-10 06:35:06 +08:00
}
} else {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( WARNING , " Invalid signature. res = %d. " , res ) ;
2019-03-10 06:35:06 +08:00
}
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( INFO , " \n auth command: " ) ;
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " hf fido auth %s%s " , paramsPlain ? " -p " : " " , sprint_hex_inrow ( & buf [ 67 ] , keyHandleLen ) ) ;
2019-03-10 07:00:59 +08:00
if ( chlen | | applen )
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " %s " , paramsPlain ? ( char * ) cdata : sprint_hex_inrow ( cdata , 32 ) ) ;
2019-03-10 07:00:59 +08:00
if ( applen )
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " %s " , paramsPlain ? ( char * ) adata : sprint_hex_inrow ( adata , 32 ) ) ;
2019-03-10 06:35:06 +08:00
if ( root ) {
JsonSaveBufAsHex ( root , " ChallengeParam " , data , 32 ) ;
JsonSaveBufAsHex ( root , " ApplicationParam " , & data [ 32 ] , 32 ) ;
JsonSaveBufAsHexCompact ( root , " PublicKey " , & buf [ 1 ] , 65 ) ;
JsonSaveInt ( root , " KeyHandleLen " , keyHandleLen ) ;
JsonSaveBufAsHexCompact ( root , " KeyHandle " , & buf [ 67 ] , keyHandleLen ) ;
JsonSaveBufAsHexCompact ( root , " DER " , & buf [ 67 + keyHandleLen ] , derLen ) ;
res = json_dump_file ( root , fname , JSON_INDENT ( 2 ) ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: can't save the file: %s " , fname ) ;
2020-06-03 20:52:27 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " File " _YELLOW_ ( " `%s` " ) " saved. " , fname ) ;
2019-03-10 06:35:06 +08:00
// free json object
json_decref ( root ) ;
}
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
}
2018-11-08 23:29:58 +08:00
2020-10-10 02:19:36 +08:00
static int cmd_hf_fido_authenticate ( const char * cmd ) {
2019-03-10 06:35:06 +08:00
uint8_t data [ 512 ] = { 0 } ;
uint8_t hdata [ 250 ] = { 0 } ;
bool public_key_loaded = false ;
uint8_t public_key [ 65 ] = { 0 } ;
int hdatalen = 0 ;
uint8_t keyHandleLen = 0 ;
2020-06-01 23:30:33 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf fido auth " ,
2019-09-03 04:46:06 +08:00
" Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers. \n key handle(var 0..255), challenge parameter (32b) and application parameter (32b). " ,
2020-09-30 14:39:08 +08:00
" hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle \n "
" hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
2019-03-10 07:00:59 +08:00
" 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters " ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
void * argtable [ ] = {
2019-03-10 06:35:06 +08:00
arg_param_begin ,
2020-09-30 14:39:08 +08:00
arg_lit0 ( " a " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " v " , " verbose " , " show technical data " ) ,
arg_lit0 ( " p " , " plain " , " send plain ASCII to challenge and application parameters instead of HEX " ) ,
2019-03-10 06:35:06 +08:00
arg_rem ( " default mode: " , " dont-enforce-user-presence-and-sign " ) ,
2020-09-30 14:39:08 +08:00
arg_lit0 ( " u " , " user " , " mode: enforce-user-presence-and-sign " ) ,
arg_lit0 ( " c " , " check " , " mode: check-only " ) ,
arg_str0 ( " j " , " json " , " fido.json " , " JSON input / output file name for parameters. " ) ,
arg_str0 ( " k " , " key " , " public key to verify signature " , NULL ) ,
2019-03-10 06:35:06 +08:00
arg_str0 ( NULL , NULL , " <HEX key handle (var 0..255b)> " , NULL ) ,
arg_str0 ( NULL , NULL , " <HEX/ASCII challenge parameter (32b HEX/1..16 chars)> " , NULL ) ,
arg_str0 ( NULL , NULL , " <HEX/ASCII application parameter (32b HEX/1..16 chars)> " , NULL ) ,
arg_param_end
} ;
2020-06-01 23:30:33 +08:00
CLIExecWithReturn ( ctx , cmd , argtable , true ) ;
2019-03-10 06:35:06 +08:00
2020-06-10 17:34:33 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
bool paramsPlain = arg_get_lit ( ctx , 3 ) ;
2019-03-10 06:35:06 +08:00
uint8_t controlByte = 0x08 ;
2020-06-10 17:34:33 +08:00
if ( arg_get_lit ( ctx , 5 ) )
2019-03-10 06:35:06 +08:00
controlByte = 0x03 ;
2020-06-10 17:34:33 +08:00
if ( arg_get_lit ( ctx , 6 ) )
2019-03-10 06:35:06 +08:00
controlByte = 0x07 ;
char fname [ 250 ] = { 0 } ;
bool err ;
2020-06-03 20:52:27 +08:00
json_t * root = OpenJson ( ctx , 7 , fname , argtable , & err ) ;
2020-06-03 17:18:17 +08:00
if ( err ) {
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2020-06-03 17:18:17 +08:00
}
2020-06-03 20:52:27 +08:00
2019-03-10 06:35:06 +08:00
if ( root ) {
size_t jlen ;
JsonLoadBufAsHex ( root , " $.ChallengeParam " , data , 32 , & jlen ) ;
JsonLoadBufAsHex ( root , " $.ApplicationParam " , & data [ 32 ] , 32 , & jlen ) ;
JsonLoadBufAsHex ( root , " $.KeyHandle " , & data [ 65 ] , 512 - 67 , & jlen ) ;
keyHandleLen = jlen & 0xff ;
data [ 64 ] = keyHandleLen ;
JsonLoadBufAsHex ( root , " $.PublicKey " , public_key , 65 , & jlen ) ;
public_key_loaded = ( jlen > 0 ) ;
}
// public key
2020-06-01 23:30:33 +08:00
CLIGetHexWithReturn ( ctx , 8 , hdata , & hdatalen ) ;
2019-03-10 06:35:06 +08:00
if ( hdatalen & & hdatalen ! = 65 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: public key length must be 65 bytes only. " ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
2020-06-08 02:11:36 +08:00
2019-03-10 06:35:06 +08:00
if ( hdatalen ) {
memmove ( public_key , hdata , hdatalen ) ;
public_key_loaded = true ;
}
2020-06-01 23:30:33 +08:00
CLIGetHexWithReturn ( ctx , 9 , hdata , & hdatalen ) ;
2019-03-10 06:35:06 +08:00
if ( hdatalen > 255 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: application parameter length must be less than 255. " ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
2020-06-08 02:11:36 +08:00
2019-03-10 06:35:06 +08:00
if ( hdatalen ) {
keyHandleLen = hdatalen ;
data [ 64 ] = keyHandleLen ;
memmove ( & data [ 65 ] , hdata , keyHandleLen ) ;
}
if ( paramsPlain ) {
memset ( hdata , 0x00 , 32 ) ;
2020-10-17 06:20:33 +08:00
hdatalen = sizeof ( hdata ) ;
2020-06-01 23:30:33 +08:00
CLIGetStrWithReturn ( ctx , 9 , hdata , & hdatalen ) ;
2019-08-05 06:31:51 +08:00
if ( hdatalen > 16 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d " , hdatalen ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
} else {
2020-10-17 06:20:33 +08:00
hdatalen = sizeof ( hdata ) ;
2020-06-01 23:30:33 +08:00
CLIGetHexWithReturn ( ctx , 10 , hdata , & hdatalen ) ;
2019-03-10 06:35:06 +08:00
if ( hdatalen & & hdatalen ! = 32 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: challenge parameter length must be 32 bytes only. " ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
}
2020-06-08 02:11:36 +08:00
2019-03-10 06:35:06 +08:00
if ( hdatalen )
memmove ( data , hdata , 32 ) ;
if ( paramsPlain ) {
memset ( hdata , 0x00 , 32 ) ;
2020-10-17 06:20:33 +08:00
hdatalen = sizeof ( hdata ) ;
2020-06-01 23:30:33 +08:00
CLIGetStrWithReturn ( ctx , 11 , hdata , & hdatalen ) ;
2019-08-05 06:31:51 +08:00
if ( hdatalen > 16 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d " , hdatalen ) ;
2020-06-08 02:11:36 +08:00
CLIParserFree ( ctx ) ;
2020-06-03 20:52:27 +08:00
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
} else {
2020-10-17 06:20:33 +08:00
hdatalen = sizeof ( hdata ) ;
2020-06-01 23:30:33 +08:00
CLIGetHexWithReturn ( ctx , 10 , hdata , & hdatalen ) ;
2019-03-10 06:35:06 +08:00
if ( hdatalen & & hdatalen ! = 32 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: application parameter length must be 32 bytes only. " ) ;
2020-06-03 20:52:27 +08:00
CLIParserFree ( ctx ) ;
return PM3_EINVARG ;
2019-03-10 06:35:06 +08:00
}
}
2020-06-08 02:11:36 +08:00
2019-03-10 06:35:06 +08:00
if ( hdatalen )
memmove ( & data [ 32 ] , hdata , 32 ) ;
2020-06-01 23:30:33 +08:00
CLIParserFree ( ctx ) ;
2019-03-10 06:35:06 +08:00
SetAPDULogging ( APDULogging ) ;
// (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
// challenge parameter [32 bytes]
// application parameter [32 bytes]
// key handle length [1b] = N
// key handle [N]
uint8_t datalen = 32 + 32 + 1 + keyHandleLen ;
uint8_t buf [ 2048 ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
DropField ( ) ;
int res = FIDOSelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't select authenticator. res=%x. Exit... " , res ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't select FIDO application. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
res = FIDOAuthentication ( data , datalen , controlByte , buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't execute authentication command. res=%x. Exit... " , res ) ;
2019-03-10 06:35:06 +08:00
return res ;
}
if ( sw ! = 0x9000 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR execute authentication command. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " --------------------------------------------------------------- " ) ;
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " User presence: %s " , ( buf [ 0 ] ? " verified " : " not verified " ) ) ;
2019-03-10 07:00:59 +08:00
uint32_t cntr = ( uint32_t ) bytes_to_num ( & buf [ 1 ] , 4 ) ;
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " Counter: %d " , cntr ) ;
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( SUCCESS , " Hash[%zu]: %s " , len - 5 , sprint_hex ( & buf [ 5 ] , len - 5 ) ) ;
2019-03-10 06:35:06 +08:00
// check ANSI X9.62 format ECDSA signature (on P-256)
uint8_t rval [ 300 ] = { 0 } ;
uint8_t sval [ 300 ] = { 0 } ;
res = ecdsa_asn1_get_signature ( & buf [ 5 ] , len - 5 , rval , sval ) ;
2020-11-13 04:51:05 +08:00
if ( res = = PM3_SUCCESS ) {
2019-03-10 06:35:06 +08:00
if ( verbose ) {
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " r: %s " , sprint_hex ( rval , 32 ) ) ;
PrintAndLogEx ( INFO , " s: %s " , sprint_hex ( sval , 32 ) ) ;
2019-03-10 06:35:06 +08:00
}
if ( public_key_loaded ) {
uint8_t xbuf [ 4096 ] = { 0 } ;
size_t xbuflen = 0 ;
res = FillBuffer ( xbuf , sizeof ( xbuf ) , & xbuflen ,
2019-03-10 07:00:59 +08:00
& data [ 32 ] , 32 , // application parameter
& buf [ 0 ] , 1 , // user presence
& buf [ 1 ] , 4 , // counter
data , 32 , // challenge parameter
NULL , 0 ) ;
2020-05-28 04:43:48 +08:00
( void ) res ;
2020-11-12 01:01:04 +08:00
//PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
2019-07-28 05:44:23 +08:00
res = ecdsa_signature_verify ( MBEDTLS_ECP_DP_SECP256R1 , public_key , xbuf , xbuflen , & buf [ 5 ] , len - 5 , true ) ;
2019-03-10 06:35:06 +08:00
if ( res ) {
2019-07-28 05:44:23 +08:00
if ( res = = MBEDTLS_ERR_ECP_VERIFY_FAILED ) {
2019-07-28 05:48:52 +08:00
PrintAndLogEx ( WARNING , " Signature is " _RED_ ( " NOT VALID. " ) ) ;
2019-03-10 06:35:06 +08:00
} else {
2019-07-14 18:17:34 +08:00
PrintAndLogEx ( WARNING , " Other signature check error: %x %s " , ( res < 0 ) ? - res : res , ecdsa_get_error ( res ) ) ;
2019-03-10 06:35:06 +08:00
}
} else {
2019-07-28 05:48:52 +08:00
PrintAndLogEx ( SUCCESS , " Signature is " _GREEN_ ( " OK " ) ) ;
2019-03-10 06:35:06 +08:00
}
} else {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( WARNING , " No public key provided. can't check signature. " ) ;
2019-03-10 06:35:06 +08:00
}
} else {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Invalid signature. res = %d. " , res ) ;
2019-03-10 06:35:06 +08:00
}
if ( root ) {
JsonSaveBufAsHex ( root , " ChallengeParam " , data , 32 ) ;
JsonSaveBufAsHex ( root , " ApplicationParam " , & data [ 32 ] , 32 ) ;
JsonSaveInt ( root , " KeyHandleLen " , keyHandleLen ) ;
JsonSaveBufAsHexCompact ( root , " KeyHandle " , & data [ 65 ] , keyHandleLen ) ;
JsonSaveInt ( root , " Counter " , cntr ) ;
res = json_dump_file ( root , fname , JSON_INDENT ( 2 ) ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: can't save the file: %s " , fname ) ;
2020-06-03 20:52:27 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " File " _YELLOW_ ( " `%s` " ) " saved. " , fname ) ;
2019-03-10 06:35:06 +08:00
// free json object
json_decref ( root ) ;
}
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
}
2018-11-08 23:29:58 +08:00
2019-04-14 03:54:04 +08:00
static void CheckSlash ( char * fileName ) {
2019-03-10 06:35:06 +08:00
if ( ( fileName [ strlen ( fileName ) - 1 ] ! = ' / ' ) & &
2019-03-10 18:20:22 +08:00
( fileName [ strlen ( fileName ) - 1 ] ! = ' \\ ' ) )
2019-03-10 06:35:06 +08:00
strcat ( fileName , " / " ) ;
2018-11-21 01:31:08 +08:00
}
2020-06-03 20:52:27 +08:00
//iceman, todo: use searchfile..
2019-04-14 03:54:04 +08:00
static int GetExistsFileNameJson ( const char * prefixDir , const char * reqestedFileName , char * fileName ) {
2019-03-10 06:35:06 +08:00
fileName [ 0 ] = 0x00 ;
strcpy ( fileName , get_my_executable_directory ( ) ) ;
CheckSlash ( fileName ) ;
strcat ( fileName , prefixDir ) ;
CheckSlash ( fileName ) ;
strcat ( fileName , reqestedFileName ) ;
if ( ! strstr ( fileName , " .json " ) )
strcat ( fileName , " .json " ) ;
if ( access ( fileName , F_OK ) < 0 ) {
strcpy ( fileName , get_my_executable_directory ( ) ) ;
CheckSlash ( fileName ) ;
strcat ( fileName , reqestedFileName ) ;
if ( ! strstr ( fileName , " .json " ) )
strcat ( fileName , " .json " ) ;
if ( access ( fileName , F_OK ) < 0 ) {
2020-06-03 20:52:27 +08:00
return PM3_EFILE ; // file not found
2019-03-10 06:35:06 +08:00
}
}
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
2018-11-21 01:21:36 +08:00
}
2020-10-10 02:19:36 +08:00
static int cmd_hf_fido_2make_credential ( const char * cmd ) {
2019-03-10 06:35:06 +08:00
json_error_t error ;
2020-06-03 20:52:27 +08:00
char fname [ FILE_PATH_SIZE ] = { 0 } ;
2019-03-10 06:35:06 +08:00
2020-06-01 23:30:33 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf fido make " ,
2020-09-30 14:39:08 +08:00
" Execute a FIDO2 Make Credential command. Needs json file with parameters. Sample file `fido2.json` in `resources/`. " ,
" hf fido make -> execute command with default parameters file `fido2.json` \n "
" hf fido make test.json -> execute command with parameters file `text.json` " ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
void * argtable [ ] = {
2019-03-10 06:35:06 +08:00
arg_param_begin ,
2020-09-30 14:39:08 +08:00
arg_lit0 ( " a " , " apdu " , " show APDU reqests and responses " ) ,
arg_litn ( " v " , " verbose " , 0 , 2 , " show technical data. vv - show full certificates data " ) ,
arg_lit0 ( " t " , " tlv " , " Show DER certificate contents in TLV representation " ) ,
arg_lit0 ( " c " , " cbor " , " show CBOR decoded data " ) ,
2019-03-10 06:35:06 +08:00
arg_str0 ( NULL , NULL , " <json file name> " , " JSON input / output file name for parameters. Default `fido2.json` " ) ,
arg_param_end
} ;
2020-06-01 23:30:33 +08:00
CLIExecWithReturn ( ctx , cmd , argtable , true ) ;
2019-03-10 06:35:06 +08:00
2020-06-10 17:34:33 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
bool verbose2 = arg_get_lit ( ctx , 2 ) > 1 ;
bool showDERTLV = arg_get_lit ( ctx , 3 ) ;
bool showCBOR = arg_get_lit ( ctx , 4 ) ;
2019-03-10 06:35:06 +08:00
2020-06-03 20:52:27 +08:00
uint8_t jsonname [ FILE_PATH_SIZE ] = { 0 } ;
2019-03-10 06:35:06 +08:00
char * cjsonname = ( char * ) jsonname ;
int jsonnamelen = 0 ;
2020-10-17 06:20:33 +08:00
jsonnamelen = sizeof ( jsonname ) ;
2020-06-01 23:30:33 +08:00
CLIGetStrWithReturn ( ctx , 5 , jsonname , & jsonnamelen ) ;
2019-03-10 06:35:06 +08:00
if ( ! jsonnamelen ) {
strcat ( cjsonname , " fido2 " ) ;
jsonnamelen = strlen ( cjsonname ) ;
}
2020-06-01 23:30:33 +08:00
CLIParserFree ( ctx ) ;
2019-03-10 06:35:06 +08:00
SetAPDULogging ( APDULogging ) ;
int res = GetExistsFileNameJson ( " fido " , cjsonname , fname ) ;
2020-06-03 20:52:27 +08:00
if ( res ! = PM3_SUCCESS ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: Can't found the json file. " ) ;
2019-03-10 06:35:06 +08:00
return res ;
}
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " fname: %s \n " , fname ) ;
2020-06-03 20:52:27 +08:00
json_t * root = json_load_file ( fname , 0 , & error ) ;
2019-03-10 06:35:06 +08:00
if ( ! root ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: json error on line %d: %s " , error . line , error . text ) ;
2020-06-03 20:52:27 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
uint8_t data [ 2048 ] = { 0 } ;
size_t datalen = 0 ;
uint8_t buf [ 2048 ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
DropField ( ) ;
res = FIDOSelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't select authenticator. res=%x. Exit... " , res ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't select FIDO application. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
res = FIDO2CreateMakeCredentionalReq ( root , data , sizeof ( data ) , & datalen ) ;
if ( res )
return res ;
if ( showCBOR ) {
2019-09-03 04:46:06 +08:00
PrintAndLogEx ( INFO , " CBOR make credential request: " ) ;
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " ---------------- " _CYAN_ ( " CBOR " ) " ------------------ " ) ;
2019-03-10 06:35:06 +08:00
TinyCborPrintFIDOPackage ( fido2CmdMakeCredential , false , data , datalen ) ;
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " ---------------- " _CYAN_ ( " CBOR " ) " ------------------ " ) ;
2019-03-10 06:35:06 +08:00
}
res = FIDO2MakeCredential ( data , datalen , buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't execute make credential command. res=%x. Exit... " , res ) ;
2019-03-10 06:35:06 +08:00
return res ;
}
if ( sw ! = 0x9000 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR execute make credential command. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2020-06-03 20:52:27 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
if ( buf [ 0 ] ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " FIDO2 make credential error: %d - %s " , buf [ 0 ] , fido2GetCmdErrorDescription ( buf [ 0 ] ) ) ;
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
2019-03-10 06:35:06 +08:00
}
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( SUCCESS , " MakeCredential result (%zu b) OK. " , len ) ;
2019-03-10 06:35:06 +08:00
if ( showCBOR ) {
2019-09-03 04:46:06 +08:00
PrintAndLogEx ( SUCCESS , " CBOR make credential response: " ) ;
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " ---------------- " _CYAN_ ( " CBOR " ) " ------------------ " ) ;
2019-03-10 06:35:06 +08:00
TinyCborPrintFIDOPackage ( fido2CmdMakeCredential , true , & buf [ 1 ] , len - 1 ) ;
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( INFO , " ---------------- " _CYAN_ ( " CBOR " ) " ------------------ " ) ;
2019-03-10 06:35:06 +08:00
}
// parse returned cbor
FIDO2MakeCredentionalParseRes ( root , & buf [ 1 ] , len - 1 , verbose , verbose2 , showCBOR , showDERTLV ) ;
if ( root ) {
res = json_dump_file ( root , fname , JSON_INDENT ( 2 ) ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: can't save the file: %s " , fname ) ;
2020-06-03 20:52:27 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " File " _YELLOW_ ( " `%s` " ) " saved. " , fname ) ;
2019-03-10 06:35:06 +08:00
}
json_decref ( root ) ;
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
}
2018-11-18 06:03:28 +08:00
2020-10-10 02:19:36 +08:00
static int cmd_hf_fido_2get_assertion ( const char * cmd ) {
2019-03-10 06:35:06 +08:00
json_error_t error ;
2020-06-03 20:52:27 +08:00
char fname [ FILE_PATH_SIZE ] = { 0 } ;
2019-03-10 06:35:06 +08:00
2020-06-01 23:30:33 +08:00
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf fido assert " ,
2020-09-30 14:39:08 +08:00
" Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file `fido2.json` in `resources/`. " ,
" hf fido assert -> execute command with default parameters file `fido2.json` \n "
" hf fido assert test.json -l -> execute command with parameters file `text.json` and add to request CredentialId " ) ;
2019-03-10 06:35:06 +08:00
2019-03-10 07:00:59 +08:00
void * argtable [ ] = {
2019-03-10 06:35:06 +08:00
arg_param_begin ,
2020-09-30 14:39:08 +08:00
arg_lit0 ( " a " , " apdu " , " show APDU reqests and responses " ) ,
arg_litn ( " v " , " verbose " , 0 , 2 , " show technical data. vv - show full certificates data " ) ,
arg_lit0 ( " c " , " cbor " , " show CBOR decoded data " ) ,
arg_lit0 ( " l " , " list " , " add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator doesn't store credential to its memory) " ) ,
2019-03-10 06:35:06 +08:00
arg_str0 ( NULL , NULL , " <json file name> " , " JSON input / output file name for parameters. Default `fido2.json` " ) ,
arg_param_end
} ;
2020-06-01 23:30:33 +08:00
CLIExecWithReturn ( ctx , cmd , argtable , true ) ;
2019-03-10 06:35:06 +08:00
2020-06-10 17:34:33 +08:00
bool APDULogging = arg_get_lit ( ctx , 1 ) ;
bool verbose = arg_get_lit ( ctx , 2 ) ;
bool verbose2 = arg_get_lit ( ctx , 2 ) > 1 ;
bool showCBOR = arg_get_lit ( ctx , 3 ) ;
bool createAllowList = arg_get_lit ( ctx , 4 ) ;
2019-03-10 06:35:06 +08:00
2020-06-03 20:52:27 +08:00
uint8_t jsonname [ FILE_PATH_SIZE ] = { 0 } ;
2019-03-10 06:35:06 +08:00
char * cjsonname = ( char * ) jsonname ;
2020-10-17 06:20:33 +08:00
int jsonnamelen = sizeof ( jsonname ) ;
2020-06-01 23:30:33 +08:00
CLIGetStrWithReturn ( ctx , 5 , jsonname , & jsonnamelen ) ;
2019-03-10 06:35:06 +08:00
if ( ! jsonnamelen ) {
strcat ( cjsonname , " fido2 " ) ;
jsonnamelen = strlen ( cjsonname ) ;
}
2020-06-01 23:30:33 +08:00
CLIParserFree ( ctx ) ;
2019-03-10 06:35:06 +08:00
SetAPDULogging ( APDULogging ) ;
2019-09-03 04:51:28 +08:00
int res = GetExistsFileNameJson ( " fido " , cjsonname , fname ) ;
2020-06-03 20:52:27 +08:00
if ( res ! = PM3_SUCCESS ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: Can't found the json file. " ) ;
2019-03-10 06:35:06 +08:00
return res ;
}
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " fname: %s \n " , fname ) ;
2020-06-03 20:52:27 +08:00
json_t * root = json_load_file ( fname , 0 , & error ) ;
2019-03-10 06:35:06 +08:00
if ( ! root ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: json error on line %d: %s " , error . line , error . text ) ;
2020-06-03 20:52:27 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
uint8_t data [ 2048 ] = { 0 } ;
size_t datalen = 0 ;
uint8_t buf [ 2048 ] = { 0 } ;
size_t len = 0 ;
uint16_t sw = 0 ;
DropField ( ) ;
res = FIDOSelect ( true , true , buf , sizeof ( buf ) , & len , & sw ) ;
if ( res ) {
2020-06-03 20:52:27 +08:00
PrintAndLogEx ( ERR , " Can't select authenticator. res=%x. exiting... " , res ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't select FIDO application. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2019-03-10 06:35:06 +08:00
DropField ( ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
res = FIDO2CreateGetAssertionReq ( root , data , sizeof ( data ) , & datalen , createAllowList ) ;
if ( res )
return res ;
if ( showCBOR ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " CBOR get assertion request: " ) ;
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " ---------------- " _CYAN_ ( " CBOR " ) " ------------------ " ) ;
2019-03-10 06:35:06 +08:00
TinyCborPrintFIDOPackage ( fido2CmdGetAssertion , false , data , datalen ) ;
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " ---------------- " _CYAN_ ( " CBOR " ) " ------------------ " ) ;
2019-03-10 06:35:06 +08:00
}
res = FIDO2GetAssertion ( data , datalen , buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " Can't execute get assertion command. res=%x. Exit... " , res ) ;
2019-03-10 06:35:06 +08:00
return res ;
}
if ( sw ! = 0x9000 ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR execute get assertion command. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-03-10 07:00:59 +08:00
if ( buf [ 0 ] ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " FIDO2 get assertion error: %d - %s " , buf [ 0 ] , fido2GetCmdErrorDescription ( buf [ 0 ] ) ) ;
2020-06-03 20:52:27 +08:00
return PM3_ESOFT ;
2019-03-10 06:35:06 +08:00
}
2019-10-06 05:56:19 +08:00
PrintAndLogEx ( SUCCESS , " GetAssertion result (%zu b) OK. " , len ) ;
2019-03-10 06:35:06 +08:00
if ( showCBOR ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " CBOR get assertion response: " ) ;
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " ---------------- " _CYAN_ ( " CBOR " ) " ------------------ " ) ;
2019-03-10 06:35:06 +08:00
TinyCborPrintFIDOPackage ( fido2CmdGetAssertion , true , & buf [ 1 ] , len - 1 ) ;
2020-11-12 01:01:04 +08:00
PrintAndLogEx ( INFO , " ---------------- " _CYAN_ ( " CBOR " ) " ------------------ " ) ;
2019-03-10 06:35:06 +08:00
}
// parse returned cbor
FIDO2GetAssertionParseRes ( root , & buf [ 1 ] , len - 1 , verbose , verbose2 , showCBOR ) ;
if ( root ) {
res = json_dump_file ( root , fname , JSON_INDENT ( 2 ) ) ;
if ( res ) {
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( ERR , " ERROR: can't save the file: %s " , fname ) ;
2020-06-03 20:52:27 +08:00
return PM3_EFILE ;
2019-03-10 06:35:06 +08:00
}
2019-04-17 17:47:32 +08:00
PrintAndLogEx ( SUCCESS , " File " _YELLOW_ ( " `%s` " ) " saved. " , fname ) ;
2019-03-10 06:35:06 +08:00
}
json_decref ( root ) ;
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
}
2018-11-18 06:03:28 +08:00
2019-03-10 07:00:59 +08:00
static command_t CommandTable [ ] = {
2020-10-12 04:40:05 +08:00
{ " help " , CmdHelp , AlwaysAvailable , " This help. " } ,
2020-10-13 23:19:18 +08:00
{ " list " , cmd_hf_fido_list , IfPm3Iso14443a , " List ISO 14443A history " } ,
2020-10-10 02:19:36 +08:00
{ " info " , cmd_hf_fido_info , IfPm3Iso14443a , " Info about FIDO tag. " } ,
{ " reg " , cmd_hf_fido_register , IfPm3Iso14443a , " FIDO U2F Registration Message. " } ,
{ " auth " , cmd_hf_fido_authenticate , IfPm3Iso14443a , " FIDO U2F Authentication Message. " } ,
{ " make " , cmd_hf_fido_2make_credential , IfPm3Iso14443a , " FIDO2 MakeCredential command. " } ,
{ " assert " , cmd_hf_fido_2get_assertion , IfPm3Iso14443a , " FIDO2 GetAssertion command. " } ,
{ NULL , NULL , 0 , NULL }
2018-11-08 23:29:58 +08:00
} ;
2019-03-10 18:20:22 +08:00
int CmdHFFido ( const char * Cmd ) {
2020-06-03 20:52:27 +08:00
clearCommandBuffer ( ) ;
2019-04-19 06:47:51 +08:00
return CmdsParse ( CommandTable , Cmd ) ;
2018-11-08 23:29:58 +08:00
}
2019-03-10 18:20:22 +08:00
int CmdHelp ( const char * Cmd ) {
2019-04-10 18:23:40 +08:00
( void ) Cmd ; // Cmd is not used so far
2019-03-10 07:00:59 +08:00
CmdsHelp ( CommandTable ) ;
2020-06-03 20:52:27 +08:00
return PM3_SUCCESS ;
2018-11-08 23:29:58 +08:00
}