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 <inttypes.h>
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <ctype.h>
# include <unistd.h>
# include <jansson.h>
2018-11-17 00:33:10 +08:00
# include <mbedtls/x509_crt.h>
# include <mbedtls/x509.h>
# include <mbedtls/pk.h>
2018-11-08 23:29:58 +08:00
# include "comms.h"
# include "cmdmain.h"
# include "util.h"
# include "ui.h"
# include "proxmark3.h"
# include "mifare.h"
# include "emv/emvcore.h"
# include "emv/emvjson.h"
# include "emv/dump.h"
# include "cliparser/cliparser.h"
2018-11-13 01:05:05 +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"
2018-11-26 22:12:28 +08:00
# include "fido/cose.h"
2018-11-08 23:29:58 +08:00
static int CmdHelp ( const char * Cmd ) ;
2019-03-10 18:20:22 +08:00
int CmdHFFidoInfo ( const char * cmd ) {
2019-03-09 15:59:13 +08:00
2019-03-10 06:35:06 +08:00
if ( cmd & & strlen ( cmd ) > 0 )
PrintAndLog ( " WARNING: command don't have any parameters. \n " ) ;
// info about 14a part
CmdHF14AInfo ( " " ) ;
// FIDO info
PrintAndLog ( " -------------------------------------------- " ) ;
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 )
PrintAndLog ( " Not a FIDO card! APDU response: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
else
PrintAndLog ( " APDU exchange error. Card returns 0x0000. " ) ;
DropField ( ) ;
return 0 ;
}
if ( ! strncmp ( ( char * ) buf , " U2F_V2 " , 7 ) ) {
if ( ! strncmp ( ( char * ) buf , " FIDO_2_0 " , 8 ) ) {
PrintAndLog ( " FIDO2 authenricator detected. Version: %.*s " , len , buf ) ;
} else {
PrintAndLog ( " FIDO authenricator detected (not standard U2F). " ) ;
PrintAndLog ( " Non U2F authenticator version: " ) ;
dump_buffer ( ( const unsigned char * ) buf , len , NULL , 0 ) ;
}
} else {
PrintAndLog ( " FIDO U2F authenricator detected. Version: %.*s " , len , buf ) ;
}
res = FIDO2GetInfo ( buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " FIDO2 version not exists (%04x - %s). " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
return 0 ;
}
2019-03-10 07:00:59 +08:00
if ( buf [ 0 ] ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " FIDO2 ger version error: %d - %s " , buf [ 0 ] , fido2GetCmdErrorDescription ( buf [ 0 ] ) ) ;
return 0 ;
}
if ( len > 1 ) {
// if (false) {
// PrintAndLog("FIDO2 version: (len=%d)", len);
// dump_buffer((const unsigned char *)buf, len, NULL, 0);
// }
PrintAndLog ( " FIDO2 version CBOR decoded: " ) ;
TinyCborPrintFIDOPackage ( fido2CmdGetInfo , true , & buf [ 1 ] , len - 1 ) ;
} else {
PrintAndLog ( " FIDO2 version length error " ) ;
}
return 0 ;
2018-11-08 23:29:58 +08:00
}
2019-03-10 18:20:22 +08:00
json_t * OpenJson ( 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 ;
2019-03-10 07:00:59 +08:00
uint8_t jsonname [ 250 ] = { 0 } ;
2019-03-10 06:35:06 +08:00
char * cjsonname = ( char * ) jsonname ;
int jsonnamelen = 0 ;
// CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
if ( CLIParamStrToBuf ( arg_get_str ( paramnum ) , jsonname , sizeof ( jsonname ) , & jsonnamelen ) ) {
CLIParserFree ( ) ;
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 ) {
PrintAndLog ( " ERROR: json error on line %d: %s " , error . line , error . text ) ;
* err = true ;
return NULL ;
}
if ( ! json_is_object ( root ) ) {
PrintAndLog ( " ERROR: Invalid json format. root must be an object. " ) ;
json_decref ( root ) ;
* err = true ;
return NULL ;
}
} else {
root = json_object ( ) ;
}
}
return root ;
2018-11-08 23:29:58 +08:00
}
2019-03-10 18:20:22 +08:00
int CmdHFFidoRegister ( 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 } ;
json_t * root = NULL ;
CLIParserInit ( " hf fido reg " ,
2019-03-10 07:00:59 +08:00
" Initiate a U2F token registration. Needs two 32-byte hash number. \n challenge parameter (32b) and application parameter (32b). " ,
" Usage: \n \t hf fido reg -> execute command with 2 parameters, filled 0x00 \n "
" \t hf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters "
" \t 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 ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_litn ( " vV " , " verbose " , 0 , 2 , " show technical data. vv - show full certificates data " ) ,
arg_lit0 ( " pP " , " plain " , " send plain ASCII to challenge and application parameters instead of HEX " ) ,
arg_lit0 ( " tT " , " tlv " , " Show DER certificate contents in TLV representation " ) ,
arg_str0 ( " jJ " , " json " , " fido.json " , " JSON input / output file name for parameters. " ) ,
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
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool APDULogging = arg_get_lit ( 1 ) ;
bool verbose = arg_get_lit ( 2 ) ;
bool verbose2 = arg_get_lit ( 2 ) > 1 ;
bool paramsPlain = arg_get_lit ( 3 ) ;
bool showDERTLV = arg_get_lit ( 4 ) ;
char fname [ 250 ] = { 0 } ;
bool err ;
root = OpenJson ( 5 , fname , argtable , & err ) ;
2019-03-10 07:00:59 +08:00
if ( err )
2019-03-10 06:35:06 +08:00
return 1 ;
if ( root ) {
size_t jlen ;
JsonLoadBufAsHex ( root , " $.ChallengeParam " , data , 32 , & jlen ) ;
JsonLoadBufAsHex ( root , " $.ApplicationParam " , & data [ 32 ] , 32 , & jlen ) ;
}
if ( paramsPlain ) {
memset ( cdata , 0x00 , 32 ) ;
CLIGetStrWithReturn ( 6 , cdata , & chlen ) ;
2019-03-19 05:35:33 +08:00
if ( chlen > 16 ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d " , chlen ) ;
return 1 ;
}
} else {
CLIGetHexWithReturn ( 6 , cdata , & chlen ) ;
if ( chlen & & chlen ! = 32 ) {
PrintAndLog ( " ERROR: challenge parameter length must be 32 bytes only. " ) ;
return 1 ;
}
}
if ( chlen )
memmove ( data , cdata , 32 ) ;
if ( paramsPlain ) {
memset ( adata , 0x00 , 32 ) ;
CLIGetStrWithReturn ( 7 , adata , & applen ) ;
2019-03-19 05:35:33 +08:00
if ( applen > 16 ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d " , applen ) ;
return 1 ;
}
} else {
CLIGetHexWithReturn ( 7 , adata , & applen ) ;
if ( applen & & applen ! = 32 ) {
PrintAndLog ( " ERROR: application parameter length must be 32 bytes only. " ) ;
return 1 ;
}
}
if ( applen )
memmove ( & data [ 32 ] , adata , 32 ) ;
CLIParserFree ( ) ;
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 ) {
PrintAndLog ( " Can't select authenticator. res=%x. Exit... " , res ) ;
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " Can't select FIDO application. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
DropField ( ) ;
return 2 ;
}
res = FIDORegister ( data , buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
PrintAndLog ( " Can't execute register command. res=%x. Exit... " , res ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " ERROR execute register command. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
return 3 ;
}
PrintAndLog ( " " ) ;
if ( APDULogging )
PrintAndLog ( " --------------------------------------------------------------- " ) ;
PrintAndLog ( " data len: %d " , len ) ;
if ( verbose2 ) {
PrintAndLog ( " --------------data---------------------- " ) ;
dump_buffer ( ( const unsigned char * ) buf , len , NULL , 0 ) ;
PrintAndLog ( " --------------data---------------------- " ) ;
}
if ( buf [ 0 ] ! = 0x05 ) {
PrintAndLog ( " ERROR: First byte must be 0x05, but it %2x " , buf [ 0 ] ) ;
return 5 ;
}
PrintAndLog ( " User public key: %s " , sprint_hex ( & buf [ 1 ] , 65 ) ) ;
uint8_t keyHandleLen = buf [ 66 ] ;
PrintAndLog ( " Key handle[%d]: %s " , keyHandleLen , sprint_hex ( & buf [ 67 ] , keyHandleLen ) ) ;
int derp = 67 + keyHandleLen ;
int derLen = ( buf [ derp + 2 ] < < 8 ) + buf [ derp + 3 ] + 4 ;
if ( verbose2 ) {
PrintAndLog ( " DER certificate[%d]: \n ------------------DER------------------- " , derLen ) ;
dump_buffer_simple ( ( const unsigned char * ) & buf [ derp ] , derLen , NULL ) ;
PrintAndLog ( " \n ----------------DER--------------------- " ) ;
} else {
if ( verbose )
PrintAndLog ( " ------------------DER------------------- " ) ;
PrintAndLog ( " DER certificate[%d]: %s... " , derLen , sprint_hex ( & buf [ derp ] , 20 ) ) ;
}
// check and print DER certificate
uint8_t public_key [ 65 ] = { 0 } ;
// print DER certificate in TLV view
if ( showDERTLV ) {
PrintAndLog ( " ----------------DER TLV----------------- " ) ;
asn1_print ( & buf [ derp ] , derLen , " " ) ;
PrintAndLog ( " ----------------DER TLV----------------- " ) ;
}
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 ;
PrintAndLog ( " Hash[%d]: %s " , len - hashp , sprint_hex ( & buf [ hashp ] , len - hashp ) ) ;
// 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 ) ;
if ( ! res ) {
if ( verbose ) {
PrintAndLog ( " r: %s " , sprint_hex ( rval , 32 ) ) ;
PrintAndLog ( " s: %s " , sprint_hex ( sval , 32 ) ) ;
}
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 ) ;
2019-03-10 06:35:06 +08:00
//PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
res = ecdsa_signature_verify ( public_key , xbuf , xbuflen , & buf [ hashp ] , len - hashp ) ;
if ( res ) {
if ( res = = - 0x4e00 ) {
PrintAndLog ( " Signature is NOT VALID. " ) ;
} else {
2019-03-10 07:00:59 +08:00
PrintAndLog ( " Other signature check error: %x %s " , ( res < 0 ) ? - res : res , ecdsa_get_error ( res ) ) ;
2019-03-10 06:35:06 +08:00
}
} else {
PrintAndLog ( " Signature is OK. " ) ;
}
} else {
PrintAndLog ( " Invalid signature. res=%d. " , res ) ;
}
PrintAndLog ( " \n auth command: " ) ;
2019-03-10 07:00:59 +08:00
printf ( " hf fido auth %s%s " , paramsPlain ? " -p " : " " , sprint_hex_inrow ( & buf [ 67 ] , keyHandleLen ) ) ;
if ( chlen | | applen )
printf ( " %s " , paramsPlain ? ( char * ) cdata : sprint_hex_inrow ( cdata , 32 ) ) ;
if ( applen )
printf ( " %s " , paramsPlain ? ( char * ) adata : sprint_hex_inrow ( adata , 32 ) ) ;
2019-03-10 06:35:06 +08:00
printf ( " \n " ) ;
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 ) {
PrintAndLog ( " ERROR: can't save the file: %s " , fname ) ;
return 200 ;
}
PrintAndLog ( " File `%s` saved. " , fname ) ;
// free json object
json_decref ( root ) ;
}
return 0 ;
2018-11-08 23:29:58 +08:00
} ;
2019-03-10 18:20:22 +08:00
int CmdHFFidoAuthenticate ( 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 ;
json_t * root = NULL ;
CLIParserInit ( " hf fido auth " ,
2019-03-10 07:00:59 +08:00
" Initiate a U2F token authentication. Needs key handle and two 32-byte hash number. \n key handle(var 0..255), challenge parameter (32b) and application parameter (32b). " ,
" Usage: \n \t hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle \n "
" \t hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
" 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 ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_lit0 ( " vV " , " verbose " , " show technical data " ) ,
arg_lit0 ( " pP " , " plain " , " send plain ASCII to challenge and application parameters instead of HEX " ) ,
arg_rem ( " default mode: " , " dont-enforce-user-presence-and-sign " ) ,
arg_lit0 ( " uU " , " user " , " mode: enforce-user-presence-and-sign " ) ,
arg_lit0 ( " cC " , " check " , " mode: check-only " ) ,
arg_str0 ( " jJ " , " json " , " fido.json " , " JSON input / output file name for parameters. " ) ,
arg_str0 ( " kK " , " key " , " public key to verify signature " , NULL ) ,
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
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool APDULogging = arg_get_lit ( 1 ) ;
bool verbose = arg_get_lit ( 2 ) ;
bool paramsPlain = arg_get_lit ( 3 ) ;
uint8_t controlByte = 0x08 ;
if ( arg_get_lit ( 5 ) )
controlByte = 0x03 ;
if ( arg_get_lit ( 6 ) )
controlByte = 0x07 ;
char fname [ 250 ] = { 0 } ;
bool err ;
root = OpenJson ( 7 , fname , argtable , & err ) ;
2019-03-10 07:00:59 +08:00
if ( err )
2019-03-10 06:35:06 +08:00
return 1 ;
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
CLIGetHexWithReturn ( 8 , hdata , & hdatalen ) ;
if ( hdatalen & & hdatalen ! = 65 ) {
PrintAndLog ( " ERROR: public key length must be 65 bytes only. " ) ;
return 1 ;
}
if ( hdatalen ) {
memmove ( public_key , hdata , hdatalen ) ;
public_key_loaded = true ;
}
CLIGetHexWithReturn ( 9 , hdata , & hdatalen ) ;
if ( hdatalen > 255 ) {
PrintAndLog ( " ERROR: application parameter length must be less than 255. " ) ;
return 1 ;
}
if ( hdatalen ) {
keyHandleLen = hdatalen ;
data [ 64 ] = keyHandleLen ;
memmove ( & data [ 65 ] , hdata , keyHandleLen ) ;
}
if ( paramsPlain ) {
memset ( hdata , 0x00 , 32 ) ;
CLIGetStrWithReturn ( 9 , hdata , & hdatalen ) ;
2019-03-19 05:35:33 +08:00
if ( hdatalen > 16 ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d " , hdatalen ) ;
return 1 ;
}
} else {
CLIGetHexWithReturn ( 10 , hdata , & hdatalen ) ;
if ( hdatalen & & hdatalen ! = 32 ) {
PrintAndLog ( " ERROR: challenge parameter length must be 32 bytes only. " ) ;
return 1 ;
}
}
if ( hdatalen )
memmove ( data , hdata , 32 ) ;
if ( paramsPlain ) {
memset ( hdata , 0x00 , 32 ) ;
CLIGetStrWithReturn ( 11 , hdata , & hdatalen ) ;
2019-03-19 05:35:33 +08:00
if ( hdatalen > 16 ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d " , hdatalen ) ;
return 1 ;
}
} else {
CLIGetHexWithReturn ( 10 , hdata , & hdatalen ) ;
if ( hdatalen & & hdatalen ! = 32 ) {
PrintAndLog ( " ERROR: application parameter length must be 32 bytes only. " ) ;
return 1 ;
}
}
if ( hdatalen )
memmove ( & data [ 32 ] , hdata , 32 ) ;
CLIParserFree ( ) ;
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 ) {
PrintAndLog ( " Can't select authenticator. res=%x. Exit... " , res ) ;
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " Can't select FIDO application. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
DropField ( ) ;
return 2 ;
}
res = FIDOAuthentication ( data , datalen , controlByte , buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
PrintAndLog ( " Can't execute authentication command. res=%x. Exit... " , res ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " ERROR execute authentication command. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
return 3 ;
}
PrintAndLog ( " --------------------------------------------------------------- " ) ;
2019-03-10 07:00:59 +08:00
PrintAndLog ( " User presence: %s " , ( buf [ 0 ] ? " verified " : " not verified " ) ) ;
uint32_t cntr = ( uint32_t ) bytes_to_num ( & buf [ 1 ] , 4 ) ;
2019-03-10 06:35:06 +08:00
PrintAndLog ( " Counter: %d " , cntr ) ;
PrintAndLog ( " Hash[%d]: %s " , len - 5 , sprint_hex ( & buf [ 5 ] , len - 5 ) ) ;
// 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 ) ;
if ( ! res ) {
if ( verbose ) {
PrintAndLog ( " r: %s " , sprint_hex ( rval , 32 ) ) ;
PrintAndLog ( " s: %s " , sprint_hex ( sval , 32 ) ) ;
}
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 ) ;
2019-03-10 06:35:06 +08:00
//PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
res = ecdsa_signature_verify ( public_key , xbuf , xbuflen , & buf [ 5 ] , len - 5 ) ;
if ( res ) {
if ( res = = - 0x4e00 ) {
PrintAndLog ( " Signature is NOT VALID. " ) ;
} else {
2019-03-10 07:00:59 +08:00
PrintAndLog ( " Other signature check error: %x %s " , ( res < 0 ) ? - res : res , ecdsa_get_error ( res ) ) ;
2019-03-10 06:35:06 +08:00
}
} else {
PrintAndLog ( " Signature is OK. " ) ;
}
} else {
PrintAndLog ( " No public key provided. can't check signature. " ) ;
}
} else {
PrintAndLog ( " Invalid signature. res=%d. " , res ) ;
}
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 ) {
PrintAndLog ( " ERROR: can't save the file: %s " , fname ) ;
return 200 ;
}
PrintAndLog ( " File `%s` saved. " , fname ) ;
// free json object
json_decref ( root ) ;
}
return 0 ;
2018-11-08 23:29:58 +08:00
} ;
2019-03-10 18:20:22 +08:00
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
}
2019-03-10 18:20:22 +08:00
int GetExistsFileNameJson ( char * prefixDir , 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 ) {
return 1 ; // file not found
}
}
return 0 ;
2018-11-21 01:21:36 +08:00
}
2019-03-10 18:20:22 +08:00
int CmdHFFido2MakeCredential ( const char * cmd ) {
2019-03-10 06:35:06 +08:00
json_error_t error ;
json_t * root = NULL ;
char fname [ 300 ] = { 0 } ;
CLIParserInit ( " hf fido make " ,
2019-03-10 07:00:59 +08:00
" Execute a FIDO2 Make Credentional command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory. " ,
" Usage: \n \t hf fido make -> execute command default parameters file `fido2.json` \n "
" \t 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 ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_litn ( " vV " , " verbose " , 0 , 2 , " show technical data. vv - show full certificates data " ) ,
arg_lit0 ( " tT " , " tlv " , " Show DER certificate contents in TLV representation " ) ,
arg_lit0 ( " cC " , " cbor " , " show CBOR decoded data " ) ,
arg_str0 ( NULL , NULL , " <json file name> " , " JSON input / output file name for parameters. Default `fido2.json` " ) ,
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool APDULogging = arg_get_lit ( 1 ) ;
bool verbose = arg_get_lit ( 2 ) ;
bool verbose2 = arg_get_lit ( 2 ) > 1 ;
bool showDERTLV = arg_get_lit ( 3 ) ;
bool showCBOR = arg_get_lit ( 4 ) ;
2019-03-10 07:00:59 +08:00
uint8_t jsonname [ 250 ] = { 0 } ;
2019-03-10 06:35:06 +08:00
char * cjsonname = ( char * ) jsonname ;
int jsonnamelen = 0 ;
CLIGetStrWithReturn ( 5 , jsonname , & jsonnamelen ) ;
if ( ! jsonnamelen ) {
strcat ( cjsonname , " fido2 " ) ;
jsonnamelen = strlen ( cjsonname ) ;
}
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
int res = GetExistsFileNameJson ( " fido " , cjsonname , fname ) ;
2019-03-10 07:00:59 +08:00
if ( res ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " ERROR: Can't found the json file. " ) ;
return res ;
}
PrintAndLog ( " fname: %s \n " , fname ) ;
root = json_load_file ( fname , 0 , & error ) ;
if ( ! root ) {
PrintAndLog ( " ERROR: json error on line %d: %s " , error . line , error . text ) ;
return 1 ;
}
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 ) {
PrintAndLog ( " Can't select authenticator. res=%x. Exit... " , res ) ;
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " Can't select FIDO application. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
DropField ( ) ;
return 2 ;
}
res = FIDO2CreateMakeCredentionalReq ( root , data , sizeof ( data ) , & datalen ) ;
if ( res )
return res ;
if ( showCBOR ) {
PrintAndLog ( " CBOR make credentional request: " ) ;
PrintAndLog ( " ---------------- CBOR ------------------ " ) ;
TinyCborPrintFIDOPackage ( fido2CmdMakeCredential , false , data , datalen ) ;
PrintAndLog ( " ---------------- CBOR ------------------ " ) ;
}
res = FIDO2MakeCredential ( data , datalen , buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
PrintAndLog ( " Can't execute make credential command. res=%x. Exit... " , res ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " ERROR execute make credential command. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
return 3 ;
}
2019-03-10 07:00:59 +08:00
if ( buf [ 0 ] ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " FIDO2 make credential error: %d - %s " , buf [ 0 ] , fido2GetCmdErrorDescription ( buf [ 0 ] ) ) ;
return 0 ;
}
PrintAndLog ( " MakeCredential result (%d b) OK. " , len ) ;
if ( showCBOR ) {
PrintAndLog ( " CBOR make credentional response: " ) ;
PrintAndLog ( " ---------------- CBOR ------------------ " ) ;
TinyCborPrintFIDOPackage ( fido2CmdMakeCredential , true , & buf [ 1 ] , len - 1 ) ;
PrintAndLog ( " ---------------- CBOR ------------------ " ) ;
}
// 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 ) {
PrintAndLog ( " ERROR: can't save the file: %s " , fname ) ;
return 200 ;
}
PrintAndLog ( " File `%s` saved. " , fname ) ;
}
json_decref ( root ) ;
return 0 ;
2018-11-18 06:03:28 +08:00
} ;
2019-03-10 18:20:22 +08:00
int CmdHFFido2GetAssertion ( const char * cmd ) {
2019-03-10 06:35:06 +08:00
json_error_t error ;
json_t * root = NULL ;
char fname [ 300 ] = { 0 } ;
CLIParserInit ( " hf fido assert " ,
2019-03-10 07:00:59 +08:00
" Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory. " ,
" Usage: \n \t hf fido assert -> execute command default parameters file `fido2.json` \n "
" \t 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 ,
arg_lit0 ( " aA " , " apdu " , " show APDU reqests and responses " ) ,
arg_litn ( " vV " , " verbose " , 0 , 2 , " show technical data. vv - show full certificates data " ) ,
arg_lit0 ( " cC " , " cbor " , " show CBOR decoded data " ) ,
arg_lit0 ( " lL " , " list " , " add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator don't store credential to its memory) " ) ,
arg_str0 ( NULL , NULL , " <json file name> " , " JSON input / output file name for parameters. Default `fido2.json` " ) ,
arg_param_end
} ;
CLIExecWithReturn ( cmd , argtable , true ) ;
bool APDULogging = arg_get_lit ( 1 ) ;
bool verbose = arg_get_lit ( 2 ) ;
bool verbose2 = arg_get_lit ( 2 ) > 1 ;
bool showCBOR = arg_get_lit ( 3 ) ;
bool createAllowList = arg_get_lit ( 4 ) ;
2019-03-10 07:00:59 +08:00
uint8_t jsonname [ 250 ] = { 0 } ;
2019-03-10 06:35:06 +08:00
char * cjsonname = ( char * ) jsonname ;
int jsonnamelen = 0 ;
CLIGetStrWithReturn ( 5 , jsonname , & jsonnamelen ) ;
if ( ! jsonnamelen ) {
strcat ( cjsonname , " fido2 " ) ;
jsonnamelen = strlen ( cjsonname ) ;
}
CLIParserFree ( ) ;
SetAPDULogging ( APDULogging ) ;
int res = GetExistsFileNameJson ( " fido " , " fido2 " , fname ) ;
2019-03-10 07:00:59 +08:00
if ( res ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " ERROR: Can't found the json file. " ) ;
return res ;
}
PrintAndLog ( " fname: %s \n " , fname ) ;
root = json_load_file ( fname , 0 , & error ) ;
if ( ! root ) {
PrintAndLog ( " ERROR: json error on line %d: %s " , error . line , error . text ) ;
return 1 ;
}
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 ) {
PrintAndLog ( " Can't select authenticator. res=%x. Exit... " , res ) ;
DropField ( ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " Can't select FIDO application. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
DropField ( ) ;
return 2 ;
}
res = FIDO2CreateGetAssertionReq ( root , data , sizeof ( data ) , & datalen , createAllowList ) ;
if ( res )
return res ;
if ( showCBOR ) {
PrintAndLog ( " CBOR get assertion request: " ) ;
PrintAndLog ( " ---------------- CBOR ------------------ " ) ;
TinyCborPrintFIDOPackage ( fido2CmdGetAssertion , false , data , datalen ) ;
PrintAndLog ( " ---------------- CBOR ------------------ " ) ;
}
res = FIDO2GetAssertion ( data , datalen , buf , sizeof ( buf ) , & len , & sw ) ;
DropField ( ) ;
if ( res ) {
PrintAndLog ( " Can't execute get assertion command. res=%x. Exit... " , res ) ;
return res ;
}
if ( sw ! = 0x9000 ) {
PrintAndLog ( " ERROR execute get assertion command. APDU response status: %04x - %s " , sw , GetAPDUCodeDescription ( sw > > 8 , sw & 0xff ) ) ;
return 3 ;
}
2019-03-10 07:00:59 +08:00
if ( buf [ 0 ] ) {
2019-03-10 06:35:06 +08:00
PrintAndLog ( " FIDO2 get assertion error: %d - %s " , buf [ 0 ] , fido2GetCmdErrorDescription ( buf [ 0 ] ) ) ;
return 0 ;
}
PrintAndLog ( " GetAssertion result (%d b) OK. " , len ) ;
if ( showCBOR ) {
PrintAndLog ( " CBOR get assertion response: " ) ;
PrintAndLog ( " ---------------- CBOR ------------------ " ) ;
TinyCborPrintFIDOPackage ( fido2CmdGetAssertion , true , & buf [ 1 ] , len - 1 ) ;
PrintAndLog ( " ---------------- CBOR ------------------ " ) ;
}
// 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 ) {
PrintAndLog ( " ERROR: can't save the file: %s " , fname ) ;
return 200 ;
}
PrintAndLog ( " File `%s` saved. " , fname ) ;
}
json_decref ( root ) ;
return 0 ;
2018-11-18 06:03:28 +08:00
} ;
2019-03-10 07:00:59 +08:00
static command_t CommandTable [ ] = {
{ " help " , CmdHelp , 1 , " This help. " } ,
{ " info " , CmdHFFidoInfo , 0 , " Info about FIDO tag. " } ,
{ " reg " , CmdHFFidoRegister , 0 , " FIDO U2F Registration Message. " } ,
{ " auth " , CmdHFFidoAuthenticate , 0 , " FIDO U2F Authentication Message. " } ,
{ " make " , CmdHFFido2MakeCredential , 0 , " FIDO2 MakeCredential command. " } ,
{ " assert " , CmdHFFido2GetAssertion , 0 , " 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 ) {
2019-03-10 06:35:06 +08:00
( void ) WaitForResponseTimeout ( CMD_ACK , NULL , 100 ) ;
CmdsParse ( CommandTable , Cmd ) ;
return 0 ;
2018-11-08 23:29:58 +08:00
}
2019-03-10 18:20:22 +08:00
int CmdHelp ( const char * Cmd ) {
2019-03-10 07:00:59 +08:00
CmdsHelp ( CommandTable ) ;
return 0 ;
2018-11-08 23:29:58 +08:00
}