fix little endian vs big endian in the macros... again... but this time correct
[RRG-proxmark3.git] / client / src / cmdhffido.c
blob64ab286879a12e35e276aa0589af4cd79305e004
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
3 //
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
6 // the license.
7 //-----------------------------------------------------------------------------
8 // High frequency MIFARE Plus commands
9 //-----------------------------------------------------------------------------
11 // Documentation here:
13 // FIDO Alliance specifications
14 // https://fidoalliance.org/download/
15 // FIDO NFC Protocol Specification v1.0
16 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
17 // FIDO U2F Raw Message Formats
18 // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
19 //-----------------------------------------------------------------------------
21 #include "cmdhffido.h"
22 #include <unistd.h>
23 #include "cmdparser.h" // command_t
24 #include "commonutil.h"
25 #include "comms.h"
26 #include "proxmark3.h"
27 #include "iso7816/iso7816core.h"
28 #include "emv/emvjson.h"
29 #include "cliparser.h"
30 #include "crypto/asn1utils.h"
31 #include "crypto/libpcrypto.h"
32 #include "fido/cbortools.h"
33 #include "fido/fidocore.h"
34 #include "ui.h"
35 #include "cmdhf14a.h"
36 #include "cmdtrace.h"
37 #include "util.h"
38 #include "fileutils.h" // laodFileJSONroot
40 #define DEF_FIDO_SIZE 2048
41 #define DEF_FIDO_PARAM_FILE "hf_fido2_defparams.json"
43 static int CmdHelp(const char *Cmd);
45 static int CmdHFFidoList(const char *Cmd) {
46 return CmdTraceListAlias(Cmd, "hf fido", "14a");
49 static int CmdHFFidoInfo(const char *Cmd) {
50 CLIParserContext *ctx;
51 CLIParserInit(&ctx, "hf fido info",
52 "Get info from Fido tags",
53 "hf fido info");
55 void *argtable[] = {
56 arg_param_begin,
57 arg_param_end
59 CLIExecWithReturn(ctx, Cmd, argtable, true);
60 CLIParserFree(ctx);
62 // info about 14a part
63 infoHF14A(false, false, false);
65 // FIDO info
66 PrintAndLogEx(INFO, "-----------" _CYAN_("FIDO Info") "---------------------------------");
67 SetAPDULogging(false);
69 uint8_t buf[APDU_RES_LEN] = {0};
70 size_t len = 0;
71 uint16_t sw = 0;
72 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
74 if (res) {
75 DropField();
76 return res;
79 if (sw != 0x9000) {
80 if (sw)
81 PrintAndLogEx(INFO, "Not a FIDO card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
82 else
83 PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000.");
85 DropField();
86 return PM3_SUCCESS;
89 if (!strncmp((char *)buf, "U2F_V2", 7)) {
90 if (!strncmp((char *)buf, "FIDO_2_0", 8)) {
91 PrintAndLogEx(INFO, "FIDO2 authenticator detected. Version: %.*s", (int)len, buf);
92 } else {
93 PrintAndLogEx(INFO, "FIDO authenticator detected (not standard U2F).");
94 PrintAndLogEx(INFO, "Non U2F authenticator version:");
95 print_buffer((const unsigned char *)buf, len, 1);
97 } else {
98 PrintAndLogEx(INFO, "FIDO U2F authenticator detected. Version: %.*s", (int)len, buf);
101 res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw);
102 DropField();
103 if (res) {
104 return res;
106 if (sw != 0x9000) {
107 PrintAndLogEx(ERR, "FIDO2 version doesn't exist (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
108 return PM3_SUCCESS;
111 if (buf[0]) {
112 PrintAndLogEx(ERR, "FIDO2 get version error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
113 return PM3_SUCCESS;
116 if (len > 1) {
117 PrintAndLogEx(SUCCESS, "FIDO2 version CBOR decoded:");
118 TinyCborPrintFIDOPackage(fido2CmdGetInfo, true, &buf[1], len - 1);
119 } else {
120 PrintAndLogEx(ERR, "FIDO2 version length error");
122 return PM3_SUCCESS;
125 static int CmdHFFidoRegister(const char *cmd) {
126 CLIParserContext *ctx;
127 CLIParserInit(&ctx, "hf fido reg",
128 "Initiate a U2F token registration. Needs two 32-byte hash numbers.\n"
129 "challenge parameter (32b) and application parameter (32b).\n"
130 "The default config filename is `fido2_defparams.json`\n"
131 "\n",
132 "hf fido reg -> execute command with 2 parameters, filled 0x00\n"
133 "hf fido reg --cp s0 --ap s1 -> execute command with plain parameters\n"
134 "hf fido reg --cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f --apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
135 "hf fido reg -f fido2-params -> execute command with custom config file\n"
138 void *argtable[] = {
139 arg_param_begin,
140 arg_lit0("a", "apdu", "show APDU requests and responses"),
141 arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
142 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
143 arg_str0("f", "file", "<fn>", "JSON input file name for parameters"),
144 arg_str0(NULL, "cp", "<ascii>", "challenge parameter (1..16 chars)"),
145 arg_str0(NULL, "ap", "<ascii>", "application parameter (1..16 chars)"),
146 arg_str0(NULL, "cpx", "<hex>", "challenge parameter (32 bytes hex)"),
147 arg_str0(NULL, "apx", "<hex>", "application parameter (32 bytes hex)"),
148 arg_param_end
150 CLIExecWithReturn(ctx, cmd, argtable, true);
152 bool APDULogging = arg_get_lit(ctx, 1);
153 bool verbose = arg_get_lit(ctx, 2);
154 bool verbose2 = arg_get_lit(ctx, 2) > 1;
155 bool showDERTLV = arg_get_lit(ctx, 3);
156 bool cpplain = arg_get_str_len(ctx, 5);
157 bool applain = arg_get_str_len(ctx, 6);
158 bool cphex = arg_get_str_len(ctx, 7);
159 bool aphex = arg_get_str_len(ctx, 8);
161 uint8_t data[64] = {0};
162 int chlen = 0;
163 uint8_t cdata[250] = {0};
164 int applen = 0;
165 uint8_t adata[250] = {0};
167 int fnlen = 0;
168 char filename[FILE_PATH_SIZE] = {0};
169 CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
171 // deafault name
172 if (fnlen == 0) {
173 strcat(filename, DEF_FIDO_PARAM_FILE);
174 fnlen = strlen(filename);
177 json_t *root = NULL;
178 int res = loadFileJSONroot(filename, (void **)&root, verbose);
179 if (res != PM3_SUCCESS) {
180 CLIParserFree(ctx);
181 return PM3_EINVARG;
184 size_t jlen = 0;
185 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
186 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
188 if (cpplain) {
189 memset(cdata, 0x00, 32);
190 chlen = sizeof(cdata);
191 CLIGetStrWithReturn(ctx, 5, cdata, &chlen);
192 if (chlen > 16) {
193 PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen);
194 CLIParserFree(ctx);
195 json_decref(root);
196 return PM3_EINVARG;
199 if (cphex && cpplain == false) {
200 chlen = sizeof(cdata);
201 CLIGetHexWithReturn(ctx, 7, cdata, &chlen);
202 if (chlen && chlen != 32) {
203 PrintAndLogEx(ERR, "ERROR: challenge parameter length must be 32 bytes only.");
204 CLIParserFree(ctx);
205 json_decref(root);
206 return PM3_EINVARG;
209 if (chlen)
210 memmove(data, cdata, 32);
212 if (applain) {
213 memset(adata, 0x00, 32);
214 applen = sizeof(adata);
215 CLIGetStrWithReturn(ctx, 6, adata, &applen);
216 if (applen > 16) {
217 PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen);
218 CLIParserFree(ctx);
219 json_decref(root);
220 return PM3_EINVARG;
223 if (aphex && applain == false) {
224 applen = sizeof(adata);
225 CLIGetHexWithReturn(ctx, 8, adata, &applen);
226 if (applen && applen != 32) {
227 PrintAndLogEx(ERR, "ERROR: application parameter length must be 32 bytes only.");
228 CLIParserFree(ctx);
229 json_decref(root);
230 return PM3_EINVARG;
233 if (applen)
234 memmove(&data[32], adata, 32);
236 CLIParserFree(ctx);
238 SetAPDULogging(APDULogging);
240 // challenge parameter [32 bytes] - The challenge parameter is the SHA-256 hash of the Client Data, a stringified JSON data structure that the FIDO Client prepares
241 // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
243 uint8_t buf[2048] = {0};
244 size_t len = 0;
245 uint16_t sw = 0;
247 DropField();
248 res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
250 if (res) {
251 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
252 DropField();
253 json_decref(root);
254 return res;
257 if (sw != 0x9000) {
258 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
259 DropField();
260 json_decref(root);
261 return PM3_ESOFT;
264 res = FIDORegister(data, buf, sizeof(buf), &len, &sw);
265 DropField();
266 if (res) {
267 PrintAndLogEx(ERR, "Can't execute register command. res=%x. Exit...", res);
268 json_decref(root);
269 return res;
272 if (sw != 0x9000) {
273 PrintAndLogEx(ERR, "ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
274 return PM3_ESOFT;
277 PrintAndLogEx(NORMAL, "");
278 if (APDULogging)
279 PrintAndLogEx(INFO, "---------------------------------------------------------------");
281 PrintAndLogEx(INFO, "data len: %zu", len);
283 if (verbose2) {
284 PrintAndLogEx(INFO, "------------ " _CYAN_("data") " ----------------------");
285 print_buffer((const unsigned char *)buf, len, 1);
286 PrintAndLogEx(INFO, "-------------" _CYAN_("data") " ----------------------");
289 if (buf[0] != 0x05) {
290 PrintAndLogEx(ERR, "ERROR: First byte must be 0x05, but it %2x", buf[0]);
291 json_decref(root);
292 return PM3_ESOFT;
294 PrintAndLogEx(SUCCESS, "User public key: %s", sprint_hex(&buf[1], 65));
296 uint8_t keyHandleLen = buf[66];
297 PrintAndLogEx(SUCCESS, "Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen));
299 int derp = 67 + keyHandleLen;
300 int derLen = (buf[derp + 2] << 8) + buf[derp + 3] + 4;
301 if (verbose2) {
302 PrintAndLogEx(INFO, "DER certificate[%d]:", derLen);
303 PrintAndLogEx(INFO, "------------------DER-------------------");
304 PrintAndLogEx(INFO, "%s", sprint_hex(buf + derp, derLen));
305 PrintAndLogEx(INFO, "----------------DER---------------------");
306 } else {
307 if (verbose)
308 PrintAndLogEx(INFO, "------------------DER-------------------");
309 PrintAndLogEx(INFO, "DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20));
312 // check and print DER certificate
313 uint8_t public_key[65] = {0};
315 // print DER certificate in TLV view
316 if (showDERTLV) {
317 PrintAndLogEx(INFO, "----------------DER TLV-----------------");
318 asn1_print(&buf[derp], derLen, " ");
319 PrintAndLogEx(INFO, "----------------DER TLV-----------------");
322 FIDOCheckDERAndGetKey(&buf[derp], derLen, verbose, public_key, sizeof(public_key));
324 // get hash
325 int hashp = 1 + 65 + 1 + keyHandleLen + derLen;
326 PrintAndLogEx(SUCCESS, "Hash[%zu]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp));
328 // check ANSI X9.62 format ECDSA signature (on P-256)
329 uint8_t rval[300] = {0};
330 uint8_t sval[300] = {0};
331 res = ecdsa_asn1_get_signature(&buf[hashp], len - hashp, rval, sval);
332 if (res == PM3_SUCCESS) {
333 if (verbose) {
334 PrintAndLogEx(INFO, " r: %s", sprint_hex(rval, 32));
335 PrintAndLogEx(INFO, " s: %s", sprint_hex(sval, 32));
338 uint8_t xbuf[4096] = {0};
339 size_t xbuflen = 0;
340 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
341 "\x00", 1,
342 &data[32], 32, // application parameter
343 &data[0], 32, // challenge parameter
344 &buf[67], keyHandleLen, // keyHandle
345 &buf[1], 65, // user public key
346 NULL, 0);
347 (void)res;
348 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
349 res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[hashp], len - hashp, true);
350 if (res) {
351 if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) {
352 PrintAndLogEx(WARNING, "Signature is ( " _RED_("not valid") " )");
353 } else {
354 PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res));
356 } else {
357 PrintAndLogEx(SUCCESS, "Signature is ( " _GREEN_("ok") " )");
360 } else {
361 PrintAndLogEx(WARNING, "Invalid signature. res = %d. ( " _RED_("fail") " )", res);
364 PrintAndLogEx(INFO, "");
365 PrintAndLogEx(INFO, "auth command: ");
366 char command[500] = {0};
367 sprintf(command, "hf fido auth --kh %s", sprint_hex_inrow(&buf[67], keyHandleLen));
368 if (chlen)
369 sprintf(command + strlen(command), " --%s %s", cpplain ? "cp" : "cpx", cpplain ? (char *)cdata : sprint_hex_inrow(cdata, 32));
370 if (applen)
371 sprintf(command + strlen(command), " --%s %s", applain ? "cp" : "cpx", applain ? (char *)adata : sprint_hex_inrow(adata, 32));
372 PrintAndLogEx(INFO, "%s", command);
374 if (root) {
375 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
376 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
377 JsonSaveBufAsHexCompact(root, "PublicKey", &buf[1], 65);
378 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
379 JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen);
380 JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen);
382 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true);
383 (void)res;
385 json_decref(root);
386 return PM3_SUCCESS;
389 static int CmdHFFidoAuthenticate(const char *cmd) {
390 CLIParserContext *ctx;
391 CLIParserInit(&ctx, "hf fido auth",
392 "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers.\n"
393 "key handle(var 0..255), challenge parameter (32b) and application parameter (32b)\n"
394 "The default config filename is `fido2_defparams.json`\n"
395 "\n",
396 "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
397 "hf fido auth \n"
398 "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
399 "--cp 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n"
400 "--ap 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
402 void *argtable[] = {
403 arg_param_begin,
404 arg_lit0("a", "apdu", "show APDU reqests and responses"),
405 arg_lit0("v", "verbose", "show technical data"),
406 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
407 arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"),
408 arg_lit0("c", "check", "mode: check-only"),
409 arg_str0("f", "file", "<fn>", "JSON input file name for parameters"),
410 arg_str0("k", "key", "<hex>", "public key to verify signature"),
411 arg_str0(NULL, "kh", "<hex>", "key handle (var 0..255b)"),
412 arg_str0(NULL, "cp", "<ascii>", "challenge parameter (1..16 chars)"),
413 arg_str0(NULL, "ap", "<ascii>", "application parameter (1..16 chars)"),
414 arg_str0(NULL, "cpx", "<hex>", "challenge parameter (32 bytes hex)"),
415 arg_str0(NULL, "apx", "<hex>", "application parameter (32 bytes hex)"),
416 arg_param_end
418 CLIExecWithReturn(ctx, cmd, argtable, true);
420 bool APDULogging = arg_get_lit(ctx, 1);
421 bool verbose = arg_get_lit(ctx, 2);
423 uint8_t controlByte = 0x08;
424 if (arg_get_lit(ctx, 4))
425 controlByte = 0x03;
427 if (arg_get_lit(ctx, 5))
428 controlByte = 0x07;
430 uint8_t data[512] = {0};
431 uint8_t hdata[250] = {0};
432 bool public_key_loaded = false;
433 uint8_t public_key[65] = {0};
434 int hdatalen = 0;
435 uint8_t keyHandleLen = 0;
437 int fnlen = 0;
438 char filename[FILE_PATH_SIZE] = {0};
439 CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
441 // deafault name
442 if (fnlen == 0) {
443 strcat(filename, DEF_FIDO_PARAM_FILE);
444 fnlen = strlen(filename);
447 json_t *root = NULL;
448 int res = loadFileJSONroot(filename, (void **)&root, verbose);
449 if (res != PM3_SUCCESS) {
450 CLIParserFree(ctx);
451 return PM3_EINVARG;
454 size_t jlen = 0;
455 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
456 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
457 JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen);
458 keyHandleLen = jlen & 0xff;
459 data[64] = keyHandleLen;
460 JsonLoadBufAsHex(root, "$.PublicKey", public_key, 65, &jlen);
461 public_key_loaded = (jlen > 0);
463 // public key
464 CLIGetHexWithReturn(ctx, 7, hdata, &hdatalen);
465 if (hdatalen && hdatalen != 65) {
466 PrintAndLogEx(ERR, "ERROR: public key length must be 65 bytes only.");
467 CLIParserFree(ctx);
468 json_decref(root);
469 return PM3_EINVARG;
472 if (hdatalen) {
473 memmove(public_key, hdata, hdatalen);
474 public_key_loaded = true;
477 CLIGetHexWithReturn(ctx, 8, hdata, &hdatalen);
478 if (hdatalen > 255) {
479 PrintAndLogEx(ERR, "ERROR: key handle length must be less than 255.");
480 CLIParserFree(ctx);
481 json_decref(root);
482 return PM3_EINVARG;
485 printf("-- hlen=%d\n", hdatalen);
486 if (hdatalen) {
487 keyHandleLen = hdatalen;
488 data[64] = keyHandleLen;
489 memmove(&data[65], hdata, keyHandleLen);
490 hdatalen = 0;
493 bool cpplain = arg_get_str_len(ctx, 9);
494 bool applain = arg_get_str_len(ctx, 10);
495 bool cphex = arg_get_str_len(ctx, 11);
496 bool aphex = arg_get_str_len(ctx, 12);
498 if (cpplain) {
499 memset(hdata, 0x00, 32);
500 hdatalen = sizeof(hdata);
501 CLIGetStrWithReturn(ctx, 9, hdata, &hdatalen);
502 if (hdatalen > 16) {
503 PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
504 CLIParserFree(ctx);
505 json_decref(root);
506 return PM3_EINVARG;
509 if (cphex && cpplain == false) {
510 hdatalen = sizeof(hdata);
511 CLIGetHexWithReturn(ctx, 11, hdata, &hdatalen);
512 if (hdatalen && hdatalen != 32) {
513 PrintAndLogEx(ERR, "ERROR: challenge parameter length must be 32 bytes only.");
514 CLIParserFree(ctx);
515 json_decref(root);
516 return PM3_EINVARG;
519 if (hdatalen) {
520 memmove(data, hdata, 32);
521 hdatalen = 0;
524 if (applain) {
525 memset(hdata, 0x00, 32);
526 hdatalen = sizeof(hdata);
527 CLIGetStrWithReturn(ctx, 10, hdata, &hdatalen);
528 if (hdatalen > 16) {
529 PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
530 CLIParserFree(ctx);
531 json_decref(root);
532 return PM3_EINVARG;
535 if (aphex && applain == false) {
536 hdatalen = sizeof(hdata);
537 CLIGetHexWithReturn(ctx, 12, hdata, &hdatalen);
538 if (hdatalen && hdatalen != 32) {
539 PrintAndLogEx(ERR, "ERROR: application parameter length must be 32 bytes only.");
540 CLIParserFree(ctx);
541 json_decref(root);
542 return PM3_EINVARG;
545 if (hdatalen) {
546 memmove(&data[32], hdata, 32);
547 hdatalen = 0;
550 CLIParserFree(ctx);
552 SetAPDULogging(APDULogging);
554 // (in parameter) control byte 0x07 - check only, 0x03 - user presence + cign. 0x08 - sign only
555 // challenge parameter [32 bytes]
556 // application parameter [32 bytes]
557 // key handle length [1b] = N
558 // key handle [N]
560 uint8_t datalen = 32 + 32 + 1 + keyHandleLen;
562 uint8_t buf[2048] = {0};
563 size_t len = 0;
564 uint16_t sw = 0;
566 DropField();
567 res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
568 if (res) {
569 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
570 DropField();
571 json_decref(root);
572 return res;
575 if (sw != 0x9000) {
576 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
577 DropField();
578 json_decref(root);
579 return PM3_ESOFT;
582 res = FIDOAuthentication(data, datalen, controlByte, buf, sizeof(buf), &len, &sw);
583 DropField();
584 if (res) {
585 PrintAndLogEx(ERR, "Can't execute authentication command. res=%x. Exit...", res);
586 json_decref(root);
587 return res;
590 if (sw != 0x9000) {
591 PrintAndLogEx(ERR, "ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
592 json_decref(root);
593 return PM3_ESOFT;
596 PrintAndLogEx(INFO, "---------------------------------------------------------------");
597 PrintAndLogEx(SUCCESS, "User presence: %s", (buf[0] ? "verified" : "not verified"));
598 uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4);
599 PrintAndLogEx(SUCCESS, "Counter: %d", cntr);
600 PrintAndLogEx(SUCCESS, "Hash[%zu]: %s", len - 5, sprint_hex(&buf[5], len - 5));
602 // check ANSI X9.62 format ECDSA signature (on P-256)
603 uint8_t rval[300] = {0};
604 uint8_t sval[300] = {0};
605 res = ecdsa_asn1_get_signature(&buf[5], len - 5, rval, sval);
606 if (res == PM3_SUCCESS) {
607 if (verbose) {
608 PrintAndLogEx(INFO, " r: %s", sprint_hex(rval, 32));
609 PrintAndLogEx(INFO, " s: %s", sprint_hex(sval, 32));
611 if (public_key_loaded) {
612 uint8_t xbuf[4096] = {0};
613 size_t xbuflen = 0;
614 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
615 &data[32], 32, // application parameter
616 &buf[0], 1, // user presence
617 &buf[1], 4, // counter
618 data, 32, // challenge parameter
619 NULL, 0);
620 (void)res;
621 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
622 res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[5], len - 5, true);
623 if (res) {
624 if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) {
625 PrintAndLogEx(WARNING, "Signature is ( " _RED_("not valid") " )");
626 } else {
627 PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res));
629 } else {
630 PrintAndLogEx(SUCCESS, "Signature is (" _GREEN_("ok") " )");
632 } else {
633 PrintAndLogEx(WARNING, "No public key provided. can't check signature.");
635 } else {
636 PrintAndLogEx(WARNING, "Invalid signature. res = %d. ( " _RED_("fail") " )", res);
639 if (root) {
640 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
641 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
642 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
643 JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen);
644 JsonSaveInt(root, "Counter", cntr);
646 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true);
647 (void)res;
649 json_decref(root);
650 return PM3_ESOFT;
653 static int CmdHFFido2MakeCredential(const char *cmd) {
654 CLIParserContext *ctx;
655 CLIParserInit(&ctx, "hf fido make",
656 "Execute a FIDO2 Make Credential command. Needs json file with parameters.\n"
657 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
658 "- for yubikey there must be only one option `\"rk\": true` or false"
660 "hf fido make --> use default parameters file `fido2_defparams.json`\n"
661 "hf fido make -f test.json --> use parameters file `text.json`"
664 void *argtable[] = {
665 arg_param_begin,
666 arg_lit0("a", "apdu", "show APDU reqests and responses"),
667 arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
668 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
669 arg_lit0("c", "cbor", "show CBOR decoded data"),
670 arg_str0("f", "file", "<fn>", "parameter JSON file name"),
671 arg_param_end
673 CLIExecWithReturn(ctx, cmd, argtable, true);
675 bool APDULogging = arg_get_lit(ctx, 1);
676 bool verbose = arg_get_lit(ctx, 2);
677 bool verbose2 = arg_get_lit(ctx, 2) > 1;
678 bool showDERTLV = arg_get_lit(ctx, 3);
679 bool showCBOR = arg_get_lit(ctx, 4);
681 int fnlen = 0;
682 char filename[FILE_PATH_SIZE] = {0};
683 CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
684 CLIParserFree(ctx);
686 // deafault name
687 if (fnlen == 0) {
688 strcat(filename, DEF_FIDO_PARAM_FILE);
689 fnlen = strlen(filename);
692 json_t *root = NULL;
693 loadFileJSONroot(filename, (void **)&root, verbose);
694 if (root == NULL) {
695 return PM3_EFILE;
698 SetAPDULogging(APDULogging);
700 uint8_t data[DEF_FIDO_SIZE] = {0};
701 size_t datalen = 0;
702 uint8_t buf[DEF_FIDO_SIZE] = {0};
703 size_t len = 0;
704 uint16_t sw = 0;
706 DropField();
707 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
708 if (res) {
709 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
710 DropField();
711 json_decref(root);
712 return res;
715 if (sw != 0x9000) {
716 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
717 DropField();
718 json_decref(root);
719 return PM3_ESOFT;
722 res = FIDO2CreateMakeCredentionalReq(root, data, sizeof(data), &datalen);
723 if (res) {
724 json_decref(root);
725 return res;
728 if (showCBOR) {
729 PrintAndLogEx(INFO, "CBOR make credential request:");
730 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
731 TinyCborPrintFIDOPackage(fido2CmdMakeCredential, false, data, datalen);
732 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
735 res = FIDO2MakeCredential(data, datalen, buf, sizeof(buf), &len, &sw);
736 DropField();
737 if (res) {
738 PrintAndLogEx(ERR, "Can't execute make credential command. res=%x. exit...", res);
739 json_decref(root);
740 return res;
743 if (sw != 0x9000) {
744 PrintAndLogEx(ERR, "ERROR execute make credential command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
745 json_decref(root);
746 return PM3_EFILE;
749 if (buf[0]) {
750 PrintAndLogEx(ERR, "FIDO2 make credential error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
751 json_decref(root);
752 return PM3_ESOFT;
755 PrintAndLogEx(SUCCESS, "MakeCredential result %zu b ( ok )", len);
756 if (showCBOR) {
757 PrintAndLogEx(SUCCESS, "CBOR make credential response:");
758 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
759 TinyCborPrintFIDOPackage(fido2CmdMakeCredential, true, &buf[1], len - 1);
760 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
763 // parse returned cbor
764 FIDO2MakeCredentionalParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR, showDERTLV);
766 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true);
767 (void)res;
768 json_decref(root);
769 return res;
772 static int CmdHFFido2GetAssertion(const char *cmd) {
773 CLIParserContext *ctx;
774 CLIParserInit(&ctx, "hf fido assert",
775 "Execute a FIDO2 Get Assertion command. Needs json file with parameters.\n"
776 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
777 "- Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)\n"
778 "- for yubikey there must be only one option `\"up\": true` or false"
780 "hf fido assert --> default parameters file `fido2_defparams.json`\n"
781 "hf fido assert -f test.json -l --> use parameters file `text.json` and add to request CredentialId");
783 void *argtable[] = {
784 arg_param_begin,
785 arg_lit0("a", "apdu", "show APDU reqests and responses"),
786 arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
787 arg_lit0("c", "cbor", "show CBOR decoded data"),
788 arg_lit0("l", "list", "add CredentialId from json to allowList"),
789 arg_str0("f", "file", "<fn>", "parameter JSON file name"),
790 arg_param_end
792 CLIExecWithReturn(ctx, cmd, argtable, true);
794 bool APDULogging = arg_get_lit(ctx, 1);
795 bool verbose = arg_get_lit(ctx, 2);
796 bool verbose2 = arg_get_lit(ctx, 2) > 1;
797 bool showCBOR = arg_get_lit(ctx, 3);
798 bool createAllowList = arg_get_lit(ctx, 4);
800 int fnlen = 0;
801 char filename[FILE_PATH_SIZE] = {0};
802 CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
803 CLIParserFree(ctx);
805 // deafault name
806 if (fnlen == 0) {
807 strcat(filename, DEF_FIDO_PARAM_FILE);
808 fnlen = strlen(filename);
811 json_t *root = NULL;
812 loadFileJSONroot(filename, (void **)&root, verbose);
813 if (root == NULL) {
814 return PM3_EFILE;
817 SetAPDULogging(APDULogging);
819 uint8_t data[DEF_FIDO_SIZE] = {0};
820 size_t datalen = 0;
821 uint8_t buf[DEF_FIDO_SIZE] = {0};
822 size_t len = 0;
823 uint16_t sw = 0;
825 DropField();
826 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
827 if (res) {
828 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. exiting...", res);
829 DropField();
830 json_decref(root);
831 return res;
834 if (sw != 0x9000) {
835 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
836 DropField();
837 json_decref(root);
838 return PM3_ESOFT;
841 res = FIDO2CreateGetAssertionReq(root, data, sizeof(data), &datalen, createAllowList);
842 if (res) {
843 json_decref(root);
844 return res;
847 if (showCBOR) {
848 PrintAndLogEx(SUCCESS, "CBOR get assertion request:");
849 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
850 TinyCborPrintFIDOPackage(fido2CmdGetAssertion, false, data, datalen);
851 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
854 res = FIDO2GetAssertion(data, datalen, buf, sizeof(buf), &len, &sw);
855 DropField();
856 if (res) {
857 PrintAndLogEx(ERR, "Can't execute get assertion command. res=%x. Exit...", res);
858 json_decref(root);
859 return res;
862 if (sw != 0x9000) {
863 PrintAndLogEx(ERR, "ERROR execute get assertion command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
864 json_decref(root);
865 return PM3_ESOFT;
868 if (buf[0]) {
869 PrintAndLogEx(ERR, "FIDO2 get assertion error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
870 json_decref(root);
871 return PM3_ESOFT;
874 PrintAndLogEx(SUCCESS, "GetAssertion result (%zu b) OK.", len);
875 if (showCBOR) {
876 PrintAndLogEx(SUCCESS, "CBOR get assertion response:");
877 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
878 TinyCborPrintFIDOPackage(fido2CmdGetAssertion, true, &buf[1], len - 1);
879 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
882 // parse returned cbor
883 FIDO2GetAssertionParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR);
885 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true);
886 (void)res;
887 json_decref(root);
888 return res;
891 static command_t CommandTable[] = {
892 {"help", CmdHelp, AlwaysAvailable, "This help."},
893 {"list", CmdHFFidoList, AlwaysAvailable, "List ISO 14443A history"},
894 {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Info about FIDO tag."},
895 {"reg", CmdHFFidoRegister, IfPm3Iso14443a, "FIDO U2F Registration Message."},
896 {"auth", CmdHFFidoAuthenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."},
897 {"make", CmdHFFido2MakeCredential, IfPm3Iso14443a, "FIDO2 MakeCredential command."},
898 {"assert", CmdHFFido2GetAssertion, IfPm3Iso14443a, "FIDO2 GetAssertion command."},
899 {NULL, NULL, 0, NULL}
902 int CmdHFFido(const char *Cmd) {
903 clearCommandBuffer();
904 return CmdsParse(CommandTable, Cmd);
907 int CmdHelp(const char *Cmd) {
908 (void)Cmd; // Cmd is not used so far
909 CmdsHelp(CommandTable);
910 return PM3_SUCCESS;