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
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
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
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
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);
254 SetAPDULogging(APDULogging
);
256 // 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
257 // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
259 uint8_t buf
[2048] = {0};
264 res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
267 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
273 if (sw
!= ISO7816_OK
) {
274 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
280 res
= FIDORegister(data
, buf
, sizeof(buf
), &len
, &sw
);
283 PrintAndLogEx(ERR
, "Can't execute register command. res=%x. Exit...", res
);
288 if (sw
!= ISO7816_OK
) {
289 PrintAndLogEx(ERR
, "ERROR execute register command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
293 PrintAndLogEx(NORMAL
, "");
295 PrintAndLogEx(INFO
, "---------------------------------------------------------------");
297 PrintAndLogEx(INFO
, "data len: %zu", len
);
300 PrintAndLogEx(INFO
, "------------ " _CYAN_("data") " ----------------------");
301 print_buffer((const unsigned char *)buf
, len
, 1);
302 PrintAndLogEx(INFO
, "-------------" _CYAN_("data") " ----------------------");
305 if (buf
[0] != 0x05) {
306 PrintAndLogEx(ERR
, "ERROR: First byte must be 0x05, but it %2x", buf
[0]);
310 PrintAndLogEx(SUCCESS
, "User public key: %s", sprint_hex(&buf
[1], 65));
312 uint8_t keyHandleLen
= buf
[66];
313 PrintAndLogEx(SUCCESS
, "Key handle[%d]: %s", keyHandleLen
, sprint_hex(&buf
[67], keyHandleLen
));
315 int derp
= 67 + keyHandleLen
;
316 int derLen
= (buf
[derp
+ 2] << 8) + buf
[derp
+ 3] + 4;
318 PrintAndLogEx(INFO
, "DER certificate[%d]:", derLen
);
319 PrintAndLogEx(INFO
, "------------------DER-------------------");
320 PrintAndLogEx(INFO
, "%s", sprint_hex(buf
+ derp
, derLen
));
321 PrintAndLogEx(INFO
, "----------------DER---------------------");
324 PrintAndLogEx(INFO
, "------------------DER-------------------");
325 PrintAndLogEx(INFO
, "DER certificate[%d]: %s...", derLen
, sprint_hex(&buf
[derp
], 20));
328 // check and print DER certificate
329 uint8_t public_key
[65] = {0};
331 // print DER certificate in TLV view
333 PrintAndLogEx(INFO
, "----------------DER TLV-----------------");
334 asn1_print(&buf
[derp
], derLen
, " ");
335 PrintAndLogEx(INFO
, "----------------DER TLV-----------------");
338 FIDOCheckDERAndGetKey(&buf
[derp
], derLen
, verbose
, public_key
, sizeof(public_key
));
341 int hashp
= 1 + 65 + 1 + keyHandleLen
+ derLen
;
342 PrintAndLogEx(SUCCESS
, "Hash[%zu]: %s", len
- hashp
, sprint_hex(&buf
[hashp
], len
- hashp
));
344 // check ANSI X9.62 format ECDSA signature (on P-256)
345 uint8_t rval
[300] = {0};
346 uint8_t sval
[300] = {0};
347 res
= ecdsa_asn1_get_signature(&buf
[hashp
], len
- hashp
, rval
, sval
);
348 if (res
== PM3_SUCCESS
) {
350 PrintAndLogEx(INFO
, " r: %s", sprint_hex(rval
, 32));
351 PrintAndLogEx(INFO
, " s: %s", sprint_hex(sval
, 32));
354 uint8_t xbuf
[4096] = {0};
356 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
358 &data
[32], 32, // application parameter
359 &data
[0], 32, // challenge parameter
360 &buf
[67], keyHandleLen
, // keyHandle
361 &buf
[1], 65, // user public key
364 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
365 res
= ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1
, public_key
, xbuf
, xbuflen
, &buf
[hashp
], len
- hashp
, true);
367 if (res
== MBEDTLS_ERR_ECP_VERIFY_FAILED
) {
368 PrintAndLogEx(WARNING
, "Signature is ( " _RED_("not valid") " )");
370 PrintAndLogEx(WARNING
, "Other signature check error: %x %s", (res
< 0) ? -res
: res
, ecdsa_get_error(res
));
373 PrintAndLogEx(SUCCESS
, "Signature is ( " _GREEN_("ok") " )");
377 PrintAndLogEx(WARNING
, "Invalid signature. res = %d. ( " _RED_("fail") " )", res
);
380 PrintAndLogEx(INFO
, "");
381 PrintAndLogEx(INFO
, "auth command: ");
382 char command
[500] = {0};
383 snprintf(command
, sizeof(command
), "hf fido auth --kh %s", sprint_hex_inrow(&buf
[67], keyHandleLen
));
385 size_t command_len
= strlen(command
);
386 snprintf(command
+ command_len
, sizeof(command
) - command_len
, " --%s %s", cpplain
? "cp" : "cpx", cpplain
? (char *)cdata
: sprint_hex_inrow(cdata
, 32));
389 size_t command_len
= strlen(command
);
390 snprintf(command
+ command_len
, sizeof(command
) - command_len
, " --%s %s", applain
? "cp" : "cpx", applain
? (char *)adata
: sprint_hex_inrow(adata
, 32));
392 PrintAndLogEx(INFO
, "%s", command
);
395 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
396 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
397 JsonSaveBufAsHexCompact(root
, "PublicKey", &buf
[1], 65);
398 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
399 JsonSaveBufAsHexCompact(root
, "KeyHandle", &buf
[67], keyHandleLen
);
400 JsonSaveBufAsHexCompact(root
, "DER", &buf
[67 + keyHandleLen
], derLen
);
402 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true, spDump
);
409 static int CmdHFFidoAuthenticate(const char *cmd
) {
410 CLIParserContext
*ctx
;
411 CLIParserInit(&ctx
, "hf fido auth",
412 "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers.\n"
413 "key handle(var 0..255), challenge parameter (32b) and application parameter (32b)\n"
414 "The default config filename is `fido2_defparams.json`\n"
416 "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
418 "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
419 "--cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n"
420 "--apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
424 arg_lit0("a", "apdu", "Show APDU requests and responses"),
425 arg_lit0("v", "verbose", "Verbose output"),
426 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
427 arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"),
428 arg_lit0("c", "check", "mode: check-only"),
429 arg_str0("f", "file", "<fn>", "JSON file name for parameters"),
430 arg_str0("k", "key", "<hex>", "Public key to verify signature"),
431 arg_str0(NULL
, "kh", "<hex>", "Key handle (var 0..255b)"),
432 arg_str0(NULL
, "cp", "<str>", "Challenge parameter (1..16 chars)"),
433 arg_str0(NULL
, "ap", "<str>", "Application parameter (1..16 chars)"),
434 arg_str0(NULL
, "cpx", "<hex>", "Challenge parameter (32 bytes hex)"),
435 arg_str0(NULL
, "apx", "<hex>", "Application parameter (32 bytes hex)"),
438 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
440 bool APDULogging
= arg_get_lit(ctx
, 1);
441 bool verbose
= arg_get_lit(ctx
, 2);
443 uint8_t controlByte
= 0x08;
444 if (arg_get_lit(ctx
, 4))
447 if (arg_get_lit(ctx
, 5))
450 uint8_t data
[512] = {0};
451 uint8_t hdata
[256] = {0};
452 bool public_key_loaded
= false;
453 uint8_t public_key
[65] = {0};
455 uint8_t keyHandleLen
= 0;
458 char filename
[FILE_PATH_SIZE
] = {0};
459 CLIParamStrToBuf(arg_get_str(ctx
, 6), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
463 strcat(filename
, DEF_FIDO_PARAM_FILE
);
464 fnlen
= strlen(filename
);
468 int res
= loadFileJSONroot(filename
, (void **)&root
, verbose
);
469 if (res
!= PM3_SUCCESS
) {
475 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
476 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
477 JsonLoadBufAsHex(root
, "$.KeyHandle", &data
[65], 512 - 67, &jlen
);
478 keyHandleLen
= jlen
& 0xff;
479 data
[64] = keyHandleLen
;
480 JsonLoadBufAsHex(root
, "$.PublicKey", public_key
, 65, &jlen
);
481 public_key_loaded
= (jlen
> 0);
484 CLIGetHexWithReturn(ctx
, 7, hdata
, &hdatalen
);
485 if (hdatalen
&& hdatalen
!= 65) {
486 PrintAndLogEx(ERR
, "ERROR: public key length must be 65 bytes only.");
493 memmove(public_key
, hdata
, hdatalen
);
494 public_key_loaded
= true;
497 CLIGetHexWithReturn(ctx
, 8, hdata
, &hdatalen
);
498 if (hdatalen
> 255) {
499 PrintAndLogEx(ERR
, "ERROR: key handle length must be less than 255.");
505 printf("-- hlen=%d\n", hdatalen
);
507 keyHandleLen
= hdatalen
;
508 data
[64] = keyHandleLen
;
509 memmove(&data
[65], hdata
, keyHandleLen
);
513 bool cpplain
= arg_get_str_len(ctx
, 9);
514 bool applain
= arg_get_str_len(ctx
, 10);
515 bool cphex
= arg_get_str_len(ctx
, 11);
516 bool aphex
= arg_get_str_len(ctx
, 12);
519 memset(hdata
, 0x00, 32);
520 hdatalen
= sizeof(hdata
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
521 CLIGetStrWithReturn(ctx
, 9, hdata
, &hdatalen
);
523 PrintAndLogEx(ERR
, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
529 if (cphex
&& cpplain
== false) {
530 hdatalen
= sizeof(hdata
);
531 CLIGetHexWithReturn(ctx
, 11, hdata
, &hdatalen
);
532 if (hdatalen
&& hdatalen
!= 32) {
533 PrintAndLogEx(ERR
, "ERROR: challenge parameter length must be 32 bytes only.");
540 memmove(data
, hdata
, 32);
545 memset(hdata
, 0x00, 32);
546 hdatalen
= sizeof(hdata
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
547 CLIGetStrWithReturn(ctx
, 10, hdata
, &hdatalen
);
549 PrintAndLogEx(ERR
, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
555 if (aphex
&& applain
== false) {
556 hdatalen
= sizeof(hdata
);
557 CLIGetHexWithReturn(ctx
, 12, hdata
, &hdatalen
);
558 if (hdatalen
&& hdatalen
!= 32) {
559 PrintAndLogEx(ERR
, "ERROR: application parameter length must be 32 bytes only.");
566 memmove(&data
[32], hdata
, 32);
572 SetAPDULogging(APDULogging
);
574 // (in parameter) control byte 0x07 - check only, 0x03 - user presence + cign. 0x08 - sign only
575 // challenge parameter [32 bytes]
576 // application parameter [32 bytes]
577 // key handle length [1b] = N
580 uint8_t datalen
= 32 + 32 + 1 + keyHandleLen
;
582 uint8_t buf
[2048] = {0};
587 res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
589 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
595 if (sw
!= ISO7816_OK
) {
596 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
602 res
= FIDOAuthentication(data
, datalen
, controlByte
, buf
, sizeof(buf
), &len
, &sw
);
605 PrintAndLogEx(ERR
, "Can't execute authentication command. res=%x. Exit...", res
);
610 if (sw
!= ISO7816_OK
) {
611 PrintAndLogEx(ERR
, "ERROR execute authentication command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
616 PrintAndLogEx(INFO
, "---------------------------------------------------------------");
617 PrintAndLogEx(SUCCESS
, "User presence: %s", (buf
[0] ? "verified" : "not verified"));
618 uint32_t cntr
= (uint32_t)bytes_to_num(&buf
[1], 4);
619 PrintAndLogEx(SUCCESS
, "Counter: %d", cntr
);
620 PrintAndLogEx(SUCCESS
, "Hash[%zu]: %s", len
- 5, sprint_hex(&buf
[5], len
- 5));
622 // check ANSI X9.62 format ECDSA signature (on P-256)
623 uint8_t rval
[300] = {0};
624 uint8_t sval
[300] = {0};
625 res
= ecdsa_asn1_get_signature(&buf
[5], len
- 5, rval
, sval
);
626 if (res
== PM3_SUCCESS
) {
628 PrintAndLogEx(INFO
, " r: %s", sprint_hex(rval
, 32));
629 PrintAndLogEx(INFO
, " s: %s", sprint_hex(sval
, 32));
631 if (public_key_loaded
) {
632 uint8_t xbuf
[4096] = {0};
634 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
635 &data
[32], 32, // application parameter
636 &buf
[0], 1, // user presence
637 &buf
[1], 4, // counter
638 data
, 32, // challenge parameter
641 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
642 res
= ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1
, public_key
, xbuf
, xbuflen
, &buf
[5], len
- 5, true);
644 if (res
== MBEDTLS_ERR_ECP_VERIFY_FAILED
) {
645 PrintAndLogEx(WARNING
, "Signature is ( " _RED_("not valid") " )");
647 PrintAndLogEx(WARNING
, "Other signature check error: %x %s", (res
< 0) ? -res
: res
, ecdsa_get_error(res
));
650 PrintAndLogEx(SUCCESS
, "Signature is ( " _GREEN_("ok") " )");
653 PrintAndLogEx(WARNING
, "No public key provided. can't check signature.");
656 PrintAndLogEx(WARNING
, "Invalid signature. res = %d. ( " _RED_("fail") " )", res
);
660 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
661 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
662 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
663 JsonSaveBufAsHexCompact(root
, "KeyHandle", &data
[65], keyHandleLen
);
664 JsonSaveInt(root
, "Counter", cntr
);
666 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true, spDump
);
673 static int CmdHFFido2MakeCredential(const char *cmd
) {
674 CLIParserContext
*ctx
;
675 CLIParserInit(&ctx
, "hf fido make",
676 "Execute a FIDO2 Make Credential command. Needs json file with parameters.\n"
677 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
678 "- for yubikey there must be only one option `\"rk\": true` or false\n"
680 " `-vv` shows full certificates data\n",
681 "hf fido make --> use default parameters file `fido2_defparams.json`\n"
682 "hf fido make -f test.json --> use parameters file `text.json`"
687 arg_lit0("a", "apdu", "Show APDU requests and responses"),
688 arg_litn("v", "verbose", 0, 2, "Verbose output"),
689 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
690 arg_lit0("c", "cbor", "Show CBOR decoded data"),
691 arg_str0("f", "file", "<fn>", "Parameter JSON file name"),
694 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
696 bool APDULogging
= arg_get_lit(ctx
, 1);
697 bool verbose
= arg_get_lit(ctx
, 2);
698 bool verbose2
= arg_get_lit(ctx
, 2) > 1;
699 bool showDERTLV
= arg_get_lit(ctx
, 3);
700 bool showCBOR
= arg_get_lit(ctx
, 4);
703 char filename
[FILE_PATH_SIZE
] = {0};
704 CLIParamStrToBuf(arg_get_str(ctx
, 5), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
709 strcat(filename
, DEF_FIDO_PARAM_FILE
);
710 fnlen
= strlen(filename
);
714 loadFileJSONroot(filename
, (void **)&root
, verbose
);
719 SetAPDULogging(APDULogging
);
721 uint8_t data
[DEF_FIDO_SIZE
] = {0};
723 uint8_t buf
[DEF_FIDO_SIZE
] = {0};
728 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
730 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. Exit...", res
);
736 if (sw
!= ISO7816_OK
) {
737 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
743 res
= FIDO2CreateMakeCredentionalReq(root
, data
, sizeof(data
), &datalen
);
750 PrintAndLogEx(INFO
, "CBOR make credential request:");
751 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
752 TinyCborPrintFIDOPackage(fido2CmdMakeCredential
, false, data
, datalen
);
753 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
756 res
= FIDO2MakeCredential(data
, datalen
, buf
, sizeof(buf
), &len
, &sw
);
759 PrintAndLogEx(ERR
, "Can't execute make credential command. res=%x. exit...", res
);
764 if (sw
!= ISO7816_OK
) {
765 PrintAndLogEx(ERR
, "ERROR execute make credential command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
771 PrintAndLogEx(ERR
, "FIDO2 make credential error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
776 PrintAndLogEx(SUCCESS
, "MakeCredential result %zu b ( ok )", len
);
778 PrintAndLogEx(SUCCESS
, "CBOR make credential response:");
779 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
780 TinyCborPrintFIDOPackage(fido2CmdMakeCredential
, true, &buf
[1], len
- 1);
781 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
784 // parse returned cbor
785 FIDO2MakeCredentionalParseRes(root
, &buf
[1], len
- 1, verbose
, verbose2
, showCBOR
, showDERTLV
);
787 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true, spDump
);
793 static int CmdHFFido2GetAssertion(const char *cmd
) {
794 CLIParserContext
*ctx
;
795 CLIParserInit(&ctx
, "hf fido assert",
796 "Execute a FIDO2 Get Assertion command. Needs json file with parameters.\n"
797 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
798 "- Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)\n"
799 "- for yubikey there must be only one option `\"up\": true` or false\n"
801 " `-vv` shows full certificates data\n",
802 "hf fido assert --> default parameters file `fido2_defparams.json`\n"
803 "hf fido assert -f test.json -l --> use parameters file `text.json` and add to request CredentialId");
807 arg_lit0("a", "apdu", "Show APDU requests and responses"),
808 arg_litn("v", "verbose", 0, 2, "Verbose output"),
809 arg_lit0("c", "cbor", "Show CBOR decoded data"),
810 arg_lit0("l", "list", "Add CredentialId from json to allowList"),
811 arg_str0("f", "file", "<fn>", "Parameter JSON file name"),
814 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
816 bool APDULogging
= arg_get_lit(ctx
, 1);
817 bool verbose
= arg_get_lit(ctx
, 2);
818 bool verbose2
= arg_get_lit(ctx
, 2) > 1;
819 bool showCBOR
= arg_get_lit(ctx
, 3);
820 bool createAllowList
= arg_get_lit(ctx
, 4);
823 char filename
[FILE_PATH_SIZE
] = {0};
824 CLIParamStrToBuf(arg_get_str(ctx
, 5), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
829 strcat(filename
, DEF_FIDO_PARAM_FILE
);
830 fnlen
= strlen(filename
);
834 loadFileJSONroot(filename
, (void **)&root
, verbose
);
839 SetAPDULogging(APDULogging
);
841 uint8_t data
[DEF_FIDO_SIZE
] = {0};
843 uint8_t buf
[DEF_FIDO_SIZE
] = {0};
848 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
850 PrintAndLogEx(ERR
, "Can't select authenticator. res=%x. exiting...", res
);
856 if (sw
!= ISO7816_OK
) {
857 PrintAndLogEx(ERR
, "Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
863 res
= FIDO2CreateGetAssertionReq(root
, data
, sizeof(data
), &datalen
, createAllowList
);
870 PrintAndLogEx(SUCCESS
, "CBOR get assertion request:");
871 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
872 TinyCborPrintFIDOPackage(fido2CmdGetAssertion
, false, data
, datalen
);
873 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
876 res
= FIDO2GetAssertion(data
, datalen
, buf
, sizeof(buf
), &len
, &sw
);
879 PrintAndLogEx(ERR
, "Can't execute get assertion command. res=%x. Exit...", res
);
884 if (sw
!= ISO7816_OK
) {
885 PrintAndLogEx(ERR
, "ERROR execute get assertion command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
891 PrintAndLogEx(ERR
, "FIDO2 get assertion error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
896 PrintAndLogEx(SUCCESS
, "GetAssertion result (%zu b) OK.", len
);
898 PrintAndLogEx(SUCCESS
, "CBOR get assertion response:");
899 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
900 TinyCborPrintFIDOPackage(fido2CmdGetAssertion
, true, &buf
[1], len
- 1);
901 PrintAndLogEx(INFO
, "---------------- " _CYAN_("CBOR") " ------------------");
904 // parse returned cbor
905 FIDO2GetAssertionParseRes(root
, &buf
[1], len
- 1, verbose
, verbose2
, showCBOR
);
907 res
= saveFileJSONrootEx(filename
, root
, JSON_INDENT(2), verbose
, true, spDump
);
913 static command_t CommandTable
[] = {
914 {"help", CmdHelp
, AlwaysAvailable
, "This help."},
915 {"list", CmdHFFidoList
, AlwaysAvailable
, "List ISO 14443A history"},
916 {"info", CmdHFFidoInfo
, IfPm3Iso14443a
, "Info about FIDO tag."},
917 {"reg", CmdHFFidoRegister
, IfPm3Iso14443a
, "FIDO U2F Registration Message."},
918 {"auth", CmdHFFidoAuthenticate
, IfPm3Iso14443a
, "FIDO U2F Authentication Message."},
919 {"make", CmdHFFido2MakeCredential
, IfPm3Iso14443a
, "FIDO2 MakeCredential command."},
920 {"assert", CmdHFFido2GetAssertion
, IfPm3Iso14443a
, "FIDO2 GetAssertion command."},
921 {NULL
, NULL
, 0, NULL
}
924 int CmdHFFido(const char *Cmd
) {
925 clearCommandBuffer();
926 return CmdsParse(CommandTable
, Cmd
);
929 int CmdHelp(const char *Cmd
) {
930 (void)Cmd
; // Cmd is not used so far
931 CmdsHelp(CommandTable
);