1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // High frequency FIDO U2F and FIDO2 contactless authenticators
17 //-----------------------------------------------------------------------------
19 // Documentation here:
21 // FIDO Alliance specifications
22 // https://fidoalliance.org/download/
23 // FIDO NFC Protocol Specification v1.0
24 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
25 // FIDO U2F Raw Message Formats
26 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
27 //-----------------------------------------------------------------------------
29 #include "cmdhffido.h"
31 #include "cmdparser.h" // command_t
32 #include "commonutil.h"
34 #include "proxmark3.h"
35 #include "iso7816/iso7816core.h"
36 #include "emv/emvjson.h"
37 #include "cliparser.h"
38 #include "crypto/asn1utils.h"
39 #include "crypto/libpcrypto.h"
40 #include "fido/cbortools.h"
41 #include "fido/fidocore.h"
46 #include "fileutils.h" // laodFileJSONroot
47 #include "protocols.h" // ISO7816 APDU return codes
49 #define DEF_FIDO_SIZE 2048
50 #define DEF_FIDO_PARAM_FILE "hf_fido2_defparams.json"
52 static int CmdHelp(const char *Cmd
);
54 static int CmdHFFidoList(const char *Cmd
) {
55 return CmdTraceListAlias(Cmd
, "hf fido", "14a");
58 static int CmdHFFidoInfo(const char *Cmd
) {
59 CLIParserContext
*ctx
;
60 CLIParserInit(&ctx
, "hf fido info",
61 "Get info from Fido tags",
68 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
71 // info about 14a part
72 infoHF14A(false, false, false);
75 PrintAndLogEx(INFO
, "-----------" _CYAN_("FIDO Info") "---------------------------------");
76 SetAPDULogging(false);
78 uint8_t buf
[APDU_RES_LEN
] = {0};
82 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
88 if (sw
!= ISO7816_OK
) {
90 PrintAndLogEx(INFO
, "Not a FIDO card. APDU response: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
92 PrintAndLogEx(ERR
, "APDU exchange error. Card returns 0x0000");
98 if (strncmp((char *)buf
, "U2F_V2", 7) == 0) {
99 if (strncmp((char *)buf
, "FIDO_2_0", 8) == 0) {
100 PrintAndLogEx(INFO
, "FIDO2 authenticator");
101 PrintAndLogEx(INFO
, "Version... " _YELLOW_("%.*s"), (int)len
, buf
);
103 PrintAndLogEx(INFO
, "FIDO authenticator (not standard U2F)");
104 PrintAndLogEx(INFO
, "Non U2F authenticator");
105 PrintAndLogEx(INFO
, "version... ");
106 print_buffer((const unsigned char *)buf
, len
, 1);
109 PrintAndLogEx(INFO
, "FIDO U2F authenticator detected");
110 PrintAndLogEx(INFO
, "Version... " _YELLOW_("%.*s"), (int)len
, buf
);
113 res
= FIDO2GetInfo(buf
, sizeof(buf
), &len
, &sw
);
119 if (sw
!= ISO7816_OK
) {
120 PrintAndLogEx(ERR
, "FIDO2 version doesn't exist (%04x - %s).", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
125 PrintAndLogEx(ERR
, "FIDO2 get version error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
130 PrintAndLogEx(SUCCESS
, "FIDO2 version CBOR decoded:");
131 TinyCborPrintFIDOPackage(fido2CmdGetInfo
, true, &buf
[1], len
- 1);
133 PrintAndLogEx(ERR
, "FIDO2 version length error");
138 static int CmdHFFidoRegister(const char *cmd
) {
139 CLIParserContext
*ctx
;
140 CLIParserInit(&ctx
, "hf fido reg",
141 "Initiate a U2F token registration. Needs two 32-byte hash numbers.\n"
142 "challenge parameter (32b) and application parameter (32b).\n"
143 "The default config filename is `fido2_defparams.json`\n"
145 " `-vv` shows full certificates data\n"
147 "hf fido reg -> execute command with 2 parameters, filled 0x00\n"
148 "hf fido reg --cp s0 --ap s1 -> execute command with plain parameters\n"
149 "hf fido reg --cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f --apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
150 "hf fido reg -f fido2-params -> execute command with custom config file\n"
155 arg_lit0("a", "apdu", "Show APDU requests and responses"),
156 arg_litn("v", "verbose", 0, 2, "Verbose output"),
157 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
158 arg_str0("f", "file", "<fn>", "JSON input file name for parameters"),
159 arg_str0(NULL
, "cp", "<str>", "Challenge parameter (1..16 chars)"),
160 arg_str0(NULL
, "ap", "<str>", "Application parameter (1..16 chars)"),
161 arg_str0(NULL
, "cpx", "<hex>", "Challenge parameter (32 bytes hex)"),
162 arg_str0(NULL
, "apx", "<hex>", "Application parameter (32 bytes hex)"),
165 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
167 bool APDULogging
= arg_get_lit(ctx
, 1);
168 bool verbose
= arg_get_lit(ctx
, 2);
169 bool verbose2
= arg_get_lit(ctx
, 2) > 1;
170 bool showDERTLV
= arg_get_lit(ctx
, 3);
171 bool cpplain
= arg_get_str_len(ctx
, 5);
172 bool applain
= arg_get_str_len(ctx
, 6);
173 bool cphex
= arg_get_str_len(ctx
, 7);
174 bool aphex
= arg_get_str_len(ctx
, 8);
176 uint8_t data
[64] = {0};
178 uint8_t cdata
[250] = {0};
180 uint8_t adata
[250] = {0};
183 char filename
[FILE_PATH_SIZE
] = {0};
184 CLIParamStrToBuf(arg_get_str(ctx
, 4), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
188 strcat(filename
, DEF_FIDO_PARAM_FILE
);
189 fnlen
= strlen(filename
);
193 int res
= loadFileJSONroot(filename
, (void **)&root
, verbose
);
194 if (res
!= PM3_SUCCESS
) {
200 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
201 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
204 memset(cdata
, 0x00, 32);
205 chlen
= sizeof(cdata
);
206 CLIGetStrWithReturn(ctx
, 5, cdata
, &chlen
);
208 PrintAndLogEx(ERR
, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen
);
214 if (cphex
&& cpplain
== false) {
215 chlen
= sizeof(cdata
);
216 CLIGetHexWithReturn(ctx
, 7, cdata
, &chlen
);
217 if (chlen
&& chlen
!= 32) {
218 PrintAndLogEx(ERR
, "ERROR: challenge parameter length must be 32 bytes only.");
225 memmove(data
, cdata
, 32);
228 memset(adata
, 0x00, 32);
229 applen
= sizeof(adata
);
230 CLIGetStrWithReturn(ctx
, 6, adata
, &applen
);
232 PrintAndLogEx(ERR
, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen
);
238 if (aphex
&& applain
== false) {
239 applen
= sizeof(adata
);
240 CLIGetHexWithReturn(ctx
, 8, adata
, &applen
);
241 if (applen
&& applen
!= 32) {
242 PrintAndLogEx(ERR
, "ERROR: application parameter length must be 32 bytes only.");
249 memmove(&data
[32], adata
, 32);
253 SetAPDULogging(APDULogging
);
255 // 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
256 // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
258 uint8_t buf
[2048] = {0};
263 res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
266 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
272 if (sw
!= ISO7816_OK
) {
273 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
279 res
= FIDORegister(data
, buf
, sizeof(buf
), &len
, &sw
);
282 PrintAndLogEx(ERR
, "Can't execute register command. res=%x. Exit...", res
);
287 if (sw
!= ISO7816_OK
) {
288 PrintAndLogEx(ERR
, "ERROR execute register command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
292 PrintAndLogEx(NORMAL
, "");
294 PrintAndLogEx(INFO
, "---------------------------------------------------------------");
296 PrintAndLogEx(INFO
, "data len: %zu", len
);
299 PrintAndLogEx(INFO
, "------------ " _CYAN_("data") " ----------------------");
300 print_buffer((const unsigned char *)buf
, len
, 1);
301 PrintAndLogEx(INFO
, "-------------" _CYAN_("data") " ----------------------");
304 if (buf
[0] != 0x05) {
305 PrintAndLogEx(ERR
, "ERROR: First byte must be 0x05, but it %2x", buf
[0]);
309 PrintAndLogEx(SUCCESS
, "User public key: %s", sprint_hex(&buf
[1], 65));
311 uint8_t keyHandleLen
= buf
[66];
312 PrintAndLogEx(SUCCESS
, "Key handle[%d]: %s", keyHandleLen
, sprint_hex(&buf
[67], keyHandleLen
));
314 int derp
= 67 + keyHandleLen
;
315 int derLen
= (buf
[derp
+ 2] << 8) + buf
[derp
+ 3] + 4;
317 PrintAndLogEx(INFO
, "DER certificate[%d]:", derLen
);
318 PrintAndLogEx(INFO
, "------------------DER-------------------");
319 PrintAndLogEx(INFO
, "%s", sprint_hex(buf
+ derp
, derLen
));
320 PrintAndLogEx(INFO
, "----------------DER---------------------");
323 PrintAndLogEx(INFO
, "------------------DER-------------------");
324 PrintAndLogEx(INFO
, "DER certificate[%d]: %s...", derLen
, sprint_hex(&buf
[derp
], 20));
327 // check and print DER certificate
328 uint8_t public_key
[65] = {0};
330 // print DER certificate in TLV view
332 PrintAndLogEx(INFO
, "----------------DER TLV-----------------");
333 asn1_print(&buf
[derp
], derLen
, " ");
334 PrintAndLogEx(INFO
, "----------------DER TLV-----------------");
337 FIDOCheckDERAndGetKey(&buf
[derp
], derLen
, verbose
, public_key
, sizeof(public_key
));
340 int hashp
= 1 + 65 + 1 + keyHandleLen
+ derLen
;
341 PrintAndLogEx(SUCCESS
, "Hash[%zu]: %s", len
- hashp
, sprint_hex(&buf
[hashp
], len
- hashp
));
343 // check ANSI X9.62 format ECDSA signature (on P-256)
344 uint8_t rval
[300] = {0};
345 uint8_t sval
[300] = {0};
346 res
= ecdsa_asn1_get_signature(&buf
[hashp
], len
- hashp
, rval
, sval
);
347 if (res
== PM3_SUCCESS
) {
349 PrintAndLogEx(INFO
, " r: %s", sprint_hex(rval
, 32));
350 PrintAndLogEx(INFO
, " s: %s", sprint_hex(sval
, 32));
353 uint8_t xbuf
[4096] = {0};
355 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
357 &data
[32], 32, // application parameter
358 &data
[0], 32, // challenge parameter
359 &buf
[67], keyHandleLen
, // keyHandle
360 &buf
[1], 65, // user public key
363 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
364 res
= ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1
, public_key
, xbuf
, xbuflen
, &buf
[hashp
], len
- hashp
, true);
366 if (res
== MBEDTLS_ERR_ECP_VERIFY_FAILED
) {
367 PrintAndLogEx(WARNING
, "Signature is ( " _RED_("not valid") " )");
369 PrintAndLogEx(WARNING
, "Other signature check error: %x %s", (res
< 0) ? -res
: res
, ecdsa_get_error(res
));
372 PrintAndLogEx(SUCCESS
, "Signature is ( " _GREEN_("ok") " )");
376 PrintAndLogEx(WARNING
, "Invalid signature. res = %d. ( " _RED_("fail") " )", res
);
379 PrintAndLogEx(INFO
, "");
380 PrintAndLogEx(INFO
, "auth command: ");
381 char command
[500] = {0};
382 snprintf(command
, sizeof(command
), "hf fido auth --kh %s", sprint_hex_inrow(&buf
[67], keyHandleLen
));
384 size_t command_len
= strlen(command
);
385 snprintf(command
+ command_len
, sizeof(command
) - command_len
, " --%s %s", cpplain
? "cp" : "cpx", cpplain
? (char *)cdata
: sprint_hex_inrow(cdata
, 32));
388 size_t command_len
= strlen(command
);
389 snprintf(command
+ command_len
, sizeof(command
) - command_len
, " --%s %s", applain
? "cp" : "cpx", applain
? (char *)adata
: sprint_hex_inrow(adata
, 32));
391 PrintAndLogEx(INFO
, "%s", command
);
394 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
395 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
396 JsonSaveBufAsHexCompact(root
, "PublicKey", &buf
[1], 65);
397 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
398 JsonSaveBufAsHexCompact(root
, "KeyHandle", &buf
[67], keyHandleLen
);
399 JsonSaveBufAsHexCompact(root
, "DER", &buf
[67 + keyHandleLen
], derLen
);
401 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true);
408 static int CmdHFFidoAuthenticate(const char *cmd
) {
409 CLIParserContext
*ctx
;
410 CLIParserInit(&ctx
, "hf fido auth",
411 "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers.\n"
412 "key handle(var 0..255), challenge parameter (32b) and application parameter (32b)\n"
413 "The default config filename is `fido2_defparams.json`\n"
415 "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
417 "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
418 "--cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n"
419 "--apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
423 arg_lit0("a", "apdu", "Show APDU requests and responses"),
424 arg_lit0("v", "verbose", "Verbose output"),
425 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
426 arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"),
427 arg_lit0("c", "check", "mode: check-only"),
428 arg_str0("f", "file", "<fn>", "JSON file name for parameters"),
429 arg_str0("k", "key", "<hex>", "Public key to verify signature"),
430 arg_str0(NULL
, "kh", "<hex>", "Key handle (var 0..255b)"),
431 arg_str0(NULL
, "cp", "<str>", "Challenge parameter (1..16 chars)"),
432 arg_str0(NULL
, "ap", "<str>", "Application parameter (1..16 chars)"),
433 arg_str0(NULL
, "cpx", "<hex>", "Challenge parameter (32 bytes hex)"),
434 arg_str0(NULL
, "apx", "<hex>", "Application parameter (32 bytes hex)"),
437 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
439 bool APDULogging
= arg_get_lit(ctx
, 1);
440 bool verbose
= arg_get_lit(ctx
, 2);
442 uint8_t controlByte
= 0x08;
443 if (arg_get_lit(ctx
, 4))
446 if (arg_get_lit(ctx
, 5))
449 uint8_t data
[512] = {0};
450 uint8_t hdata
[256] = {0};
451 bool public_key_loaded
= false;
452 uint8_t public_key
[65] = {0};
454 uint8_t keyHandleLen
= 0;
457 char filename
[FILE_PATH_SIZE
] = {0};
458 CLIParamStrToBuf(arg_get_str(ctx
, 6), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
462 strcat(filename
, DEF_FIDO_PARAM_FILE
);
463 fnlen
= strlen(filename
);
467 int res
= loadFileJSONroot(filename
, (void **)&root
, verbose
);
468 if (res
!= PM3_SUCCESS
) {
474 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
475 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
476 JsonLoadBufAsHex(root
, "$.KeyHandle", &data
[65], 512 - 67, &jlen
);
477 keyHandleLen
= jlen
& 0xff;
478 data
[64] = keyHandleLen
;
479 JsonLoadBufAsHex(root
, "$.PublicKey", public_key
, 65, &jlen
);
480 public_key_loaded
= (jlen
> 0);
483 CLIGetHexWithReturn(ctx
, 7, hdata
, &hdatalen
);
484 if (hdatalen
&& hdatalen
!= 65) {
485 PrintAndLogEx(ERR
, "ERROR: public key length must be 65 bytes only.");
492 memmove(public_key
, hdata
, hdatalen
);
493 public_key_loaded
= true;
496 CLIGetHexWithReturn(ctx
, 8, hdata
, &hdatalen
);
497 if (hdatalen
> 255) {
498 PrintAndLogEx(ERR
, "ERROR: key handle length must be less than 255.");
504 printf("-- hlen=%d\n", hdatalen
);
506 keyHandleLen
= hdatalen
;
507 data
[64] = keyHandleLen
;
508 memmove(&data
[65], hdata
, keyHandleLen
);
512 bool cpplain
= arg_get_str_len(ctx
, 9);
513 bool applain
= arg_get_str_len(ctx
, 10);
514 bool cphex
= arg_get_str_len(ctx
, 11);
515 bool aphex
= arg_get_str_len(ctx
, 12);
518 memset(hdata
, 0x00, 32);
519 hdatalen
= sizeof(hdata
);
520 CLIGetStrWithReturn(ctx
, 9, hdata
, &hdatalen
);
522 PrintAndLogEx(ERR
, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
528 if (cphex
&& cpplain
== false) {
529 hdatalen
= sizeof(hdata
);
530 CLIGetHexWithReturn(ctx
, 11, hdata
, &hdatalen
);
531 if (hdatalen
&& hdatalen
!= 32) {
532 PrintAndLogEx(ERR
, "ERROR: challenge parameter length must be 32 bytes only.");
539 memmove(data
, hdata
, 32);
544 memset(hdata
, 0x00, 32);
545 hdatalen
= sizeof(hdata
);
546 CLIGetStrWithReturn(ctx
, 10, hdata
, &hdatalen
);
548 PrintAndLogEx(ERR
, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
554 if (aphex
&& applain
== false) {
555 hdatalen
= sizeof(hdata
);
556 CLIGetHexWithReturn(ctx
, 12, hdata
, &hdatalen
);
557 if (hdatalen
&& hdatalen
!= 32) {
558 PrintAndLogEx(ERR
, "ERROR: application parameter length must be 32 bytes only.");
565 memmove(&data
[32], hdata
, 32);
571 SetAPDULogging(APDULogging
);
573 // (in parameter) control byte 0x07 - check only, 0x03 - user presence + cign. 0x08 - sign only
574 // challenge parameter [32 bytes]
575 // application parameter [32 bytes]
576 // key handle length [1b] = N
579 uint8_t datalen
= 32 + 32 + 1 + keyHandleLen
;
581 uint8_t buf
[2048] = {0};
586 res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
588 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
594 if (sw
!= ISO7816_OK
) {
595 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
601 res
= FIDOAuthentication(data
, datalen
, controlByte
, buf
, sizeof(buf
), &len
, &sw
);
604 PrintAndLogEx(ERR
, "Can't execute authentication command. res=%x. Exit...", res
);
609 if (sw
!= ISO7816_OK
) {
610 PrintAndLogEx(ERR
, "ERROR execute authentication command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
615 PrintAndLogEx(INFO
, "---------------------------------------------------------------");
616 PrintAndLogEx(SUCCESS
, "User presence: %s", (buf
[0] ? "verified" : "not verified"));
617 uint32_t cntr
= (uint32_t)bytes_to_num(&buf
[1], 4);
618 PrintAndLogEx(SUCCESS
, "Counter: %d", cntr
);
619 PrintAndLogEx(SUCCESS
, "Hash[%zu]: %s", len
- 5, sprint_hex(&buf
[5], len
- 5));
621 // check ANSI X9.62 format ECDSA signature (on P-256)
622 uint8_t rval
[300] = {0};
623 uint8_t sval
[300] = {0};
624 res
= ecdsa_asn1_get_signature(&buf
[5], len
- 5, rval
, sval
);
625 if (res
== PM3_SUCCESS
) {
627 PrintAndLogEx(INFO
, " r: %s", sprint_hex(rval
, 32));
628 PrintAndLogEx(INFO
, " s: %s", sprint_hex(sval
, 32));
630 if (public_key_loaded
) {
631 uint8_t xbuf
[4096] = {0};
633 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
634 &data
[32], 32, // application parameter
635 &buf
[0], 1, // user presence
636 &buf
[1], 4, // counter
637 data
, 32, // challenge parameter
640 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
641 res
= ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1
, public_key
, xbuf
, xbuflen
, &buf
[5], len
- 5, true);
643 if (res
== MBEDTLS_ERR_ECP_VERIFY_FAILED
) {
644 PrintAndLogEx(WARNING
, "Signature is ( " _RED_("not valid") " )");
646 PrintAndLogEx(WARNING
, "Other signature check error: %x %s", (res
< 0) ? -res
: res
, ecdsa_get_error(res
));
649 PrintAndLogEx(SUCCESS
, "Signature is ( " _GREEN_("ok") " )");
652 PrintAndLogEx(WARNING
, "No public key provided. can't check signature.");
655 PrintAndLogEx(WARNING
, "Invalid signature. res = %d. ( " _RED_("fail") " )", res
);
659 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
660 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
661 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
662 JsonSaveBufAsHexCompact(root
, "KeyHandle", &data
[65], keyHandleLen
);
663 JsonSaveInt(root
, "Counter", cntr
);
665 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true);
672 static int CmdHFFido2MakeCredential(const char *cmd
) {
673 CLIParserContext
*ctx
;
674 CLIParserInit(&ctx
, "hf fido make",
675 "Execute a FIDO2 Make Credential command. Needs json file with parameters.\n"
676 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
677 "- for yubikey there must be only one option `\"rk\": true` or false\n"
679 " `-vv` shows full certificates data\n",
680 "hf fido make --> use default parameters file `fido2_defparams.json`\n"
681 "hf fido make -f test.json --> use parameters file `text.json`"
686 arg_lit0("a", "apdu", "Show APDU requests and responses"),
687 arg_litn("v", "verbose", 0, 2, "Verbose output"),
688 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
689 arg_lit0("c", "cbor", "Show CBOR decoded data"),
690 arg_str0("f", "file", "<fn>", "Parameter JSON file name"),
693 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
695 bool APDULogging
= arg_get_lit(ctx
, 1);
696 bool verbose
= arg_get_lit(ctx
, 2);
697 bool verbose2
= arg_get_lit(ctx
, 2) > 1;
698 bool showDERTLV
= arg_get_lit(ctx
, 3);
699 bool showCBOR
= arg_get_lit(ctx
, 4);
702 char filename
[FILE_PATH_SIZE
] = {0};
703 CLIParamStrToBuf(arg_get_str(ctx
, 5), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
708 strcat(filename
, DEF_FIDO_PARAM_FILE
);
709 fnlen
= strlen(filename
);
713 loadFileJSONroot(filename
, (void **)&root
, verbose
);
718 SetAPDULogging(APDULogging
);
720 uint8_t data
[DEF_FIDO_SIZE
] = {0};
722 uint8_t buf
[DEF_FIDO_SIZE
] = {0};
727 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
729 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
735 if (sw
!= ISO7816_OK
) {
736 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
742 res
= FIDO2CreateMakeCredentionalReq(root
, data
, sizeof(data
), &datalen
);
749 PrintAndLogEx(INFO
, "CBOR make credential request:");
750 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
751 TinyCborPrintFIDOPackage(fido2CmdMakeCredential
, false, data
, datalen
);
752 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
755 res
= FIDO2MakeCredential(data
, datalen
, buf
, sizeof(buf
), &len
, &sw
);
758 PrintAndLogEx(ERR
, "Can't execute make credential command. res=%x. exit...", res
);
763 if (sw
!= ISO7816_OK
) {
764 PrintAndLogEx(ERR
, "ERROR execute make credential command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
770 PrintAndLogEx(ERR
, "FIDO2 make credential error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
775 PrintAndLogEx(SUCCESS
, "MakeCredential result %zu b ( ok )", len
);
777 PrintAndLogEx(SUCCESS
, "CBOR make credential response:");
778 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
779 TinyCborPrintFIDOPackage(fido2CmdMakeCredential
, true, &buf
[1], len
- 1);
780 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
783 // parse returned cbor
784 FIDO2MakeCredentionalParseRes(root
, &buf
[1], len
- 1, verbose
, verbose2
, showCBOR
, showDERTLV
);
786 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true);
792 static int CmdHFFido2GetAssertion(const char *cmd
) {
793 CLIParserContext
*ctx
;
794 CLIParserInit(&ctx
, "hf fido assert",
795 "Execute a FIDO2 Get Assertion command. Needs json file with parameters.\n"
796 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
797 "- Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)\n"
798 "- for yubikey there must be only one option `\"up\": true` or false\n"
800 " `-vv` shows full certificates data\n",
801 "hf fido assert --> default parameters file `fido2_defparams.json`\n"
802 "hf fido assert -f test.json -l --> use parameters file `text.json` and add to request CredentialId");
806 arg_lit0("a", "apdu", "Show APDU requests and responses"),
807 arg_litn("v", "verbose", 0, 2, "Verbose output"),
808 arg_lit0("c", "cbor", "Show CBOR decoded data"),
809 arg_lit0("l", "list", "Add CredentialId from json to allowList"),
810 arg_str0("f", "file", "<fn>", "Parameter JSON file name"),
813 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
815 bool APDULogging
= arg_get_lit(ctx
, 1);
816 bool verbose
= arg_get_lit(ctx
, 2);
817 bool verbose2
= arg_get_lit(ctx
, 2) > 1;
818 bool showCBOR
= arg_get_lit(ctx
, 3);
819 bool createAllowList
= arg_get_lit(ctx
, 4);
822 char filename
[FILE_PATH_SIZE
] = {0};
823 CLIParamStrToBuf(arg_get_str(ctx
, 5), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
828 strcat(filename
, DEF_FIDO_PARAM_FILE
);
829 fnlen
= strlen(filename
);
833 loadFileJSONroot(filename
, (void **)&root
, verbose
);
838 SetAPDULogging(APDULogging
);
840 uint8_t data
[DEF_FIDO_SIZE
] = {0};
842 uint8_t buf
[DEF_FIDO_SIZE
] = {0};
847 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
849 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. exiting...", res
);
855 if (sw
!= ISO7816_OK
) {
856 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
862 res
= FIDO2CreateGetAssertionReq(root
, data
, sizeof(data
), &datalen
, createAllowList
);
869 PrintAndLogEx(SUCCESS
, "CBOR get assertion request:");
870 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
871 TinyCborPrintFIDOPackage(fido2CmdGetAssertion
, false, data
, datalen
);
872 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
875 res
= FIDO2GetAssertion(data
, datalen
, buf
, sizeof(buf
), &len
, &sw
);
878 PrintAndLogEx(ERR
, "Can't execute get assertion command. res=%x. Exit...", res
);
883 if (sw
!= ISO7816_OK
) {
884 PrintAndLogEx(ERR
, "ERROR execute get assertion command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
890 PrintAndLogEx(ERR
, "FIDO2 get assertion error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
895 PrintAndLogEx(SUCCESS
, "GetAssertion result (%zu b) OK.", len
);
897 PrintAndLogEx(SUCCESS
, "CBOR get assertion response:");
898 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
899 TinyCborPrintFIDOPackage(fido2CmdGetAssertion
, true, &buf
[1], len
- 1);
900 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
903 // parse returned cbor
904 FIDO2GetAssertionParseRes(root
, &buf
[1], len
- 1, verbose
, verbose2
, showCBOR
);
906 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true);
912 static command_t CommandTable
[] = {
913 {"help", CmdHelp
, AlwaysAvailable
, "This help."},
914 {"list", CmdHFFidoList
, AlwaysAvailable
, "List ISO 14443A history"},
915 {"info", CmdHFFidoInfo
, IfPm3Iso14443a
, "Info about FIDO tag."},
916 {"reg", CmdHFFidoRegister
, IfPm3Iso14443a
, "FIDO U2F Registration Message."},
917 {"auth", CmdHFFidoAuthenticate
, IfPm3Iso14443a
, "FIDO U2F Authentication Message."},
918 {"make", CmdHFFido2MakeCredential
, IfPm3Iso14443a
, "FIDO2 MakeCredential command."},
919 {"assert", CmdHFFido2GetAssertion
, IfPm3Iso14443a
, "FIDO2 GetAssertion command."},
920 {NULL
, NULL
, 0, NULL
}
923 int CmdHFFido(const char *Cmd
) {
924 clearCommandBuffer();
925 return CmdsParse(CommandTable
, Cmd
);
928 int CmdHelp(const char *Cmd
) {
929 (void)Cmd
; // Cmd is not used so far
930 CmdsHelp(CommandTable
);