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 //-----------------------------------------------------------------------------
21 #include "cmdhffido.h"
23 #include "cmdparser.h" // command_t
24 #include "commonutil.h"
26 #include "proxmark3.h"
27 #include "iso7816/iso7816core.h"
28 #include "emv/emvjson.h"
29 #include "cliparser.h"
30 #include "crypto/asn1utils.h"
31 #include "crypto/libpcrypto.h"
32 #include "fido/cbortools.h"
33 #include "fido/fidocore.h"
38 #include "fileutils.h" // laodFileJSONroot
40 #define DEF_FIDO_SIZE 2048
41 #define DEF_FIDO_PARAM_FILE "hf_fido2_defparams.json"
43 static int CmdHelp(const char *Cmd
);
45 static int CmdHFFidoList(const char *Cmd
) {
46 return CmdTraceListAlias(Cmd
, "hf fido", "14a");
49 static int CmdHFFidoInfo(const char *Cmd
) {
50 CLIParserContext
*ctx
;
51 CLIParserInit(&ctx
, "hf fido info",
52 "Get info from Fido tags",
59 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
62 // info about 14a part
63 infoHF14A(false, false, false);
66 PrintAndLogEx(INFO
, "-----------" _CYAN_("FIDO Info") "---------------------------------");
67 SetAPDULogging(false);
69 uint8_t buf
[APDU_RES_LEN
] = {0};
72 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
81 PrintAndLogEx(INFO
, "Not a FIDO card! APDU response: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
83 PrintAndLogEx(ERR
, "APDU exchange error. Card returns 0x0000.");
89 if (!strncmp((char *)buf
, "U2F_V2", 7)) {
90 if (!strncmp((char *)buf
, "FIDO_2_0", 8)) {
91 PrintAndLogEx(INFO
, "FIDO2 authenticator detected. Version: %.*s", (int)len
, buf
);
93 PrintAndLogEx(INFO
, "FIDO authenticator detected (not standard U2F).");
94 PrintAndLogEx(INFO
, "Non U2F authenticator version:");
95 print_buffer((const unsigned char *)buf
, len
, 1);
98 PrintAndLogEx(INFO
, "FIDO U2F authenticator detected. Version: %.*s", (int)len
, buf
);
101 res
= FIDO2GetInfo(buf
, sizeof(buf
), &len
, &sw
);
107 PrintAndLogEx(ERR
, "FIDO2 version doesn't exist (%04x - %s).", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
112 PrintAndLogEx(ERR
, "FIDO2 get version error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
117 PrintAndLogEx(SUCCESS
, "FIDO2 version CBOR decoded:");
118 TinyCborPrintFIDOPackage(fido2CmdGetInfo
, true, &buf
[1], len
- 1);
120 PrintAndLogEx(ERR
, "FIDO2 version length error");
125 static int CmdHFFidoRegister(const char *cmd
) {
126 CLIParserContext
*ctx
;
127 CLIParserInit(&ctx
, "hf fido reg",
128 "Initiate a U2F token registration. Needs two 32-byte hash numbers.\n"
129 "challenge parameter (32b) and application parameter (32b).\n"
130 "The default config filename is `fido2_defparams.json`\n"
132 "hf fido reg -> execute command with 2 parameters, filled 0x00\n"
133 "hf fido reg --cp s0 --ap s1 -> execute command with plain parameters\n"
134 "hf fido reg --cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f --apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
135 "hf fido reg -f fido2-params -> execute command with custom config file\n"
140 arg_lit0("a", "apdu", "show APDU requests and responses"),
141 arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
142 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
143 arg_str0("f", "file", "<fn>", "JSON input file name for parameters"),
144 arg_str0(NULL
, "cp", "<ascii>", "challenge parameter (1..16 chars)"),
145 arg_str0(NULL
, "ap", "<ascii>", "application parameter (1..16 chars)"),
146 arg_str0(NULL
, "cpx", "<hex>", "challenge parameter (32 bytes hex)"),
147 arg_str0(NULL
, "apx", "<hex>", "application parameter (32 bytes hex)"),
150 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
152 bool APDULogging
= arg_get_lit(ctx
, 1);
153 bool verbose
= arg_get_lit(ctx
, 2);
154 bool verbose2
= arg_get_lit(ctx
, 2) > 1;
155 bool showDERTLV
= arg_get_lit(ctx
, 3);
156 bool cpplain
= arg_get_str_len(ctx
, 5);
157 bool applain
= arg_get_str_len(ctx
, 6);
158 bool cphex
= arg_get_str_len(ctx
, 7);
159 bool aphex
= arg_get_str_len(ctx
, 8);
161 uint8_t data
[64] = {0};
163 uint8_t cdata
[250] = {0};
165 uint8_t adata
[250] = {0};
168 char filename
[FILE_PATH_SIZE
] = {0};
169 CLIParamStrToBuf(arg_get_str(ctx
, 4), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
173 strcat(filename
, DEF_FIDO_PARAM_FILE
);
174 fnlen
= strlen(filename
);
178 int res
= loadFileJSONroot(filename
, (void **)&root
, verbose
);
179 if (res
!= PM3_SUCCESS
) {
185 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
186 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
189 memset(cdata
, 0x00, 32);
190 chlen
= sizeof(cdata
);
191 CLIGetStrWithReturn(ctx
, 5, cdata
, &chlen
);
193 PrintAndLogEx(ERR
, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen
);
199 if (cphex
&& cpplain
== false) {
200 chlen
= sizeof(cdata
);
201 CLIGetHexWithReturn(ctx
, 7, cdata
, &chlen
);
202 if (chlen
&& chlen
!= 32) {
203 PrintAndLogEx(ERR
, "ERROR: challenge parameter length must be 32 bytes only.");
210 memmove(data
, cdata
, 32);
213 memset(adata
, 0x00, 32);
214 applen
= sizeof(adata
);
215 CLIGetStrWithReturn(ctx
, 6, adata
, &applen
);
217 PrintAndLogEx(ERR
, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen
);
223 if (aphex
&& applain
== false) {
224 applen
= sizeof(adata
);
225 CLIGetHexWithReturn(ctx
, 8, adata
, &applen
);
226 if (applen
&& applen
!= 32) {
227 PrintAndLogEx(ERR
, "ERROR: application parameter length must be 32 bytes only.");
234 memmove(&data
[32], adata
, 32);
238 SetAPDULogging(APDULogging
);
240 // 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
241 // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
243 uint8_t buf
[2048] = {0};
248 res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
251 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
258 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
264 res
= FIDORegister(data
, buf
, sizeof(buf
), &len
, &sw
);
267 PrintAndLogEx(ERR
, "Can't execute register command. res=%x. Exit...", res
);
273 PrintAndLogEx(ERR
, "ERROR execute register command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
277 PrintAndLogEx(NORMAL
, "");
279 PrintAndLogEx(INFO
, "---------------------------------------------------------------");
281 PrintAndLogEx(INFO
, "data len: %zu", len
);
284 PrintAndLogEx(INFO
, "------------ " _CYAN_("data") " ----------------------");
285 print_buffer((const unsigned char *)buf
, len
, 1);
286 PrintAndLogEx(INFO
, "-------------" _CYAN_("data") " ----------------------");
289 if (buf
[0] != 0x05) {
290 PrintAndLogEx(ERR
, "ERROR: First byte must be 0x05, but it %2x", buf
[0]);
294 PrintAndLogEx(SUCCESS
, "User public key: %s", sprint_hex(&buf
[1], 65));
296 uint8_t keyHandleLen
= buf
[66];
297 PrintAndLogEx(SUCCESS
, "Key handle[%d]: %s", keyHandleLen
, sprint_hex(&buf
[67], keyHandleLen
));
299 int derp
= 67 + keyHandleLen
;
300 int derLen
= (buf
[derp
+ 2] << 8) + buf
[derp
+ 3] + 4;
302 PrintAndLogEx(INFO
, "DER certificate[%d]:", derLen
);
303 PrintAndLogEx(INFO
, "------------------DER-------------------");
304 PrintAndLogEx(INFO
, "%s", sprint_hex(buf
+ derp
, derLen
));
305 PrintAndLogEx(INFO
, "----------------DER---------------------");
308 PrintAndLogEx(INFO
, "------------------DER-------------------");
309 PrintAndLogEx(INFO
, "DER certificate[%d]: %s...", derLen
, sprint_hex(&buf
[derp
], 20));
312 // check and print DER certificate
313 uint8_t public_key
[65] = {0};
315 // print DER certificate in TLV view
317 PrintAndLogEx(INFO
, "----------------DER TLV-----------------");
318 asn1_print(&buf
[derp
], derLen
, " ");
319 PrintAndLogEx(INFO
, "----------------DER TLV-----------------");
322 FIDOCheckDERAndGetKey(&buf
[derp
], derLen
, verbose
, public_key
, sizeof(public_key
));
325 int hashp
= 1 + 65 + 1 + keyHandleLen
+ derLen
;
326 PrintAndLogEx(SUCCESS
, "Hash[%zu]: %s", len
- hashp
, sprint_hex(&buf
[hashp
], len
- hashp
));
328 // check ANSI X9.62 format ECDSA signature (on P-256)
329 uint8_t rval
[300] = {0};
330 uint8_t sval
[300] = {0};
331 res
= ecdsa_asn1_get_signature(&buf
[hashp
], len
- hashp
, rval
, sval
);
332 if (res
== PM3_SUCCESS
) {
334 PrintAndLogEx(INFO
, " r: %s", sprint_hex(rval
, 32));
335 PrintAndLogEx(INFO
, " s: %s", sprint_hex(sval
, 32));
338 uint8_t xbuf
[4096] = {0};
340 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
342 &data
[32], 32, // application parameter
343 &data
[0], 32, // challenge parameter
344 &buf
[67], keyHandleLen
, // keyHandle
345 &buf
[1], 65, // user public key
348 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
349 res
= ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1
, public_key
, xbuf
, xbuflen
, &buf
[hashp
], len
- hashp
, true);
351 if (res
== MBEDTLS_ERR_ECP_VERIFY_FAILED
) {
352 PrintAndLogEx(WARNING
, "Signature is ( " _RED_("not valid") " )");
354 PrintAndLogEx(WARNING
, "Other signature check error: %x %s", (res
< 0) ? -res
: res
, ecdsa_get_error(res
));
357 PrintAndLogEx(SUCCESS
, "Signature is ( " _GREEN_("ok") " )");
361 PrintAndLogEx(WARNING
, "Invalid signature. res = %d. ( " _RED_("fail") " )", res
);
364 PrintAndLogEx(INFO
, "");
365 PrintAndLogEx(INFO
, "auth command: ");
366 char command
[500] = {0};
367 sprintf(command
, "hf fido auth --kh %s", sprint_hex_inrow(&buf
[67], keyHandleLen
));
369 sprintf(command
+ strlen(command
), " --%s %s", cpplain
? "cp" : "cpx", cpplain
? (char *)cdata
: sprint_hex_inrow(cdata
, 32));
371 sprintf(command
+ strlen(command
), " --%s %s", applain
? "cp" : "cpx", applain
? (char *)adata
: sprint_hex_inrow(adata
, 32));
372 PrintAndLogEx(INFO
, "%s", command
);
375 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
376 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
377 JsonSaveBufAsHexCompact(root
, "PublicKey", &buf
[1], 65);
378 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
379 JsonSaveBufAsHexCompact(root
, "KeyHandle", &buf
[67], keyHandleLen
);
380 JsonSaveBufAsHexCompact(root
, "DER", &buf
[67 + keyHandleLen
], derLen
);
382 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true);
389 static int CmdHFFidoAuthenticate(const char *cmd
) {
390 CLIParserContext
*ctx
;
391 CLIParserInit(&ctx
, "hf fido auth",
392 "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers.\n"
393 "key handle(var 0..255), challenge parameter (32b) and application parameter (32b)\n"
394 "The default config filename is `fido2_defparams.json`\n"
396 "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
398 "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
399 "--cp 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n"
400 "--ap 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
404 arg_lit0("a", "apdu", "show APDU reqests and responses"),
405 arg_lit0("v", "verbose", "show technical data"),
406 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
407 arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"),
408 arg_lit0("c", "check", "mode: check-only"),
409 arg_str0("f", "file", "<fn>", "JSON input file name for parameters"),
410 arg_str0("k", "key", "<hex>", "public key to verify signature"),
411 arg_str0(NULL
, "kh", "<hex>", "key handle (var 0..255b)"),
412 arg_str0(NULL
, "cp", "<ascii>", "challenge parameter (1..16 chars)"),
413 arg_str0(NULL
, "ap", "<ascii>", "application parameter (1..16 chars)"),
414 arg_str0(NULL
, "cpx", "<hex>", "challenge parameter (32 bytes hex)"),
415 arg_str0(NULL
, "apx", "<hex>", "application parameter (32 bytes hex)"),
418 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
420 bool APDULogging
= arg_get_lit(ctx
, 1);
421 bool verbose
= arg_get_lit(ctx
, 2);
423 uint8_t controlByte
= 0x08;
424 if (arg_get_lit(ctx
, 4))
427 if (arg_get_lit(ctx
, 5))
430 uint8_t data
[512] = {0};
431 uint8_t hdata
[250] = {0};
432 bool public_key_loaded
= false;
433 uint8_t public_key
[65] = {0};
435 uint8_t keyHandleLen
= 0;
438 char filename
[FILE_PATH_SIZE
] = {0};
439 CLIParamStrToBuf(arg_get_str(ctx
, 6), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
443 strcat(filename
, DEF_FIDO_PARAM_FILE
);
444 fnlen
= strlen(filename
);
448 int res
= loadFileJSONroot(filename
, (void **)&root
, verbose
);
449 if (res
!= PM3_SUCCESS
) {
455 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
456 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
457 JsonLoadBufAsHex(root
, "$.KeyHandle", &data
[65], 512 - 67, &jlen
);
458 keyHandleLen
= jlen
& 0xff;
459 data
[64] = keyHandleLen
;
460 JsonLoadBufAsHex(root
, "$.PublicKey", public_key
, 65, &jlen
);
461 public_key_loaded
= (jlen
> 0);
464 CLIGetHexWithReturn(ctx
, 7, hdata
, &hdatalen
);
465 if (hdatalen
&& hdatalen
!= 65) {
466 PrintAndLogEx(ERR
, "ERROR: public key length must be 65 bytes only.");
473 memmove(public_key
, hdata
, hdatalen
);
474 public_key_loaded
= true;
477 CLIGetHexWithReturn(ctx
, 8, hdata
, &hdatalen
);
478 if (hdatalen
> 255) {
479 PrintAndLogEx(ERR
, "ERROR: key handle length must be less than 255.");
485 printf("-- hlen=%d\n", hdatalen
);
487 keyHandleLen
= hdatalen
;
488 data
[64] = keyHandleLen
;
489 memmove(&data
[65], hdata
, keyHandleLen
);
493 bool cpplain
= arg_get_str_len(ctx
, 9);
494 bool applain
= arg_get_str_len(ctx
, 10);
495 bool cphex
= arg_get_str_len(ctx
, 11);
496 bool aphex
= arg_get_str_len(ctx
, 12);
499 memset(hdata
, 0x00, 32);
500 hdatalen
= sizeof(hdata
);
501 CLIGetStrWithReturn(ctx
, 9, hdata
, &hdatalen
);
503 PrintAndLogEx(ERR
, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
509 if (cphex
&& cpplain
== false) {
510 hdatalen
= sizeof(hdata
);
511 CLIGetHexWithReturn(ctx
, 11, hdata
, &hdatalen
);
512 if (hdatalen
&& hdatalen
!= 32) {
513 PrintAndLogEx(ERR
, "ERROR: challenge parameter length must be 32 bytes only.");
520 memmove(data
, hdata
, 32);
525 memset(hdata
, 0x00, 32);
526 hdatalen
= sizeof(hdata
);
527 CLIGetStrWithReturn(ctx
, 10, hdata
, &hdatalen
);
529 PrintAndLogEx(ERR
, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
535 if (aphex
&& applain
== false) {
536 hdatalen
= sizeof(hdata
);
537 CLIGetHexWithReturn(ctx
, 12, hdata
, &hdatalen
);
538 if (hdatalen
&& hdatalen
!= 32) {
539 PrintAndLogEx(ERR
, "ERROR: application parameter length must be 32 bytes only.");
546 memmove(&data
[32], hdata
, 32);
552 SetAPDULogging(APDULogging
);
554 // (in parameter) control byte 0x07 - check only, 0x03 - user presence + cign. 0x08 - sign only
555 // challenge parameter [32 bytes]
556 // application parameter [32 bytes]
557 // key handle length [1b] = N
560 uint8_t datalen
= 32 + 32 + 1 + keyHandleLen
;
562 uint8_t buf
[2048] = {0};
567 res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
569 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
576 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
582 res
= FIDOAuthentication(data
, datalen
, controlByte
, buf
, sizeof(buf
), &len
, &sw
);
585 PrintAndLogEx(ERR
, "Can't execute authentication command. res=%x. Exit...", res
);
591 PrintAndLogEx(ERR
, "ERROR execute authentication command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
596 PrintAndLogEx(INFO
, "---------------------------------------------------------------");
597 PrintAndLogEx(SUCCESS
, "User presence: %s", (buf
[0] ? "verified" : "not verified"));
598 uint32_t cntr
= (uint32_t)bytes_to_num(&buf
[1], 4);
599 PrintAndLogEx(SUCCESS
, "Counter: %d", cntr
);
600 PrintAndLogEx(SUCCESS
, "Hash[%zu]: %s", len
- 5, sprint_hex(&buf
[5], len
- 5));
602 // check ANSI X9.62 format ECDSA signature (on P-256)
603 uint8_t rval
[300] = {0};
604 uint8_t sval
[300] = {0};
605 res
= ecdsa_asn1_get_signature(&buf
[5], len
- 5, rval
, sval
);
606 if (res
== PM3_SUCCESS
) {
608 PrintAndLogEx(INFO
, " r: %s", sprint_hex(rval
, 32));
609 PrintAndLogEx(INFO
, " s: %s", sprint_hex(sval
, 32));
611 if (public_key_loaded
) {
612 uint8_t xbuf
[4096] = {0};
614 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
615 &data
[32], 32, // application parameter
616 &buf
[0], 1, // user presence
617 &buf
[1], 4, // counter
618 data
, 32, // challenge parameter
621 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
622 res
= ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1
, public_key
, xbuf
, xbuflen
, &buf
[5], len
- 5, true);
624 if (res
== MBEDTLS_ERR_ECP_VERIFY_FAILED
) {
625 PrintAndLogEx(WARNING
, "Signature is ( " _RED_("not valid") " )");
627 PrintAndLogEx(WARNING
, "Other signature check error: %x %s", (res
< 0) ? -res
: res
, ecdsa_get_error(res
));
630 PrintAndLogEx(SUCCESS
, "Signature is (" _GREEN_("ok") " )");
633 PrintAndLogEx(WARNING
, "No public key provided. can't check signature.");
636 PrintAndLogEx(WARNING
, "Invalid signature. res = %d. ( " _RED_("fail") " )", res
);
640 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
641 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
642 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
643 JsonSaveBufAsHexCompact(root
, "KeyHandle", &data
[65], keyHandleLen
);
644 JsonSaveInt(root
, "Counter", cntr
);
646 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true);
653 static int CmdHFFido2MakeCredential(const char *cmd
) {
654 CLIParserContext
*ctx
;
655 CLIParserInit(&ctx
, "hf fido make",
656 "Execute a FIDO2 Make Credential command. Needs json file with parameters.\n"
657 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
658 "- for yubikey there must be only one option `\"rk\": true` or false"
660 "hf fido make --> use default parameters file `fido2_defparams.json`\n"
661 "hf fido make -f test.json --> use parameters file `text.json`"
666 arg_lit0("a", "apdu", "show APDU reqests and responses"),
667 arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
668 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
669 arg_lit0("c", "cbor", "show CBOR decoded data"),
670 arg_str0("f", "file", "<fn>", "parameter JSON file name"),
673 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
675 bool APDULogging
= arg_get_lit(ctx
, 1);
676 bool verbose
= arg_get_lit(ctx
, 2);
677 bool verbose2
= arg_get_lit(ctx
, 2) > 1;
678 bool showDERTLV
= arg_get_lit(ctx
, 3);
679 bool showCBOR
= arg_get_lit(ctx
, 4);
682 char filename
[FILE_PATH_SIZE
] = {0};
683 CLIParamStrToBuf(arg_get_str(ctx
, 5), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
688 strcat(filename
, DEF_FIDO_PARAM_FILE
);
689 fnlen
= strlen(filename
);
693 loadFileJSONroot(filename
, (void **)&root
, verbose
);
698 SetAPDULogging(APDULogging
);
700 uint8_t data
[DEF_FIDO_SIZE
] = {0};
702 uint8_t buf
[DEF_FIDO_SIZE
] = {0};
707 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
709 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
716 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
722 res
= FIDO2CreateMakeCredentionalReq(root
, data
, sizeof(data
), &datalen
);
729 PrintAndLogEx(INFO
, "CBOR make credential request:");
730 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
731 TinyCborPrintFIDOPackage(fido2CmdMakeCredential
, false, data
, datalen
);
732 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
735 res
= FIDO2MakeCredential(data
, datalen
, buf
, sizeof(buf
), &len
, &sw
);
738 PrintAndLogEx(ERR
, "Can't execute make credential command. res=%x. exit...", res
);
744 PrintAndLogEx(ERR
, "ERROR execute make credential command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
750 PrintAndLogEx(ERR
, "FIDO2 make credential error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
755 PrintAndLogEx(SUCCESS
, "MakeCredential result %zu b ( ok )", len
);
757 PrintAndLogEx(SUCCESS
, "CBOR make credential response:");
758 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
759 TinyCborPrintFIDOPackage(fido2CmdMakeCredential
, true, &buf
[1], len
- 1);
760 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
763 // parse returned cbor
764 FIDO2MakeCredentionalParseRes(root
, &buf
[1], len
- 1, verbose
, verbose2
, showCBOR
, showDERTLV
);
766 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true);
772 static int CmdHFFido2GetAssertion(const char *cmd
) {
773 CLIParserContext
*ctx
;
774 CLIParserInit(&ctx
, "hf fido assert",
775 "Execute a FIDO2 Get Assertion command. Needs json file with parameters.\n"
776 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
777 "- Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)\n"
778 "- for yubikey there must be only one option `\"up\": true` or false"
780 "hf fido assert --> default parameters file `fido2_defparams.json`\n"
781 "hf fido assert -f test.json -l --> use parameters file `text.json` and add to request CredentialId");
785 arg_lit0("a", "apdu", "show APDU reqests and responses"),
786 arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
787 arg_lit0("c", "cbor", "show CBOR decoded data"),
788 arg_lit0("l", "list", "add CredentialId from json to allowList"),
789 arg_str0("f", "file", "<fn>", "parameter JSON file name"),
792 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
794 bool APDULogging
= arg_get_lit(ctx
, 1);
795 bool verbose
= arg_get_lit(ctx
, 2);
796 bool verbose2
= arg_get_lit(ctx
, 2) > 1;
797 bool showCBOR
= arg_get_lit(ctx
, 3);
798 bool createAllowList
= arg_get_lit(ctx
, 4);
801 char filename
[FILE_PATH_SIZE
] = {0};
802 CLIParamStrToBuf(arg_get_str(ctx
, 5), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
807 strcat(filename
, DEF_FIDO_PARAM_FILE
);
808 fnlen
= strlen(filename
);
812 loadFileJSONroot(filename
, (void **)&root
, verbose
);
817 SetAPDULogging(APDULogging
);
819 uint8_t data
[DEF_FIDO_SIZE
] = {0};
821 uint8_t buf
[DEF_FIDO_SIZE
] = {0};
826 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
828 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. exiting...", res
);
835 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
841 res
= FIDO2CreateGetAssertionReq(root
, data
, sizeof(data
), &datalen
, createAllowList
);
848 PrintAndLogEx(SUCCESS
, "CBOR get assertion request:");
849 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
850 TinyCborPrintFIDOPackage(fido2CmdGetAssertion
, false, data
, datalen
);
851 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
854 res
= FIDO2GetAssertion(data
, datalen
, buf
, sizeof(buf
), &len
, &sw
);
857 PrintAndLogEx(ERR
, "Can't execute get assertion command. res=%x. Exit...", res
);
863 PrintAndLogEx(ERR
, "ERROR execute get assertion command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
869 PrintAndLogEx(ERR
, "FIDO2 get assertion error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
874 PrintAndLogEx(SUCCESS
, "GetAssertion result (%zu b) OK.", len
);
876 PrintAndLogEx(SUCCESS
, "CBOR get assertion response:");
877 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
878 TinyCborPrintFIDOPackage(fido2CmdGetAssertion
, true, &buf
[1], len
- 1);
879 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
882 // parse returned cbor
883 FIDO2GetAssertionParseRes(root
, &buf
[1], len
- 1, verbose
, verbose2
, showCBOR
);
885 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true);
891 static command_t CommandTable
[] = {
892 {"help", CmdHelp
, AlwaysAvailable
, "This help."},
893 {"list", CmdHFFidoList
, AlwaysAvailable
, "List ISO 14443A history"},
894 {"info", CmdHFFidoInfo
, IfPm3Iso14443a
, "Info about FIDO tag."},
895 {"reg", CmdHFFidoRegister
, IfPm3Iso14443a
, "FIDO U2F Registration Message."},
896 {"auth", CmdHFFidoAuthenticate
, IfPm3Iso14443a
, "FIDO U2F Authentication Message."},
897 {"make", CmdHFFido2MakeCredential
, IfPm3Iso14443a
, "FIDO2 MakeCredential command."},
898 {"assert", CmdHFFido2GetAssertion
, IfPm3Iso14443a
, "FIDO2 GetAssertion command."},
899 {NULL
, NULL
, 0, NULL
}
902 int CmdHFFido(const char *Cmd
) {
903 clearCommandBuffer();
904 return CmdsParse(CommandTable
, Cmd
);
907 int CmdHelp(const char *Cmd
) {
908 (void)Cmd
; // Cmd is not used so far
909 CmdsHelp(CommandTable
);