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"
31 #include <mbedtls/x509_crt.h>
32 #include <mbedtls/x509.h>
33 #include <mbedtls/pk.h>
38 #include "proxmark3.h"
40 #include "emv/emvcore.h"
41 #include "emv/emvjson.h"
43 #include "emv/apduinfo.h"
44 #include "cliparser/cliparser.h"
45 #include "crypto/asn1utils.h"
46 #include "crypto/libpcrypto.h"
47 #include "fido/cbortools.h"
48 #include "fido/fidocore.h"
49 #include "fido/cose.h"
51 static int CmdHelp(const char *Cmd
);
53 int CmdHFFidoInfo(const char *cmd
) {
55 if (cmd
&& strlen(cmd
) > 0)
56 PrintAndLog("WARNING: command don't have any parameters.\n");
58 // info about 14a part
62 PrintAndLog("--------------------------------------------");
63 SetAPDULogging(false);
65 uint8_t buf
[APDU_RESPONSE_LEN
] = {0};
68 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
77 PrintAndLog("Not a FIDO card! APDU response: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
79 PrintAndLog("APDU exchange error. Card returns 0x0000.");
85 if (!strncmp((char *)buf
, "U2F_V2", 7)) {
86 if (!strncmp((char *)buf
, "FIDO_2_0", 8)) {
87 PrintAndLog("FIDO2 authenricator detected. Version: %.*s", len
, buf
);
89 PrintAndLog("FIDO authenricator detected (not standard U2F).");
90 PrintAndLog("Non U2F authenticator version:");
91 dump_buffer((const unsigned char *)buf
, len
, NULL
, 0);
94 PrintAndLog("FIDO U2F authenricator detected. Version: %.*s", len
, buf
);
97 res
= FIDO2GetInfo(buf
, sizeof(buf
), &len
, &sw
);
103 PrintAndLog("FIDO2 version not exists (%04x - %s).", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
109 PrintAndLog("FIDO2 ger version error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
115 // PrintAndLog("FIDO2 version: (len=%d)", len);
116 // dump_buffer((const unsigned char *)buf, len, NULL, 0);
119 PrintAndLog("FIDO2 version CBOR decoded:");
120 TinyCborPrintFIDOPackage(fido2CmdGetInfo
, true, &buf
[1], len
- 1);
122 PrintAndLog("FIDO2 version length error");
128 json_t
*OpenJson(int paramnum
, char *fname
, void* argtable
[], bool *err
) {
133 uint8_t jsonname
[250] ={0};
134 char *cjsonname
= (char *)jsonname
;
137 // CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
138 if (CLIParamStrToBuf(arg_get_str(paramnum
), jsonname
, sizeof(jsonname
), &jsonnamelen
)) {
143 // current path + file name
144 if (!strstr(cjsonname
, ".json"))
145 strcat(cjsonname
, ".json");
148 strcpy(fname
, get_my_executable_directory());
149 strcat(fname
, cjsonname
);
150 if (access(fname
, F_OK
) != -1) {
151 root
= json_load_file(fname
, 0, &error
);
153 PrintAndLog("ERROR: json error on line %d: %s", error
.line
, error
.text
);
158 if (!json_is_object(root
)) {
159 PrintAndLog("ERROR: Invalid json format. root must be an object.");
166 root
= json_object();
172 int CmdHFFidoRegister(const char *cmd
) {
173 uint8_t data
[64] = {0};
175 uint8_t cdata
[250] = {0};
177 uint8_t adata
[250] = {0};
180 CLIParserInit("hf fido reg",
181 "Initiate a U2F token registration. Needs two 32-byte hash number. \nchallenge parameter (32b) and application parameter (32b).",
182 "Usage:\n\thf fido reg -> execute command with 2 parameters, filled 0x00\n"
183 "\thf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"
184 "\thf fido reg -p s0 s1 -> execute command with plain parameters");
188 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
189 arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
190 arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
191 arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"),
192 arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
193 arg_str0(NULL
, NULL
, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL
),
194 arg_str0(NULL
, NULL
, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL
),
197 CLIExecWithReturn(cmd
, argtable
, true);
199 bool APDULogging
= arg_get_lit(1);
200 bool verbose
= arg_get_lit(2);
201 bool verbose2
= arg_get_lit(2) > 1;
202 bool paramsPlain
= arg_get_lit(3);
203 bool showDERTLV
= arg_get_lit(4);
205 char fname
[250] = {0};
207 root
= OpenJson(5, fname
, argtable
, &err
);
212 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
213 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
217 memset(cdata
, 0x00, 32);
218 CLIGetStrWithReturn(6, cdata
, &chlen
);
219 if (chlen
&& chlen
> 16) {
220 PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen
);
224 CLIGetHexWithReturn(6, cdata
, &chlen
);
225 if (chlen
&& chlen
!= 32) {
226 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
231 memmove(data
, cdata
, 32);
235 memset(adata
, 0x00, 32);
236 CLIGetStrWithReturn(7, adata
, &applen
);
237 if (applen
&& applen
> 16) {
238 PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen
);
242 CLIGetHexWithReturn(7, adata
, &applen
);
243 if (applen
&& applen
!= 32) {
244 PrintAndLog("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 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
266 PrintAndLog("Can't select authenticator. res=%x. Exit...", res
);
272 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
277 res
= FIDORegister(data
, buf
, sizeof(buf
), &len
, &sw
);
280 PrintAndLog("Can't execute register command. res=%x. Exit...", res
);
285 PrintAndLog("ERROR execute register command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
291 PrintAndLog("---------------------------------------------------------------");
292 PrintAndLog("data len: %d", len
);
294 PrintAndLog("--------------data----------------------");
295 dump_buffer((const unsigned char *)buf
, len
, NULL
, 0);
296 PrintAndLog("--------------data----------------------");
299 if (buf
[0] != 0x05) {
300 PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf
[0]);
303 PrintAndLog("User public key: %s", sprint_hex(&buf
[1], 65));
305 uint8_t keyHandleLen
= buf
[66];
306 PrintAndLog("Key handle[%d]: %s", keyHandleLen
, sprint_hex(&buf
[67], keyHandleLen
));
308 int derp
= 67 + keyHandleLen
;
309 int derLen
= (buf
[derp
+ 2] << 8) + buf
[derp
+ 3] + 4;
311 PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen
);
312 dump_buffer_simple((const unsigned char *)&buf
[derp
], derLen
, NULL
);
313 PrintAndLog("\n----------------DER---------------------");
316 PrintAndLog("------------------DER-------------------");
317 PrintAndLog("DER certificate[%d]: %s...", derLen
, sprint_hex(&buf
[derp
], 20));
320 // check and print DER certificate
321 uint8_t public_key
[65] = {0};
323 // print DER certificate in TLV view
325 PrintAndLog("----------------DER TLV-----------------");
326 asn1_print(&buf
[derp
], derLen
, " ");
327 PrintAndLog("----------------DER TLV-----------------");
330 FIDOCheckDERAndGetKey(&buf
[derp
], derLen
, verbose
, public_key
, sizeof(public_key
));
333 int hashp
= 1 + 65 + 1 + keyHandleLen
+ derLen
;
334 PrintAndLog("Hash[%d]: %s", len
- hashp
, sprint_hex(&buf
[hashp
], len
- hashp
));
336 // check ANSI X9.62 format ECDSA signature (on P-256)
337 uint8_t rval
[300] = {0};
338 uint8_t sval
[300] = {0};
339 res
= ecdsa_asn1_get_signature(&buf
[hashp
], len
- hashp
, rval
, sval
);
342 PrintAndLog(" r: %s", sprint_hex(rval
, 32));
343 PrintAndLog(" s: %s", sprint_hex(sval
, 32));
346 uint8_t xbuf
[4096] = {0};
348 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
350 &data
[32], 32, // application parameter
351 &data
[0], 32, // challenge parameter
352 &buf
[67], keyHandleLen
, // keyHandle
353 &buf
[1], 65, // user public key
355 //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
356 res
= ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1
, public_key
, xbuf
, xbuflen
, &buf
[hashp
], len
- hashp
, true);
358 if (res
== MBEDTLS_ERR_ECP_VERIFY_FAILED
) {
359 PrintAndLog("Signature is NOT VALID.");
361 PrintAndLog("Other signature check error: %x %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
364 PrintAndLog("Signature is OK.");
368 PrintAndLog("Invalid signature. res=%d.", res
);
371 PrintAndLog("\nauth command: ");
372 printf("hf fido auth %s%s", paramsPlain
?"-p ":"", sprint_hex_inrow(&buf
[67], keyHandleLen
));
374 printf(" %s", paramsPlain
?(char *)cdata
:sprint_hex_inrow(cdata
, 32));
376 printf(" %s", paramsPlain
?(char *)adata
:sprint_hex_inrow(adata
, 32));
380 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
381 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
382 JsonSaveBufAsHexCompact(root
, "PublicKey", &buf
[1], 65);
383 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
384 JsonSaveBufAsHexCompact(root
, "KeyHandle", &buf
[67], keyHandleLen
);
385 JsonSaveBufAsHexCompact(root
, "DER", &buf
[67 + keyHandleLen
], derLen
);
387 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
389 PrintAndLog("ERROR: can't save the file: %s", fname
);
392 PrintAndLog("File `%s` saved.", fname
);
401 int CmdHFFidoAuthenticate(const char *cmd
) {
402 uint8_t data
[512] = {0};
403 uint8_t hdata
[250] = {0};
404 bool public_key_loaded
= false;
405 uint8_t public_key
[65] = {0};
407 uint8_t keyHandleLen
= 0;
410 CLIParserInit("hf fido auth",
411 "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).",
412 "Usage:\n\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
413 "\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
414 "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
418 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
419 arg_lit0("vV", "verbose", "show technical data"),
420 arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
421 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
422 arg_lit0("uU", "user", "mode: enforce-user-presence-and-sign"),
423 arg_lit0("cC", "check", "mode: check-only"),
424 arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
425 arg_str0("kK", "key", "public key to verify signature", NULL
),
426 arg_str0(NULL
, NULL
, "<HEX key handle (var 0..255b)>", NULL
),
427 arg_str0(NULL
, NULL
, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL
),
428 arg_str0(NULL
, NULL
, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL
),
431 CLIExecWithReturn(cmd
, argtable
, true);
433 bool APDULogging
= arg_get_lit(1);
434 bool verbose
= arg_get_lit(2);
435 bool paramsPlain
= arg_get_lit(3);
436 uint8_t controlByte
= 0x08;
442 char fname
[250] = {0};
444 root
= OpenJson(7, fname
, argtable
, &err
);
449 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
450 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
451 JsonLoadBufAsHex(root
, "$.KeyHandle", &data
[65], 512 - 67, &jlen
);
452 keyHandleLen
= jlen
& 0xff;
453 data
[64] = keyHandleLen
;
454 JsonLoadBufAsHex(root
, "$.PublicKey", public_key
, 65, &jlen
);
455 public_key_loaded
= (jlen
> 0);
459 CLIGetHexWithReturn(8, hdata
, &hdatalen
);
460 if (hdatalen
&& hdatalen
!= 65) {
461 PrintAndLog("ERROR: public key length must be 65 bytes only.");
465 memmove(public_key
, hdata
, hdatalen
);
466 public_key_loaded
= true;
469 CLIGetHexWithReturn(9, hdata
, &hdatalen
);
470 if (hdatalen
> 255) {
471 PrintAndLog("ERROR: application parameter length must be less than 255.");
475 keyHandleLen
= hdatalen
;
476 data
[64] = keyHandleLen
;
477 memmove(&data
[65], hdata
, keyHandleLen
);
481 memset(hdata
, 0x00, 32);
482 CLIGetStrWithReturn(9, hdata
, &hdatalen
);
483 if (hdatalen
&& hdatalen
> 16) {
484 PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
488 CLIGetHexWithReturn(10, hdata
, &hdatalen
);
489 if (hdatalen
&& hdatalen
!= 32) {
490 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
495 memmove(data
, hdata
, 32);
498 memset(hdata
, 0x00, 32);
499 CLIGetStrWithReturn(11, hdata
, &hdatalen
);
500 if (hdatalen
&& hdatalen
> 16) {
501 PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen
);
505 CLIGetHexWithReturn(10, hdata
, &hdatalen
);
506 if (hdatalen
&& hdatalen
!= 32) {
507 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
512 memmove(&data
[32], hdata
, 32);
516 SetAPDULogging(APDULogging
);
518 // (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
519 // challenge parameter [32 bytes]
520 // application parameter [32 bytes]
521 // key handle length [1b] = N
524 uint8_t datalen
= 32 + 32 + 1 + keyHandleLen
;
526 uint8_t buf
[2048] = {0};
531 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
534 PrintAndLog("Can't select authenticator. res=%x. Exit...", res
);
540 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
545 res
= FIDOAuthentication(data
, datalen
, controlByte
, buf
, sizeof(buf
), &len
, &sw
);
548 PrintAndLog("Can't execute authentication command. res=%x. Exit...", res
);
553 PrintAndLog("ERROR execute authentication command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
557 PrintAndLog("---------------------------------------------------------------");
558 PrintAndLog("User presence: %s", (buf
[0]?"verified":"not verified"));
559 uint32_t cntr
= (uint32_t)bytes_to_num(&buf
[1], 4);
560 PrintAndLog("Counter: %d", cntr
);
561 PrintAndLog("Hash[%d]: %s", len
- 5, sprint_hex(&buf
[5], len
- 5));
563 // check ANSI X9.62 format ECDSA signature (on P-256)
564 uint8_t rval
[300] = {0};
565 uint8_t sval
[300] = {0};
566 res
= ecdsa_asn1_get_signature(&buf
[5], len
- 5, rval
, sval
);
569 PrintAndLog(" r: %s", sprint_hex(rval
, 32));
570 PrintAndLog(" s: %s", sprint_hex(sval
, 32));
572 if (public_key_loaded
) {
573 uint8_t xbuf
[4096] = {0};
575 res
= FillBuffer(xbuf
, sizeof(xbuf
), &xbuflen
,
576 &data
[32], 32, // application parameter
577 &buf
[0], 1, // user presence
578 &buf
[1], 4, // counter
579 data
, 32, // challenge parameter
581 //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
582 res
= ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1
, public_key
, xbuf
, xbuflen
, &buf
[5], len
- 5, true);
584 if (res
== MBEDTLS_ERR_ECP_VERIFY_FAILED
) {
585 PrintAndLog("Signature is NOT VALID.");
587 PrintAndLog("Other signature check error: %x %s", (res
<0)?-res
:res
, ecdsa_get_error(res
));
590 PrintAndLog("Signature is OK.");
593 PrintAndLog("No public key provided. can't check signature.");
596 PrintAndLog("Invalid signature. res=%d.", res
);
600 JsonSaveBufAsHex(root
, "ChallengeParam", data
, 32);
601 JsonSaveBufAsHex(root
, "ApplicationParam", &data
[32], 32);
602 JsonSaveInt(root
, "KeyHandleLen", keyHandleLen
);
603 JsonSaveBufAsHexCompact(root
, "KeyHandle", &data
[65], keyHandleLen
);
604 JsonSaveInt(root
, "Counter", cntr
);
606 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
608 PrintAndLog("ERROR: can't save the file: %s", fname
);
611 PrintAndLog("File `%s` saved.", fname
);
619 void CheckSlash(char *fileName
) {
620 if ((fileName
[strlen(fileName
) - 1] != '/') &&
621 (fileName
[strlen(fileName
) - 1] != '\\'))
622 strcat(fileName
, "/");
625 int GetExistsFileNameJson(char *prefixDir
, char *reqestedFileName
, char *fileName
) {
627 strcpy(fileName
, get_my_executable_directory());
628 CheckSlash(fileName
);
630 strcat(fileName
, prefixDir
);
631 CheckSlash(fileName
);
633 strcat(fileName
, reqestedFileName
);
634 if (!strstr(fileName
, ".json"))
635 strcat(fileName
, ".json");
637 if (access(fileName
, F_OK
) < 0) {
638 strcpy(fileName
, get_my_executable_directory());
639 CheckSlash(fileName
);
641 strcat(fileName
, reqestedFileName
);
642 if (!strstr(fileName
, ".json"))
643 strcat(fileName
, ".json");
645 if (access(fileName
, F_OK
) < 0) {
646 return 1; // file not found
652 int CmdHFFido2MakeCredential(const char *cmd
) {
655 char fname
[300] = {0};
657 CLIParserInit("hf fido make",
658 "Execute a FIDO2 Make Credentional command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.",
659 "Usage:\n\thf fido make -> execute command default parameters file `fido2.json`\n"
660 "\thf fido make test.json -> execute command with parameters file `text.json`");
664 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
665 arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
666 arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"),
667 arg_lit0("cC", "cbor", "show CBOR decoded data"),
668 arg_str0(NULL
, NULL
, "<json file name>", "JSON input / output file name for parameters. Default `fido2.json`"),
671 CLIExecWithReturn(cmd
, argtable
, true);
673 bool APDULogging
= arg_get_lit(1);
674 bool verbose
= arg_get_lit(2);
675 bool verbose2
= arg_get_lit(2) > 1;
676 bool showDERTLV
= arg_get_lit(3);
677 bool showCBOR
= arg_get_lit(4);
679 uint8_t jsonname
[250] ={0};
680 char *cjsonname
= (char *)jsonname
;
682 CLIGetStrWithReturn(5, jsonname
, &jsonnamelen
);
685 strcat(cjsonname
, "fido2");
686 jsonnamelen
= strlen(cjsonname
);
691 SetAPDULogging(APDULogging
);
693 int res
= GetExistsFileNameJson("fido", cjsonname
, fname
);
695 PrintAndLog("ERROR: Can't found the json file.");
698 PrintAndLog("fname: %s\n", fname
);
699 root
= json_load_file(fname
, 0, &error
);
701 PrintAndLog("ERROR: json error on line %d: %s", error
.line
, error
.text
);
705 uint8_t data
[2048] = {0};
707 uint8_t buf
[2048] = {0};
712 res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
715 PrintAndLog("Can't select authenticator. res=%x. Exit...", res
);
721 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
726 res
= FIDO2CreateMakeCredentionalReq(root
, data
, sizeof(data
), &datalen
);
731 PrintAndLog("CBOR make credentional request:");
732 PrintAndLog("---------------- CBOR ------------------");
733 TinyCborPrintFIDOPackage(fido2CmdMakeCredential
, false, data
, datalen
);
734 PrintAndLog("---------------- CBOR ------------------");
737 res
= FIDO2MakeCredential(data
, datalen
, buf
, sizeof(buf
), &len
, &sw
);
740 PrintAndLog("Can't execute make credential command. res=%x. Exit...", res
);
745 PrintAndLog("ERROR execute make credential command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
750 PrintAndLog("FIDO2 make credential error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
754 PrintAndLog("MakeCredential result (%d b) OK.", len
);
756 PrintAndLog("CBOR make credentional response:");
757 PrintAndLog("---------------- CBOR ------------------");
758 TinyCborPrintFIDOPackage(fido2CmdMakeCredential
, true, &buf
[1], len
- 1);
759 PrintAndLog("---------------- CBOR ------------------");
762 // parse returned cbor
763 FIDO2MakeCredentionalParseRes(root
, &buf
[1], len
- 1, verbose
, verbose2
, showCBOR
, showDERTLV
);
766 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
768 PrintAndLog("ERROR: can't save the file: %s", fname
);
771 PrintAndLog("File `%s` saved.", fname
);
779 int CmdHFFido2GetAssertion(const char *cmd
) {
782 char fname
[300] = {0};
784 CLIParserInit("hf fido assert",
785 "Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.",
786 "Usage:\n\thf fido assert -> execute command default parameters file `fido2.json`\n"
787 "\thf fido assert test.json -l -> execute command with parameters file `text.json` and add to request CredentialId");
791 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
792 arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
793 arg_lit0("cC", "cbor", "show CBOR decoded data"),
794 arg_lit0("lL", "list", "add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator don't store credential to its memory)"),
795 arg_str0(NULL
, NULL
, "<json file name>", "JSON input / output file name for parameters. Default `fido2.json`"),
798 CLIExecWithReturn(cmd
, argtable
, true);
800 bool APDULogging
= arg_get_lit(1);
801 bool verbose
= arg_get_lit(2);
802 bool verbose2
= arg_get_lit(2) > 1;
803 bool showCBOR
= arg_get_lit(3);
804 bool createAllowList
= arg_get_lit(4);
806 uint8_t jsonname
[250] ={0};
807 char *cjsonname
= (char *)jsonname
;
809 CLIGetStrWithReturn(5, jsonname
, &jsonnamelen
);
812 strcat(cjsonname
, "fido2");
813 jsonnamelen
= strlen(cjsonname
);
818 SetAPDULogging(APDULogging
);
820 int res
= GetExistsFileNameJson("fido", "fido2", fname
);
822 PrintAndLog("ERROR: Can't found the json file.");
825 PrintAndLog("fname: %s\n", fname
);
826 root
= json_load_file(fname
, 0, &error
);
828 PrintAndLog("ERROR: json error on line %d: %s", error
.line
, error
.text
);
832 uint8_t data
[2048] = {0};
834 uint8_t buf
[2048] = {0};
839 res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
842 PrintAndLog("Can't select authenticator. res=%x. Exit...", res
);
848 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
853 res
= FIDO2CreateGetAssertionReq(root
, data
, sizeof(data
), &datalen
, createAllowList
);
858 PrintAndLog("CBOR get assertion request:");
859 PrintAndLog("---------------- CBOR ------------------");
860 TinyCborPrintFIDOPackage(fido2CmdGetAssertion
, false, data
, datalen
);
861 PrintAndLog("---------------- CBOR ------------------");
864 res
= FIDO2GetAssertion(data
, datalen
, buf
, sizeof(buf
), &len
, &sw
);
867 PrintAndLog("Can't execute get assertion command. res=%x. Exit...", res
);
872 PrintAndLog("ERROR execute get assertion command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
877 PrintAndLog("FIDO2 get assertion error: %d - %s", buf
[0], fido2GetCmdErrorDescription(buf
[0]));
881 PrintAndLog("GetAssertion result (%d b) OK.", len
);
883 PrintAndLog("CBOR get assertion response:");
884 PrintAndLog("---------------- CBOR ------------------");
885 TinyCborPrintFIDOPackage(fido2CmdGetAssertion
, true, &buf
[1], len
- 1);
886 PrintAndLog("---------------- CBOR ------------------");
889 // parse returned cbor
890 FIDO2GetAssertionParseRes(root
, &buf
[1], len
- 1, verbose
, verbose2
, showCBOR
);
893 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
895 PrintAndLog("ERROR: can't save the file: %s", fname
);
898 PrintAndLog("File `%s` saved.", fname
);
906 static command_t CommandTable
[] =
908 {"help", CmdHelp
, 1, "This help."},
909 {"info", CmdHFFidoInfo
, 0, "Info about FIDO tag."},
910 {"reg", CmdHFFidoRegister
, 0, "FIDO U2F Registration Message."},
911 {"auth", CmdHFFidoAuthenticate
, 0, "FIDO U2F Authentication Message."},
912 {"make", CmdHFFido2MakeCredential
, 0, "FIDO2 MakeCredential command."},
913 {"assert", CmdHFFido2GetAssertion
, 0, "FIDO2 GetAssertion command."},
914 {NULL
, NULL
, 0, NULL
}
917 int CmdHFFido(const char *Cmd
) {
918 (void)WaitForResponseTimeout(CMD_ACK
,NULL
,100);
919 CmdsParse(CommandTable
, Cmd
);
923 int CmdHelp(const char *Cmd
) {
924 CmdsHelp(CommandTable
);