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"
42 #include "crypto/asn1utils.h"
43 #include "crypto/libpcrypto.h"
44 #include "fido/additional_ca.h"
45 #include "mbedtls/x509_crt.h"
46 #include "mbedtls/x509.h"
47 #include "mbedtls/pk.h"
49 static int CmdHelp(const char *Cmd
);
51 int FIDOSelect(bool ActivateField
, bool LeaveFieldON
, uint8_t *Result
, size_t MaxResultLen
, size_t *ResultLen
, uint16_t *sw
) {
52 uint8_t data
[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
54 return EMVSelect(ActivateField
, LeaveFieldON
, data
, sizeof(data
), Result
, MaxResultLen
, ResultLen
, sw
, NULL
);
57 int FIDOExchange(sAPDU apdu
, uint8_t *Result
, size_t MaxResultLen
, size_t *ResultLen
, uint16_t *sw
) {
58 int res
= EMVExchange(true, apdu
, Result
, MaxResultLen
, ResultLen
, sw
, NULL
);
59 if (res
== 5) // apdu result (sw) not a 0x9000
62 while (!res
&& (*sw
>> 8) == 0x61) {
63 size_t oldlen
= *ResultLen
;
64 res
= EMVExchange(true, (sAPDU
){0x00, 0xC0, 0x00, 0x00, 0x00, NULL
}, &Result
[oldlen
], MaxResultLen
- oldlen
, ResultLen
, sw
, NULL
);
65 if (res
== 5) // apdu result (sw) not a 0x9000
69 if (*ResultLen
> MaxResultLen
)
75 int FIDORegister(uint8_t *params
, uint8_t *Result
, size_t MaxResultLen
, size_t *ResultLen
, uint16_t *sw
) {
76 return FIDOExchange((sAPDU
){0x00, 0x01, 0x03, 0x00, 64, params
}, Result
, MaxResultLen
, ResultLen
, sw
);
79 int FIDOAuthentication(uint8_t *params
, uint8_t paramslen
, uint8_t controlb
, uint8_t *Result
, size_t MaxResultLen
, size_t *ResultLen
, uint16_t *sw
) {
80 return FIDOExchange((sAPDU
){0x00, 0x02, controlb
, 0x00, paramslen
, params
}, Result
, MaxResultLen
, ResultLen
, sw
);
83 int FIDO2GetInfo(uint8_t *Result
, size_t MaxResultLen
, size_t *ResultLen
, uint16_t *sw
) {
84 uint8_t data
[] = {0x04};
85 return FIDOExchange((sAPDU
){0x80, 0x10, 0x00, 0x00, sizeof(data
), data
}, Result
, MaxResultLen
, ResultLen
, sw
);
88 int CmdHFFidoInfo(const char *cmd
) {
90 if (cmd
&& strlen(cmd
) > 0)
91 PrintAndLog("WARNING: command don't have any parameters.\n");
93 // info about 14a part
97 PrintAndLog("--------------------------------------------");
98 SetAPDULogging(false);
100 uint8_t buf
[APDU_RES_LEN
] = {0};
103 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
112 PrintAndLog("Not a FIDO card! APDU response: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
114 PrintAndLog("APDU exchange error. Card returns 0x0000.");
120 if (!strncmp((char *)buf
, "U2F_V2", 7)) {
121 if (!strncmp((char *)buf
, "FIDO_2_0", 8)) {
122 PrintAndLog("FIDO2 authenricator detected. Version: %.*s", len
, buf
);
124 PrintAndLog("FIDO authenricator detected (not standard U2F).");
125 PrintAndLog("Non U2F authenticator version:");
126 dump_buffer((const unsigned char *)buf
, len
, NULL
, 0);
129 PrintAndLog("FIDO U2F authenricator detected. Version: %.*s", len
, buf
);
132 res
= FIDO2GetInfo(buf
, sizeof(buf
), &len
, &sw
);
138 PrintAndLog("FIDO2 version not exists (%04x - %s).", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
143 PrintAndLog("FIDO2 version: (%d)", len
);
144 dump_buffer((const unsigned char *)buf
, len
, NULL
, 0);
149 json_t
*OpenJson(int paramnum
, char *fname
, void* argtable
[], bool *err
) {
154 uint8_t jsonname
[250] ={0};
155 char *cjsonname
= (char *)jsonname
;
158 // CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
159 if (CLIParamStrToBuf(arg_get_str(paramnum
), jsonname
, sizeof(jsonname
), &jsonnamelen
)) {
164 // current path + file name
165 if (!strstr(cjsonname
, ".json"))
166 strcat(cjsonname
, ".json");
169 strcpy(fname
, get_my_executable_directory());
170 strcat(fname
, cjsonname
);
171 if (access(fname
, F_OK
) != -1) {
172 root
= json_load_file(fname
, 0, &error
);
174 PrintAndLog("ERROR: json error on line %d: %s", error
.line
, error
.text
);
179 if (!json_is_object(root
)) {
180 PrintAndLog("ERROR: Invalid json format. root must be an object.");
187 root
= json_object();
193 int CmdHFFidoRegister(const char *cmd
) {
194 uint8_t data
[64] = {0};
196 uint8_t cdata
[250] = {0};
198 uint8_t adata
[250] = {0};
201 CLIParserInit("hf fido reg",
202 "Initiate a U2F token registration. Needs two 32-byte hash number. \nchallenge parameter (32b) and application parameter (32b).",
203 "Usage:\n\thf fido reg -> execute command with 2 parameters, filled 0x00\n"
204 "\thf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"
205 "\thf fido reg -p s0 s1 -> execute command with plain parameters");
209 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
210 arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
211 arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
212 arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"),
213 arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
214 arg_str0(NULL
, NULL
, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL
),
215 arg_str0(NULL
, NULL
, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL
),
218 CLIExecWithReturn(cmd
, argtable
, true);
220 bool APDULogging
= arg_get_lit(1);
221 bool verbose
= arg_get_lit(2);
222 bool verbose2
= arg_get_lit(2) > 1;
223 bool paramsPlain
= arg_get_lit(3);
224 bool showDERTLV
= arg_get_lit(4);
226 char fname
[250] = {0};
228 root
= OpenJson(5, fname
, argtable
, &err
);
233 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
234 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
238 memset(cdata
, 0x00, 32);
239 CLIGetStrWithReturn(6, cdata
, &chlen
);
240 if (chlen
&& chlen
> 16) {
241 PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen
);
245 CLIGetHexWithReturn(6, cdata
, &chlen
);
246 if (chlen
&& chlen
!= 32) {
247 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
252 memmove(data
, cdata
, 32);
256 memset(adata
, 0x00, 32);
257 CLIGetStrWithReturn(7, adata
, &applen
);
258 if (applen
&& applen
> 16) {
259 PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen
);
263 CLIGetHexWithReturn(7, adata
, &applen
);
264 if (applen
&& applen
!= 32) {
265 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
270 memmove(&data
[32], adata
, 32);
274 SetAPDULogging(APDULogging
);
276 // 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
277 // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
279 uint8_t buf
[2048] = {0};
284 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
287 PrintAndLog("Can't select authenticator. res=%x. Exit...", res
);
293 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
298 res
= FIDORegister(data
, buf
, sizeof(buf
), &len
, &sw
);
301 PrintAndLog("Can't execute register command. res=%x. Exit...", res
);
306 PrintAndLog("ERROR execute register command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
312 PrintAndLog("---------------------------------------------------------------");
313 PrintAndLog("data len: %d", len
);
315 PrintAndLog("--------------data----------------------");
316 dump_buffer((const unsigned char *)buf
, len
, NULL
, 0);
317 PrintAndLog("--------------data----------------------");
320 if (buf
[0] != 0x05) {
321 PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf
[0]);
324 PrintAndLog("User public key: %s", sprint_hex(&buf
[1], 65));
326 uint8_t keyHandleLen
= buf
[66];
327 PrintAndLog("Key handle[%d]: %s", keyHandleLen
, sprint_hex(&buf
[67], keyHandleLen
));
329 int derp
= 67 + keyHandleLen
;
330 int derLen
= (buf
[derp
+ 2] << 8) + buf
[derp
+ 3] + 4;
332 PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen
);
333 dump_buffer_simple((const unsigned char *)&buf
[derp
], derLen
, NULL
);
334 PrintAndLog("\n----------------DER---------------------");
337 PrintAndLog("------------------DER-------------------");
338 PrintAndLog("DER certificate[%d]: %s...", derLen
, sprint_hex(&buf
[derp
], 20));
341 // check and print DER certificate
342 uint8_t public_key
[65] = {0};
344 // print DER certificate in TLV view
346 PrintAndLog("----------------DER TLV-----------------");
347 asn1_print(&buf
[derp
], derLen
, " ");
348 PrintAndLog("----------------DER TLV-----------------");
352 mbedtls_x509_crt cacert
;
353 mbedtls_x509_crt_init(&cacert
);
354 res
= mbedtls_x509_crt_parse(&cacert
, (const unsigned char *) additional_ca_pem
, additional_ca_pem_len
);
356 PrintAndLog("ERROR: CA parse certificate returned -0x%x - %s", -res
, ecdsa_get_error(res
));
359 PrintAndLog("CA load OK. %d skipped", res
);
361 // load DER certificate from authenticator's data
362 mbedtls_x509_crt cert
;
363 mbedtls_x509_crt_init(&cert
);
364 res
= mbedtls_x509_crt_parse_der(&cert
, &buf
[derp
], derLen
);
366 PrintAndLog("ERROR: DER parse returned 0x%x - %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
369 // get certificate info
370 char linfo
[300] = {0};
372 mbedtls_x509_crt_info(linfo
, sizeof(linfo
), " ", &cert
);
373 PrintAndLog("DER certificate info:\n%s", linfo
);
376 // verify certificate
377 uint32_t verifyflags
= 0;
378 res
= mbedtls_x509_crt_verify(&cert
, &cacert
, NULL
, NULL
, &verifyflags
, NULL
, NULL
);
380 PrintAndLog("ERROR: DER verify returned 0x%x - %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
382 PrintAndLog("Certificate OK.");
386 memset(linfo
, 0x00, sizeof(linfo
));
387 mbedtls_x509_crt_verify_info(linfo
, sizeof(linfo
), " ", verifyflags
);
388 PrintAndLog("Verification info:\n%s", linfo
);
392 res
= ecdsa_public_key_from_pk(&cert
.pk
, public_key
, sizeof(public_key
));
394 PrintAndLog("ERROR: getting public key from certificate 0x%x - %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
397 PrintAndLog("Got a public key from certificate:\n%s", sprint_hex_inrow(public_key
, 65));
401 PrintAndLog("------------------DER-------------------");
403 mbedtls_x509_crt_free(&cert
);
404 mbedtls_x509_crt_free(&cacert
);
407 int hashp
= 1 + 65 + 1 + keyHandleLen
+ derLen
;
408 PrintAndLog("Hash[%d]: %s", len
- hashp
, sprint_hex(&buf
[hashp
], len
- hashp
));
410 // check ANSI X9.62 format ECDSA signature (on P-256)
411 uint8_t rval
[300] = {0};
412 uint8_t sval
[300] = {0};
413 res
= ecdsa_asn1_get_signature(&buf
[hashp
], len
- hashp
, rval
, sval
);
416 PrintAndLog(" r: %s", sprint_hex(rval
, 32));
417 PrintAndLog(" s: %s", sprint_hex(sval
, 32));
420 uint8_t xbuf
[4096] = {0};
422 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
424 &data
[32], 32, // application parameter
425 &data
[0], 32, // challenge parameter
426 &buf
[67], keyHandleLen
, // keyHandle
427 &buf
[1], 65, // user public key
429 //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
430 res
= ecdsa_signature_verify(public_key
, xbuf
, xbuflen
, &buf
[hashp
], len
- hashp
);
432 if (res
== -0x4e00) {
433 PrintAndLog("Signature is NOT VALID.");
435 PrintAndLog("Other signature check error: %x %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
438 PrintAndLog("Signature is OK.");
442 PrintAndLog("Invalid signature. res=%d.", res
);
445 PrintAndLog("\nauth command: ");
446 printf("hf fido auth %s%s", paramsPlain
?"-p ":"", sprint_hex_inrow(&buf
[67], keyHandleLen
));
448 printf(" %s", paramsPlain
?(char *)cdata
:sprint_hex_inrow(cdata
, 32));
450 printf(" %s", paramsPlain
?(char *)adata
:sprint_hex_inrow(adata
, 32));
454 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
455 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
456 JsonSaveBufAsHexCompact(root
, "PublicKey", &buf
[1], 65);
457 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
458 JsonSaveBufAsHexCompact(root
, "KeyHandle", &buf
[67], keyHandleLen
);
459 JsonSaveBufAsHexCompact(root
, "DER", &buf
[67 + keyHandleLen
], derLen
);
461 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
463 PrintAndLog("ERROR: can't save the file: %s", fname
);
466 PrintAndLog("File `%s` saved.", fname
);
475 int CmdHFFidoAuthenticate(const char *cmd
) {
476 uint8_t data
[512] = {0};
477 uint8_t hdata
[250] = {0};
478 bool public_key_loaded
= false;
479 uint8_t public_key
[65] = {0};
481 uint8_t keyHandleLen
= 0;
484 CLIParserInit("hf fido auth",
485 "Initiate a U2F token authentication. Needs key handle and two 32-byte hash number. \nkey handle(var 0..255), challenge parameter (32b) and application parameter (32b).",
486 "Usage:\n\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
487 "\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
488 "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
492 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
493 arg_lit0("vV", "verbose", "show technical data"),
494 arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
495 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
496 arg_lit0("uU", "user", "mode: enforce-user-presence-and-sign"),
497 arg_lit0("cC", "check", "mode: check-only"),
498 arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
499 arg_str0("kK", "key", "public key to verify signature", NULL
),
500 arg_str0(NULL
, NULL
, "<HEX key handle (var 0..255b)>", NULL
),
501 arg_str0(NULL
, NULL
, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL
),
502 arg_str0(NULL
, NULL
, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL
),
505 CLIExecWithReturn(cmd
, argtable
, true);
507 bool APDULogging
= arg_get_lit(1);
508 bool verbose
= arg_get_lit(2);
509 bool paramsPlain
= arg_get_lit(3);
510 uint8_t controlByte
= 0x08;
516 char fname
[250] = {0};
518 root
= OpenJson(7, fname
, argtable
, &err
);
523 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
524 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
525 JsonLoadBufAsHex(root
, "$.KeyHandle", &data
[65], 512 - 67, &jlen
);
526 keyHandleLen
= jlen
& 0xff;
527 data
[64] = keyHandleLen
;
528 JsonLoadBufAsHex(root
, "$.PublicKey", public_key
, 65, &jlen
);
529 public_key_loaded
= (jlen
> 0);
533 CLIGetHexWithReturn(8, hdata
, &hdatalen
);
534 if (hdatalen
&& hdatalen
!= 65) {
535 PrintAndLog("ERROR: public key length must be 65 bytes only.");
539 memmove(public_key
, hdata
, hdatalen
);
540 public_key_loaded
= true;
543 CLIGetHexWithReturn(9, hdata
, &hdatalen
);
544 if (hdatalen
> 255) {
545 PrintAndLog("ERROR: application parameter length must be less than 255.");
549 keyHandleLen
= hdatalen
;
550 data
[64] = keyHandleLen
;
551 memmove(&data
[65], hdata
, keyHandleLen
);
555 memset(hdata
, 0x00, 32);
556 CLIGetStrWithReturn(9, hdata
, &hdatalen
);
557 if (hdatalen
&& hdatalen
> 16) {
558 PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
562 CLIGetHexWithReturn(10, hdata
, &hdatalen
);
563 if (hdatalen
&& hdatalen
!= 32) {
564 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
569 memmove(data
, hdata
, 32);
572 memset(hdata
, 0x00, 32);
573 CLIGetStrWithReturn(11, hdata
, &hdatalen
);
574 if (hdatalen
&& hdatalen
> 16) {
575 PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
579 CLIGetHexWithReturn(10, hdata
, &hdatalen
);
580 if (hdatalen
&& hdatalen
!= 32) {
581 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
586 memmove(&data
[32], hdata
, 32);
590 SetAPDULogging(APDULogging
);
592 // (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
593 // challenge parameter [32 bytes]
594 // application parameter [32 bytes]
595 // key handle length [1b] = N
598 uint8_t datalen
= 32 + 32 + 1 + keyHandleLen
;
600 uint8_t buf
[2048] = {0};
605 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
608 PrintAndLog("Can't select authenticator. res=%x. Exit...", res
);
614 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
619 res
= FIDOAuthentication(data
, datalen
, controlByte
, buf
, sizeof(buf
), &len
, &sw
);
622 PrintAndLog("Can't execute authentication command. res=%x. Exit...", res
);
627 PrintAndLog("ERROR execute authentication command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
631 PrintAndLog("---------------------------------------------------------------");
632 PrintAndLog("User presence: %s", (buf
[0]?"verified":"not verified"));
633 uint32_t cntr
= (uint32_t)bytes_to_num(&buf
[1], 4);
634 PrintAndLog("Counter: %d", cntr
);
635 PrintAndLog("Hash[%d]: %s", len
- 5, sprint_hex(&buf
[5], len
- 5));
637 // check ANSI X9.62 format ECDSA signature (on P-256)
638 uint8_t rval
[300] = {0};
639 uint8_t sval
[300] = {0};
640 res
= ecdsa_asn1_get_signature(&buf
[5], len
- 5, rval
, sval
);
643 PrintAndLog(" r: %s", sprint_hex(rval
, 32));
644 PrintAndLog(" s: %s", sprint_hex(sval
, 32));
646 if (public_key_loaded
) {
647 uint8_t xbuf
[4096] = {0};
649 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
650 &data
[32], 32, // application parameter
651 &buf
[0], 1, // user presence
652 &buf
[1], 4, // counter
653 data
, 32, // challenge parameter
655 //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
656 res
= ecdsa_signature_verify(public_key
, xbuf
, xbuflen
, &buf
[5], len
- 5);
658 if (res
== -0x4e00) {
659 PrintAndLog("Signature is NOT VALID.");
661 PrintAndLog("Other signature check error: %x %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
664 PrintAndLog("Signature is OK.");
667 PrintAndLog("No public key provided. can't check signature.");
670 PrintAndLog("Invalid signature. res=%d.", res
);
674 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
675 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
676 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
677 JsonSaveBufAsHexCompact(root
, "KeyHandle", &data
[65], keyHandleLen
);
678 JsonSaveInt(root
, "Counter", cntr
);
680 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
682 PrintAndLog("ERROR: can't save the file: %s", fname
);
685 PrintAndLog("File `%s` saved.", fname
);
693 static command_t CommandTable
[] =
695 {"help", CmdHelp
, 1, "This help."},
696 {"info", CmdHFFidoInfo
, 0, "Info about FIDO tag."},
697 {"reg", CmdHFFidoRegister
, 0, "FIDO U2F Registration Message."},
698 {"auth", CmdHFFidoAuthenticate
, 0, "FIDO U2F Authentication Message."},
699 {NULL
, NULL
, 0, NULL
}
702 int CmdHFFido(const char *Cmd
) {
703 (void)WaitForResponseTimeout(CMD_ACK
,NULL
,100);
704 CmdsParse(CommandTable
, Cmd
);
708 int CmdHelp(const char *Cmd
) {
709 CmdsHelp(CommandTable
);