]>
cvs.zerfleddert.de Git - proxmark3-svn/blob - client/cmdhffido.c
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
7 //-----------------------------------------------------------------------------
8 // High frequency MIFARE Plus commands
9 //-----------------------------------------------------------------------------
11 // Documentation here:
13 // FIDO Alliance specifications
14 // https://fidoalliance.org/download/
15 // FIDO NFC Protocol Specification v1.0
16 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
17 // FIDO U2F Raw Message Formats
18 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
19 //-----------------------------------------------------------------------------
22 #include "cmdhffido.h"
35 #include "proxmark3.h"
38 #include "emv/emvcore.h"
39 #include "emv/emvjson.h"
41 #include "cliparser/cliparser.h"
43 static int CmdHelp ( const char * Cmd
);
45 int FIDOSelect ( bool ActivateField
, bool LeaveFieldON
, uint8_t * Result
, size_t MaxResultLen
, size_t * ResultLen
, uint16_t * sw
) {
46 uint8_t data
[] = { 0xA0 , 0x00 , 0x00 , 0x06 , 0x47 , 0x2F , 0x00 , 0x01 };
48 return EMVSelect ( ActivateField
, LeaveFieldON
, data
, sizeof ( data
), Result
, MaxResultLen
, ResultLen
, sw
, NULL
);
51 int FIDOExchange ( sAPDU apdu
, uint8_t * Result
, size_t MaxResultLen
, size_t * ResultLen
, uint16_t * sw
) {
52 int res
= EMVExchange ( true , apdu
, Result
, MaxResultLen
, ResultLen
, sw
, NULL
);
53 if ( res
== 5 ) // apdu result (sw) not a 0x9000
56 while (! res
&& (* sw
>> 8 ) == 0x61 ) {
57 size_t oldlen
= * ResultLen
;
58 res
= EMVExchange ( true , ( sAPDU
){ 0x00 , 0xC0 , 0x00 , 0x00 , 0x00 , NULL
}, & Result
[ oldlen
], MaxResultLen
- oldlen
, ResultLen
, sw
, NULL
);
59 if ( res
== 5 ) // apdu result (sw) not a 0x9000
63 if (* ResultLen
> MaxResultLen
)
69 int FIDORegister ( uint8_t * params
, uint8_t * Result
, size_t MaxResultLen
, size_t * ResultLen
, uint16_t * sw
) {
70 return FIDOExchange (( sAPDU
){ 0x00 , 0x01 , 0x03 , 0x00 , 64 , params
}, Result
, MaxResultLen
, ResultLen
, sw
);
73 int FIDOAuthentication ( uint8_t * params
, uint8_t paramslen
, uint8_t controlb
, uint8_t * Result
, size_t MaxResultLen
, size_t * ResultLen
, uint16_t * sw
) {
74 return FIDOExchange (( sAPDU
){ 0x00 , 0x02 , controlb
, 0x00 , paramslen
, params
}, Result
, MaxResultLen
, ResultLen
, sw
);
77 int FIDO2GetInfo ( uint8_t * Result
, size_t MaxResultLen
, size_t * ResultLen
, uint16_t * sw
) {
78 uint8_t data
[] = { 0x04 };
79 return FIDOExchange (( sAPDU
){ 0x80 , 0x10 , 0x00 , 0x00 , sizeof ( data
), data
}, Result
, MaxResultLen
, ResultLen
, sw
);
82 int CmdHFFidoInfo ( const char * cmd
) {
84 if ( cmd
&& strlen ( cmd
) > 0 )
85 PrintAndLog ( "WARNING: command don't have any parameters. \n " );
87 // info about 14a part
91 PrintAndLog ( "--------------------------------------------" );
92 SetAPDULogging ( false );
94 uint8_t buf
[ APDU_RES_LEN
] = { 0 };
97 int res
= FIDOSelect ( true , true , buf
, sizeof ( buf
), & len
, & sw
);
106 PrintAndLog ( "Not a FIDO card! APDU response: %04x - %s" , sw
, GetAPDUCodeDescription ( sw
>> 8 , sw
& 0xff ));
108 PrintAndLog ( "APDU exchange error. Card returns 0x0000." );
114 if (! strncmp (( char *) buf
, "U2F_V2" , 7 )) {
115 if (! strncmp (( char *) buf
, "FIDO_2_0" , 8 )) {
116 PrintAndLog ( "FIDO2 authenricator detected. Version: %.*s" , len
, buf
);
118 PrintAndLog ( "FIDO authenricator detected (not standard U2F)." );
119 PrintAndLog ( "Non U2F authenticator version:" );
120 dump_buffer (( const unsigned char *) buf
, len
, NULL
, 0 );
123 PrintAndLog ( "FIDO U2F authenricator detected. Version: %.*s" , len
, buf
);
126 res
= FIDO2GetInfo ( buf
, sizeof ( buf
), & len
, & sw
);
132 PrintAndLog ( "FIDO2 version not exists (%04x - %s)." , sw
, GetAPDUCodeDescription ( sw
>> 8 , sw
& 0xff ));
137 PrintAndLog ( "FIDO2 version: (%d)" , len
);
138 dump_buffer (( const unsigned char *) buf
, len
, NULL
, 0 );
143 json_t
* OpenJson ( int paramnum
, char * fname
, void * argtable
[], bool * err
) {
148 uint8_t jsonname
[ 250 ] ={ 0 };
149 char * cjsonname
= ( char *) jsonname
;
152 // CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
153 if ( CLIParamStrToBuf ( arg_get_str ( paramnum
), jsonname
, sizeof ( jsonname
), & jsonnamelen
)) {
158 // current path + file name
159 if (! strstr ( cjsonname
, ".json" ))
160 strcat ( cjsonname
, ".json" );
163 strcpy ( fname
, get_my_executable_directory ());
164 strcat ( fname
, cjsonname
);
165 if ( access ( fname
, F_OK
) != - 1 ) {
166 root
= json_load_file ( fname
, 0 , & error
);
168 PrintAndLog ( "ERROR: json error on line %d: %s" , error
. line
, error
. text
);
173 if (! json_is_object ( root
)) {
174 PrintAndLog ( "ERROR: Invalid json format. root must be an object." );
181 root
= json_object ();
187 int CmdHFFidoRegister ( const char * cmd
) {
188 uint8_t data
[ 64 ] = { 0 };
190 uint8_t cdata
[ 250 ] = { 0 };
192 uint8_t adata
[ 250 ] = { 0 };
195 CLIParserInit ( "hf fido reg" ,
196 "Initiate a U2F token registration. Needs two 32-byte hash number. \n challenge parameter (32b) and application parameter (32b)." ,
197 "Usage: \n\t hf fido reg -> execute command with 2 parameters, filled 0x00 \n "
198 " \t hf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"
199 " \t hf fido reg -p s0 s1 -> execute command with plain parameters" );
203 arg_lit0 ( "aA" , "apdu" , "show APDU reqests and responses" ),
204 arg_lit0 ( "vV" , "verbose" , "show technical data" ),
205 arg_lit0 ( "pP" , "plain" , "send plain ASCII to challenge and application parameters instead of HEX" ),
206 arg_str0 ( "jJ" , "json" , "fido.json" , "JSON input / output file name for parameters." ),
207 arg_str0 ( NULL
, NULL
, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>" , NULL
),
208 arg_str0 ( NULL
, NULL
, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>" , NULL
),
211 CLIExecWithReturn ( cmd
, argtable
, true );
213 bool APDULogging
= arg_get_lit ( 1 );
214 bool verbose
= arg_get_lit ( 2 );
215 bool paramsPlain
= arg_get_lit ( 3 );
217 char fname
[ 250 ] = { 0 };
219 root
= OpenJson ( 4 , fname
, argtable
, & err
);
224 JsonLoadBufAsHex ( root
, "$.ChallengeParam" , data
, 32 , & jlen
);
225 JsonLoadBufAsHex ( root
, "$.ApplicationParam" , & data
[ 32 ], 32 , & jlen
);
229 memset ( cdata
, 0x00 , 32 );
230 CLIGetStrWithReturn ( 5 , cdata
, & chlen
);
231 if ( chlen
&& chlen
> 16 ) {
232 PrintAndLog ( "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d" , chlen
);
236 CLIGetHexWithReturn ( 5 , cdata
, & chlen
);
237 if ( chlen
&& chlen
!= 32 ) {
238 PrintAndLog ( "ERROR: challenge parameter length must be 32 bytes only." );
243 memmove ( data
, cdata
, 32 );
247 memset ( adata
, 0x00 , 32 );
248 CLIGetStrWithReturn ( 6 , adata
, & applen
);
249 if ( applen
&& applen
> 16 ) {
250 PrintAndLog ( "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d" , applen
);
254 CLIGetHexWithReturn ( 6 , adata
, & applen
);
255 if ( applen
&& applen
!= 32 ) {
256 PrintAndLog ( "ERROR: application parameter length must be 32 bytes only." );
261 memmove (& data
[ 32 ], adata
, 32 );
265 SetAPDULogging ( APDULogging
);
267 // 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
268 // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
270 uint8_t buf
[ 2048 ] = { 0 };
275 int res
= FIDOSelect ( true , true , buf
, sizeof ( buf
), & len
, & sw
);
278 PrintAndLog ( "Can't select authenticator. res=%x. Exit..." , res
);
284 PrintAndLog ( "Can't select FIDO application. APDU response status: %04x - %s" , sw
, GetAPDUCodeDescription ( sw
>> 8 , sw
& 0xff ));
289 res
= FIDORegister ( data
, buf
, sizeof ( buf
), & len
, & sw
);
292 PrintAndLog ( "Can't execute register command. res=%x. Exit..." , res
);
297 PrintAndLog ( "ERROR execute register command. APDU response status: %04x - %s" , sw
, GetAPDUCodeDescription ( sw
>> 8 , sw
& 0xff ));
303 PrintAndLog ( "---------------------------------------------------------------" );
304 PrintAndLog ( "data len: %d" , len
);
306 PrintAndLog ( "--------------data----------------------" );
307 dump_buffer (( const unsigned char *) buf
, len
, NULL
, 0 );
308 PrintAndLog ( "--------------data----------------------" );
311 if ( buf
[ 0 ] != 0x05 ) {
312 PrintAndLog ( "ERROR: First byte must be 0x05, but it %2x" , buf
[ 0 ]);
315 PrintAndLog ( "User public key: %s" , sprint_hex (& buf
[ 1 ], 65 ));
317 uint8_t keyHandleLen
= buf
[ 66 ];
318 PrintAndLog ( "Key handle[%d]: %s" , keyHandleLen
, sprint_hex (& buf
[ 67 ], keyHandleLen
));
320 int derp
= 67 + keyHandleLen
;
321 int derLen
= ( buf
[ derp
+ 2 ] << 8 ) + buf
[ derp
+ 3 ] + 4 ;
322 // needs to decode DER certificate
324 PrintAndLog ( "DER certificate[%d]:------------------DER-------------------" , derLen
);
325 dump_buffer_simple (( const unsigned char *)& buf
[ 67 + keyHandleLen
], derLen
, NULL
);
326 PrintAndLog ( " \n ----------------DER---------------------" );
328 PrintAndLog ( "DER certificate[%d]: %s..." , derLen
, sprint_hex (& buf
[ derp
], 20 ));
332 int hashp
= 1 + 65 + 1 + keyHandleLen
+ derLen
;
333 PrintAndLog ( "Hash[%d]: %s" , len
- hashp
, sprint_hex (& buf
[ hashp
], len
- hashp
));
335 // check ANSI X9.62 format ECDSA signature (on P-256)
337 PrintAndLog ( " \n auth command: " );
338 printf ( "hf fido auth %s%s" , paramsPlain
? "-p " : "" , sprint_hex_inrow (& buf
[ 67 ], keyHandleLen
));
340 printf ( " %s" , paramsPlain
?( char *) cdata
: sprint_hex_inrow ( cdata
, 32 ));
342 printf ( " %s" , paramsPlain
?( char *) adata
: sprint_hex_inrow ( adata
, 32 ));
346 JsonSaveBufAsHex ( root
, "ChallengeParam" , data
, 32 );
347 JsonSaveBufAsHex ( root
, "ApplicationParam" , & data
[ 32 ], 32 );
348 JsonSaveInt ( root
, "KeyHandleLen" , keyHandleLen
);
349 JsonSaveBufAsHexCompact ( root
, "KeyHandle" , & buf
[ 67 ], keyHandleLen
);
350 JsonSaveBufAsHexCompact ( root
, "DER" , & buf
[ 67 + keyHandleLen
], derLen
);
352 res
= json_dump_file ( root
, fname
, JSON_INDENT ( 2 ));
354 PrintAndLog ( "ERROR: can't save the file: %s" , fname
);
357 PrintAndLog ( "File `%s` saved." , fname
);
366 int CmdHFFidoAuthenticate ( const char * cmd
) {
367 uint8_t data
[ 512 ] = { 0 };
368 uint8_t hdata
[ 250 ] = { 0 };
370 uint8_t keyHandleLen
= 0 ;
373 CLIParserInit ( "hf fido auth" ,
374 "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)." ,
375 "Usage: \n\t hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle \n "
376 " \t hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
377 "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters" );
381 arg_lit0 ( "aA" , "apdu" , "show APDU reqests and responses" ),
382 arg_lit0 ( "vV" , "verbose" , "show technical data" ),
383 arg_lit0 ( "pP" , "plain" , "send plain ASCII to challenge and application parameters instead of HEX" ),
384 arg_rem ( "default mode:" , "dont-enforce-user-presence-and-sign" ),
385 arg_lit0 ( "uU" , "user" , "mode: enforce-user-presence-and-sign" ),
386 arg_lit0 ( "cC" , "check" , "mode: check-only" ),
387 arg_str0 ( "jJ" , "json" , "fido.json" , "JSON input / output file name for parameters." ),
388 arg_str0 ( NULL
, NULL
, "<HEX key handle (var 0..255b)>" , NULL
),
389 arg_str0 ( NULL
, NULL
, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>" , NULL
),
390 arg_str0 ( NULL
, NULL
, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>" , NULL
),
393 CLIExecWithReturn ( cmd
, argtable
, true );
395 bool APDULogging
= arg_get_lit ( 1 );
396 //bool verbose = arg_get_lit(2);
397 bool paramsPlain
= arg_get_lit ( 3 );
398 uint8_t controlByte
= 0x08 ;
404 char fname
[ 250 ] = { 0 };
406 root
= OpenJson ( 7 , fname
, argtable
, & err
);
411 JsonLoadBufAsHex ( root
, "$.ChallengeParam" , data
, 32 , & jlen
);
412 JsonLoadBufAsHex ( root
, "$.ApplicationParam" , & data
[ 32 ], 32 , & jlen
);
413 JsonLoadBufAsHex ( root
, "$.KeyHandle" , & data
[ 65 ], 512 - 67 , & jlen
);
414 keyHandleLen
= jlen
& 0xff ;
415 data
[ 64 ] = keyHandleLen
;
418 CLIGetHexWithReturn ( 8 , hdata
, & hdatalen
);
419 if ( hdatalen
> 255 ) {
420 PrintAndLog ( "ERROR: application parameter length must be less than 255." );
424 keyHandleLen
= hdatalen
;
425 data
[ 64 ] = keyHandleLen
;
426 memmove (& data
[ 65 ], hdata
, keyHandleLen
);
430 memset ( hdata
, 0x00 , 32 );
431 CLIGetStrWithReturn ( 9 , hdata
, & hdatalen
);
432 if ( hdatalen
&& hdatalen
> 16 ) {
433 PrintAndLog ( "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d" , hdatalen
);
437 CLIGetHexWithReturn ( 9 , hdata
, & hdatalen
);
438 if ( hdatalen
&& hdatalen
!= 32 ) {
439 PrintAndLog ( "ERROR: challenge parameter length must be 32 bytes only." );
444 memmove ( data
, hdata
, 32 );
447 memset ( hdata
, 0x00 , 32 );
448 CLIGetStrWithReturn ( 10 , hdata
, & hdatalen
);
449 if ( hdatalen
&& hdatalen
> 16 ) {
450 PrintAndLog ( "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d" , hdatalen
);
454 CLIGetHexWithReturn ( 10 , hdata
, & hdatalen
);
455 if ( hdatalen
&& hdatalen
!= 32 ) {
456 PrintAndLog ( "ERROR: application parameter length must be 32 bytes only." );
461 memmove (& data
[ 32 ], hdata
, 32 );
465 SetAPDULogging ( APDULogging
);
467 // (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
468 // challenge parameter [32 bytes]
469 // application parameter [32 bytes]
470 // key handle length [1b] = N
473 uint8_t datalen
= 32 + 32 + 1 + keyHandleLen
;
475 uint8_t buf
[ 2048 ] = { 0 };
480 int res
= FIDOSelect ( true , true , buf
, sizeof ( buf
), & len
, & sw
);
483 PrintAndLog ( "Can't select authenticator. res=%x. Exit..." , res
);
489 PrintAndLog ( "Can't select FIDO application. APDU response status: %04x - %s" , sw
, GetAPDUCodeDescription ( sw
>> 8 , sw
& 0xff ));
494 res
= FIDOAuthentication ( data
, datalen
, controlByte
, buf
, sizeof ( buf
), & len
, & sw
);
497 PrintAndLog ( "Can't execute authentication command. res=%x. Exit..." , res
);
502 PrintAndLog ( "ERROR execute authentication command. APDU response status: %04x - %s" , sw
, GetAPDUCodeDescription ( sw
>> 8 , sw
& 0xff ));
506 PrintAndLog ( "---------------------------------------------------------------" );
507 PrintAndLog ( "User presence: %s" , ( buf
[ 0 ]? "verified" : "not verified" ));
508 uint32_t cntr
= ( uint32_t ) bytes_to_num (& buf
[ 1 ], 4 );
509 PrintAndLog ( "Counter: %d" , cntr
);
510 PrintAndLog ( "Hash[%d]: %s" , len
- 5 , sprint_hex (& buf
[ 5 ], len
- 5 ));
513 JsonSaveBufAsHex ( root
, "ChallengeParam" , data
, 32 );
514 JsonSaveBufAsHex ( root
, "ApplicationParam" , & data
[ 32 ], 32 );
515 JsonSaveInt ( root
, "KeyHandleLen" , keyHandleLen
);
516 JsonSaveBufAsHexCompact ( root
, "KeyHandle" , & data
[ 65 ], keyHandleLen
);
517 JsonSaveInt ( root
, "Counter" , cntr
);
519 res
= json_dump_file ( root
, fname
, JSON_INDENT ( 2 ));
521 PrintAndLog ( "ERROR: can't save the file: %s" , fname
);
524 PrintAndLog ( "File `%s` saved." , fname
);
532 static command_t CommandTable
[] =
534 { "help" , CmdHelp
, 1 , "This help." },
535 { "info" , CmdHFFidoInfo
, 0 , "Info about FIDO tag." },
536 { "reg" , CmdHFFidoRegister
, 0 , "FIDO U2F Registration Message." },
537 { "auth" , CmdHFFidoAuthenticate
, 0 , "FIDO U2F Authentication Message." },
538 { NULL
, NULL
, 0 , NULL
}
541 int CmdHFFido ( const char * Cmd
) {
542 ( void ) WaitForResponseTimeout ( CMD_ACK
, NULL
, 100 );
543 CmdsParse ( CommandTable
, Cmd
);
547 int CmdHelp ( const char * Cmd
) {
548 CmdsHelp ( CommandTable
);