fix one too small
[RRG-proxmark3.git] / client / src / cmdhffido.c
blobb54a5a712627abf276faece5a3eae1bba1cbeebc
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
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.
8 //
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"
30 #include <unistd.h>
31 #include "cmdparser.h" // command_t
32 #include "commonutil.h"
33 #include "comms.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"
42 #include "ui.h"
43 #include "cmdhf14a.h"
44 #include "cmdtrace.h"
45 #include "util.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",
62 "hf fido info");
64 void *argtable[] = {
65 arg_param_begin,
66 arg_param_end
68 CLIExecWithReturn(ctx, Cmd, argtable, true);
69 CLIParserFree(ctx);
71 // info about 14a part
72 infoHF14A(false, false, false);
74 // FIDO info
75 PrintAndLogEx(INFO, "-----------" _CYAN_("FIDO Info") "---------------------------------");
76 SetAPDULogging(false);
78 uint8_t buf[APDU_RES_LEN] = {0};
79 size_t len = 0;
80 uint16_t sw = 0;
82 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
83 if (res) {
84 DropField();
85 return res;
88 if (sw != ISO7816_OK) {
89 if (sw) {
90 PrintAndLogEx(INFO, "Not a FIDO card. APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
91 } else {
92 PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000");
94 DropField();
95 return PM3_SUCCESS;
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);
102 } else {
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);
108 } else {
109 PrintAndLogEx(INFO, "FIDO U2F authenticator detected");
110 PrintAndLogEx(INFO, "Version... " _YELLOW_("%.*s"), (int)len, buf);
113 res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw);
114 DropField();
115 if (res) {
116 return res;
119 if (sw != ISO7816_OK) {
120 PrintAndLogEx(ERR, "FIDO2 version doesn't exist (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
121 return PM3_SUCCESS;
124 if (buf[0]) {
125 PrintAndLogEx(ERR, "FIDO2 get version error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
126 return PM3_SUCCESS;
129 if (len > 1) {
130 PrintAndLogEx(SUCCESS, "FIDO2 version CBOR decoded:");
131 TinyCborPrintFIDOPackage(fido2CmdGetInfo, true, &buf[1], len - 1);
132 } else {
133 PrintAndLogEx(ERR, "FIDO2 version length error");
135 return PM3_SUCCESS;
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"
144 "note:\n"
145 " `-vv` shows full certificates data\n"
146 "\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"
153 void *argtable[] = {
154 arg_param_begin,
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)"),
163 arg_param_end
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};
177 int chlen = 0;
178 uint8_t cdata[250] = {0};
179 int applen = 0;
180 uint8_t adata[250] = {0};
182 int fnlen = 0;
183 char filename[FILE_PATH_SIZE] = {0};
184 CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
186 // default name
187 if (fnlen == 0) {
188 strcat(filename, DEF_FIDO_PARAM_FILE);
189 fnlen = strlen(filename);
192 json_t *root = NULL;
193 int res = loadFileJSONroot(filename, (void **)&root, verbose);
194 if (res != PM3_SUCCESS) {
195 CLIParserFree(ctx);
196 return PM3_EINVARG;
199 size_t jlen = 0;
200 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
201 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
203 if (cpplain) {
204 memset(cdata, 0x00, 32);
205 chlen = sizeof(cdata);
206 CLIGetStrWithReturn(ctx, 5, cdata, &chlen);
207 if (chlen > 16) {
208 PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen);
209 CLIParserFree(ctx);
210 json_decref(root);
211 return PM3_EINVARG;
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.");
219 CLIParserFree(ctx);
220 json_decref(root);
221 return PM3_EINVARG;
224 if (chlen)
225 memmove(data, cdata, 32);
227 if (applain) {
228 memset(adata, 0x00, 32);
229 applen = sizeof(adata);
230 CLIGetStrWithReturn(ctx, 6, adata, &applen);
231 if (applen > 16) {
232 PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen);
233 CLIParserFree(ctx);
234 json_decref(root);
235 return PM3_EINVARG;
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.");
243 CLIParserFree(ctx);
244 json_decref(root);
245 return PM3_EINVARG;
248 if (applen)
249 memmove(&data[32], adata, 32);
251 CLIParserFree(ctx);
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};
259 size_t len = 0;
260 uint16_t sw = 0;
262 DropField();
263 res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
265 if (res) {
266 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
267 DropField();
268 json_decref(root);
269 return res;
272 if (sw != ISO7816_OK) {
273 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
274 DropField();
275 json_decref(root);
276 return PM3_ESOFT;
279 res = FIDORegister(data, buf, sizeof(buf), &len, &sw);
280 DropField();
281 if (res) {
282 PrintAndLogEx(ERR, "Can't execute register command. res=%x. Exit...", res);
283 json_decref(root);
284 return res;
287 if (sw != ISO7816_OK) {
288 PrintAndLogEx(ERR, "ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
289 return PM3_ESOFT;
292 PrintAndLogEx(NORMAL, "");
293 if (APDULogging)
294 PrintAndLogEx(INFO, "---------------------------------------------------------------");
296 PrintAndLogEx(INFO, "data len: %zu", len);
298 if (verbose2) {
299 PrintAndLogEx(INFO, "------------ " _CYAN_("data") " ----------------------");
300 print_buffer((const unsigned char *)buf, len, 1);
301 PrintAndLogEx(INFO, "-------------" _CYAN_("data") " ----------------------");
304 if (buf[0] != 0x05) {
305 PrintAndLogEx(ERR, "ERROR: First byte must be 0x05, but it %2x", buf[0]);
306 json_decref(root);
307 return PM3_ESOFT;
309 PrintAndLogEx(SUCCESS, "User public key: %s", sprint_hex(&buf[1], 65));
311 uint8_t keyHandleLen = buf[66];
312 PrintAndLogEx(SUCCESS, "Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen));
314 int derp = 67 + keyHandleLen;
315 int derLen = (buf[derp + 2] << 8) + buf[derp + 3] + 4;
316 if (verbose2) {
317 PrintAndLogEx(INFO, "DER certificate[%d]:", derLen);
318 PrintAndLogEx(INFO, "------------------DER-------------------");
319 PrintAndLogEx(INFO, "%s", sprint_hex(buf + derp, derLen));
320 PrintAndLogEx(INFO, "----------------DER---------------------");
321 } else {
322 if (verbose)
323 PrintAndLogEx(INFO, "------------------DER-------------------");
324 PrintAndLogEx(INFO, "DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20));
327 // check and print DER certificate
328 uint8_t public_key[65] = {0};
330 // print DER certificate in TLV view
331 if (showDERTLV) {
332 PrintAndLogEx(INFO, "----------------DER TLV-----------------");
333 asn1_print(&buf[derp], derLen, " ");
334 PrintAndLogEx(INFO, "----------------DER TLV-----------------");
337 FIDOCheckDERAndGetKey(&buf[derp], derLen, verbose, public_key, sizeof(public_key));
339 // get hash
340 int hashp = 1 + 65 + 1 + keyHandleLen + derLen;
341 PrintAndLogEx(SUCCESS, "Hash[%zu]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp));
343 // check ANSI X9.62 format ECDSA signature (on P-256)
344 uint8_t rval[300] = {0};
345 uint8_t sval[300] = {0};
346 res = ecdsa_asn1_get_signature(&buf[hashp], len - hashp, rval, sval);
347 if (res == PM3_SUCCESS) {
348 if (verbose) {
349 PrintAndLogEx(INFO, " r: %s", sprint_hex(rval, 32));
350 PrintAndLogEx(INFO, " s: %s", sprint_hex(sval, 32));
353 uint8_t xbuf[4096] = {0};
354 size_t xbuflen = 0;
355 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
356 "\x00", 1,
357 &data[32], 32, // application parameter
358 &data[0], 32, // challenge parameter
359 &buf[67], keyHandleLen, // keyHandle
360 &buf[1], 65, // user public key
361 (uint8_t *)NULL, 0);
362 (void)res;
363 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
364 res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[hashp], len - hashp, true);
365 if (res) {
366 if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) {
367 PrintAndLogEx(WARNING, "Signature is ( " _RED_("not valid") " )");
368 } else {
369 PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res));
371 } else {
372 PrintAndLogEx(SUCCESS, "Signature is ( " _GREEN_("ok") " )");
375 } else {
376 PrintAndLogEx(WARNING, "Invalid signature. res = %d. ( " _RED_("fail") " )", res);
379 PrintAndLogEx(INFO, "");
380 PrintAndLogEx(INFO, "auth command: ");
381 char command[500] = {0};
382 snprintf(command, sizeof(command), "hf fido auth --kh %s", sprint_hex_inrow(&buf[67], keyHandleLen));
383 if (chlen) {
384 size_t command_len = strlen(command);
385 snprintf(command + command_len, sizeof(command) - command_len, " --%s %s", cpplain ? "cp" : "cpx", cpplain ? (char *)cdata : sprint_hex_inrow(cdata, 32));
387 if (applen) {
388 size_t command_len = strlen(command);
389 snprintf(command + command_len, sizeof(command) - command_len, " --%s %s", applain ? "cp" : "cpx", applain ? (char *)adata : sprint_hex_inrow(adata, 32));
391 PrintAndLogEx(INFO, "%s", command);
393 if (root) {
394 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
395 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
396 JsonSaveBufAsHexCompact(root, "PublicKey", &buf[1], 65);
397 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
398 JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen);
399 JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen);
401 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true);
402 (void)res;
404 json_decref(root);
405 return PM3_SUCCESS;
408 static int CmdHFFidoAuthenticate(const char *cmd) {
409 CLIParserContext *ctx;
410 CLIParserInit(&ctx, "hf fido auth",
411 "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers.\n"
412 "key handle(var 0..255), challenge parameter (32b) and application parameter (32b)\n"
413 "The default config filename is `fido2_defparams.json`\n"
414 "\n",
415 "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
416 "hf fido auth \n"
417 "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
418 "--cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n"
419 "--apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
421 void *argtable[] = {
422 arg_param_begin,
423 arg_lit0("a", "apdu", "Show APDU requests and responses"),
424 arg_lit0("v", "verbose", "Verbose output"),
425 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
426 arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"),
427 arg_lit0("c", "check", "mode: check-only"),
428 arg_str0("f", "file", "<fn>", "JSON file name for parameters"),
429 arg_str0("k", "key", "<hex>", "Public key to verify signature"),
430 arg_str0(NULL, "kh", "<hex>", "Key handle (var 0..255b)"),
431 arg_str0(NULL, "cp", "<str>", "Challenge parameter (1..16 chars)"),
432 arg_str0(NULL, "ap", "<str>", "Application parameter (1..16 chars)"),
433 arg_str0(NULL, "cpx", "<hex>", "Challenge parameter (32 bytes hex)"),
434 arg_str0(NULL, "apx", "<hex>", "Application parameter (32 bytes hex)"),
435 arg_param_end
437 CLIExecWithReturn(ctx, cmd, argtable, true);
439 bool APDULogging = arg_get_lit(ctx, 1);
440 bool verbose = arg_get_lit(ctx, 2);
442 uint8_t controlByte = 0x08;
443 if (arg_get_lit(ctx, 4))
444 controlByte = 0x03;
446 if (arg_get_lit(ctx, 5))
447 controlByte = 0x07;
449 uint8_t data[512] = {0};
450 uint8_t hdata[256] = {0};
451 bool public_key_loaded = false;
452 uint8_t public_key[65] = {0};
453 int hdatalen = 0;
454 uint8_t keyHandleLen = 0;
456 int fnlen = 0;
457 char filename[FILE_PATH_SIZE] = {0};
458 CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
460 // default name
461 if (fnlen == 0) {
462 strcat(filename, DEF_FIDO_PARAM_FILE);
463 fnlen = strlen(filename);
466 json_t *root = NULL;
467 int res = loadFileJSONroot(filename, (void **)&root, verbose);
468 if (res != PM3_SUCCESS) {
469 CLIParserFree(ctx);
470 return PM3_EINVARG;
473 size_t jlen = 0;
474 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
475 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
476 JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen);
477 keyHandleLen = jlen & 0xff;
478 data[64] = keyHandleLen;
479 JsonLoadBufAsHex(root, "$.PublicKey", public_key, 65, &jlen);
480 public_key_loaded = (jlen > 0);
482 // public key
483 CLIGetHexWithReturn(ctx, 7, hdata, &hdatalen);
484 if (hdatalen && hdatalen != 65) {
485 PrintAndLogEx(ERR, "ERROR: public key length must be 65 bytes only.");
486 CLIParserFree(ctx);
487 json_decref(root);
488 return PM3_EINVARG;
491 if (hdatalen) {
492 memmove(public_key, hdata, hdatalen);
493 public_key_loaded = true;
496 CLIGetHexWithReturn(ctx, 8, hdata, &hdatalen);
497 if (hdatalen > 255) {
498 PrintAndLogEx(ERR, "ERROR: key handle length must be less than 255.");
499 CLIParserFree(ctx);
500 json_decref(root);
501 return PM3_EINVARG;
504 printf("-- hlen=%d\n", hdatalen);
505 if (hdatalen) {
506 keyHandleLen = hdatalen;
507 data[64] = keyHandleLen;
508 memmove(&data[65], hdata, keyHandleLen);
509 hdatalen = 0;
512 bool cpplain = arg_get_str_len(ctx, 9);
513 bool applain = arg_get_str_len(ctx, 10);
514 bool cphex = arg_get_str_len(ctx, 11);
515 bool aphex = arg_get_str_len(ctx, 12);
517 if (cpplain) {
518 memset(hdata, 0x00, 32);
519 hdatalen = sizeof(hdata);
520 CLIGetStrWithReturn(ctx, 9, hdata, &hdatalen);
521 if (hdatalen > 16) {
522 PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
523 CLIParserFree(ctx);
524 json_decref(root);
525 return PM3_EINVARG;
528 if (cphex && cpplain == false) {
529 hdatalen = sizeof(hdata);
530 CLIGetHexWithReturn(ctx, 11, hdata, &hdatalen);
531 if (hdatalen && hdatalen != 32) {
532 PrintAndLogEx(ERR, "ERROR: challenge parameter length must be 32 bytes only.");
533 CLIParserFree(ctx);
534 json_decref(root);
535 return PM3_EINVARG;
538 if (hdatalen) {
539 memmove(data, hdata, 32);
540 hdatalen = 0;
543 if (applain) {
544 memset(hdata, 0x00, 32);
545 hdatalen = sizeof(hdata);
546 CLIGetStrWithReturn(ctx, 10, hdata, &hdatalen);
547 if (hdatalen > 16) {
548 PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
549 CLIParserFree(ctx);
550 json_decref(root);
551 return PM3_EINVARG;
554 if (aphex && applain == false) {
555 hdatalen = sizeof(hdata);
556 CLIGetHexWithReturn(ctx, 12, hdata, &hdatalen);
557 if (hdatalen && hdatalen != 32) {
558 PrintAndLogEx(ERR, "ERROR: application parameter length must be 32 bytes only.");
559 CLIParserFree(ctx);
560 json_decref(root);
561 return PM3_EINVARG;
564 if (hdatalen) {
565 memmove(&data[32], hdata, 32);
566 hdatalen = 0;
569 CLIParserFree(ctx);
571 SetAPDULogging(APDULogging);
573 // (in parameter) control byte 0x07 - check only, 0x03 - user presence + cign. 0x08 - sign only
574 // challenge parameter [32 bytes]
575 // application parameter [32 bytes]
576 // key handle length [1b] = N
577 // key handle [N]
579 uint8_t datalen = 32 + 32 + 1 + keyHandleLen;
581 uint8_t buf[2048] = {0};
582 size_t len = 0;
583 uint16_t sw = 0;
585 DropField();
586 res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
587 if (res) {
588 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
589 DropField();
590 json_decref(root);
591 return res;
594 if (sw != ISO7816_OK) {
595 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
596 DropField();
597 json_decref(root);
598 return PM3_ESOFT;
601 res = FIDOAuthentication(data, datalen, controlByte, buf, sizeof(buf), &len, &sw);
602 DropField();
603 if (res) {
604 PrintAndLogEx(ERR, "Can't execute authentication command. res=%x. Exit...", res);
605 json_decref(root);
606 return res;
609 if (sw != ISO7816_OK) {
610 PrintAndLogEx(ERR, "ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
611 json_decref(root);
612 return PM3_ESOFT;
615 PrintAndLogEx(INFO, "---------------------------------------------------------------");
616 PrintAndLogEx(SUCCESS, "User presence: %s", (buf[0] ? "verified" : "not verified"));
617 uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4);
618 PrintAndLogEx(SUCCESS, "Counter: %d", cntr);
619 PrintAndLogEx(SUCCESS, "Hash[%zu]: %s", len - 5, sprint_hex(&buf[5], len - 5));
621 // check ANSI X9.62 format ECDSA signature (on P-256)
622 uint8_t rval[300] = {0};
623 uint8_t sval[300] = {0};
624 res = ecdsa_asn1_get_signature(&buf[5], len - 5, rval, sval);
625 if (res == PM3_SUCCESS) {
626 if (verbose) {
627 PrintAndLogEx(INFO, " r: %s", sprint_hex(rval, 32));
628 PrintAndLogEx(INFO, " s: %s", sprint_hex(sval, 32));
630 if (public_key_loaded) {
631 uint8_t xbuf[4096] = {0};
632 size_t xbuflen = 0;
633 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
634 &data[32], 32, // application parameter
635 &buf[0], 1, // user presence
636 &buf[1], 4, // counter
637 data, 32, // challenge parameter
638 (uint8_t *)NULL, 0);
639 (void)res;
640 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
641 res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[5], len - 5, true);
642 if (res) {
643 if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) {
644 PrintAndLogEx(WARNING, "Signature is ( " _RED_("not valid") " )");
645 } else {
646 PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res));
648 } else {
649 PrintAndLogEx(SUCCESS, "Signature is ( " _GREEN_("ok") " )");
651 } else {
652 PrintAndLogEx(WARNING, "No public key provided. can't check signature.");
654 } else {
655 PrintAndLogEx(WARNING, "Invalid signature. res = %d. ( " _RED_("fail") " )", res);
658 if (root) {
659 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
660 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
661 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
662 JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen);
663 JsonSaveInt(root, "Counter", cntr);
665 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true);
666 (void)res;
668 json_decref(root);
669 return PM3_ESOFT;
672 static int CmdHFFido2MakeCredential(const char *cmd) {
673 CLIParserContext *ctx;
674 CLIParserInit(&ctx, "hf fido make",
675 "Execute a FIDO2 Make Credential command. Needs json file with parameters.\n"
676 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
677 "- for yubikey there must be only one option `\"rk\": true` or false\n"
678 "note:\n"
679 " `-vv` shows full certificates data\n",
680 "hf fido make --> use default parameters file `fido2_defparams.json`\n"
681 "hf fido make -f test.json --> use parameters file `text.json`"
684 void *argtable[] = {
685 arg_param_begin,
686 arg_lit0("a", "apdu", "Show APDU requests and responses"),
687 arg_litn("v", "verbose", 0, 2, "Verbose output"),
688 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
689 arg_lit0("c", "cbor", "Show CBOR decoded data"),
690 arg_str0("f", "file", "<fn>", "Parameter JSON file name"),
691 arg_param_end
693 CLIExecWithReturn(ctx, cmd, argtable, true);
695 bool APDULogging = arg_get_lit(ctx, 1);
696 bool verbose = arg_get_lit(ctx, 2);
697 bool verbose2 = arg_get_lit(ctx, 2) > 1;
698 bool showDERTLV = arg_get_lit(ctx, 3);
699 bool showCBOR = arg_get_lit(ctx, 4);
701 int fnlen = 0;
702 char filename[FILE_PATH_SIZE] = {0};
703 CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
704 CLIParserFree(ctx);
706 // default name
707 if (fnlen == 0) {
708 strcat(filename, DEF_FIDO_PARAM_FILE);
709 fnlen = strlen(filename);
712 json_t *root = NULL;
713 loadFileJSONroot(filename, (void **)&root, verbose);
714 if (root == NULL) {
715 return PM3_EFILE;
718 SetAPDULogging(APDULogging);
720 uint8_t data[DEF_FIDO_SIZE] = {0};
721 size_t datalen = 0;
722 uint8_t buf[DEF_FIDO_SIZE] = {0};
723 size_t len = 0;
724 uint16_t sw = 0;
726 DropField();
727 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
728 if (res) {
729 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
730 DropField();
731 json_decref(root);
732 return res;
735 if (sw != ISO7816_OK) {
736 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
737 DropField();
738 json_decref(root);
739 return PM3_ESOFT;
742 res = FIDO2CreateMakeCredentionalReq(root, data, sizeof(data), &datalen);
743 if (res) {
744 json_decref(root);
745 return res;
748 if (showCBOR) {
749 PrintAndLogEx(INFO, "CBOR make credential request:");
750 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
751 TinyCborPrintFIDOPackage(fido2CmdMakeCredential, false, data, datalen);
752 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
755 res = FIDO2MakeCredential(data, datalen, buf, sizeof(buf), &len, &sw);
756 DropField();
757 if (res) {
758 PrintAndLogEx(ERR, "Can't execute make credential command. res=%x. exit...", res);
759 json_decref(root);
760 return res;
763 if (sw != ISO7816_OK) {
764 PrintAndLogEx(ERR, "ERROR execute make credential command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
765 json_decref(root);
766 return PM3_EFILE;
769 if (buf[0]) {
770 PrintAndLogEx(ERR, "FIDO2 make credential error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
771 json_decref(root);
772 return PM3_ESOFT;
775 PrintAndLogEx(SUCCESS, "MakeCredential result %zu b ( ok )", len);
776 if (showCBOR) {
777 PrintAndLogEx(SUCCESS, "CBOR make credential response:");
778 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
779 TinyCborPrintFIDOPackage(fido2CmdMakeCredential, true, &buf[1], len - 1);
780 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
783 // parse returned cbor
784 FIDO2MakeCredentionalParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR, showDERTLV);
786 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true);
787 (void)res;
788 json_decref(root);
789 return res;
792 static int CmdHFFido2GetAssertion(const char *cmd) {
793 CLIParserContext *ctx;
794 CLIParserInit(&ctx, "hf fido assert",
795 "Execute a FIDO2 Get Assertion command. Needs json file with parameters.\n"
796 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
797 "- Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)\n"
798 "- for yubikey there must be only one option `\"up\": true` or false\n"
799 "note:\n"
800 " `-vv` shows full certificates data\n",
801 "hf fido assert --> default parameters file `fido2_defparams.json`\n"
802 "hf fido assert -f test.json -l --> use parameters file `text.json` and add to request CredentialId");
804 void *argtable[] = {
805 arg_param_begin,
806 arg_lit0("a", "apdu", "Show APDU requests and responses"),
807 arg_litn("v", "verbose", 0, 2, "Verbose output"),
808 arg_lit0("c", "cbor", "Show CBOR decoded data"),
809 arg_lit0("l", "list", "Add CredentialId from json to allowList"),
810 arg_str0("f", "file", "<fn>", "Parameter JSON file name"),
811 arg_param_end
813 CLIExecWithReturn(ctx, cmd, argtable, true);
815 bool APDULogging = arg_get_lit(ctx, 1);
816 bool verbose = arg_get_lit(ctx, 2);
817 bool verbose2 = arg_get_lit(ctx, 2) > 1;
818 bool showCBOR = arg_get_lit(ctx, 3);
819 bool createAllowList = arg_get_lit(ctx, 4);
821 int fnlen = 0;
822 char filename[FILE_PATH_SIZE] = {0};
823 CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
824 CLIParserFree(ctx);
826 // default name
827 if (fnlen == 0) {
828 strcat(filename, DEF_FIDO_PARAM_FILE);
829 fnlen = strlen(filename);
832 json_t *root = NULL;
833 loadFileJSONroot(filename, (void **)&root, verbose);
834 if (root == NULL) {
835 return PM3_EFILE;
838 SetAPDULogging(APDULogging);
840 uint8_t data[DEF_FIDO_SIZE] = {0};
841 size_t datalen = 0;
842 uint8_t buf[DEF_FIDO_SIZE] = {0};
843 size_t len = 0;
844 uint16_t sw = 0;
846 DropField();
847 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
848 if (res) {
849 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. exiting...", res);
850 DropField();
851 json_decref(root);
852 return res;
855 if (sw != ISO7816_OK) {
856 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
857 DropField();
858 json_decref(root);
859 return PM3_ESOFT;
862 res = FIDO2CreateGetAssertionReq(root, data, sizeof(data), &datalen, createAllowList);
863 if (res) {
864 json_decref(root);
865 return res;
868 if (showCBOR) {
869 PrintAndLogEx(SUCCESS, "CBOR get assertion request:");
870 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
871 TinyCborPrintFIDOPackage(fido2CmdGetAssertion, false, data, datalen);
872 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
875 res = FIDO2GetAssertion(data, datalen, buf, sizeof(buf), &len, &sw);
876 DropField();
877 if (res) {
878 PrintAndLogEx(ERR, "Can't execute get assertion command. res=%x. Exit...", res);
879 json_decref(root);
880 return res;
883 if (sw != ISO7816_OK) {
884 PrintAndLogEx(ERR, "ERROR execute get assertion command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
885 json_decref(root);
886 return PM3_ESOFT;
889 if (buf[0]) {
890 PrintAndLogEx(ERR, "FIDO2 get assertion error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
891 json_decref(root);
892 return PM3_ESOFT;
895 PrintAndLogEx(SUCCESS, "GetAssertion result (%zu b) OK.", len);
896 if (showCBOR) {
897 PrintAndLogEx(SUCCESS, "CBOR get assertion response:");
898 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
899 TinyCborPrintFIDOPackage(fido2CmdGetAssertion, true, &buf[1], len - 1);
900 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
903 // parse returned cbor
904 FIDO2GetAssertionParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR);
906 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true);
907 (void)res;
908 json_decref(root);
909 return res;
912 static command_t CommandTable[] = {
913 {"help", CmdHelp, AlwaysAvailable, "This help."},
914 {"list", CmdHFFidoList, AlwaysAvailable, "List ISO 14443A history"},
915 {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Info about FIDO tag."},
916 {"reg", CmdHFFidoRegister, IfPm3Iso14443a, "FIDO U2F Registration Message."},
917 {"auth", CmdHFFidoAuthenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."},
918 {"make", CmdHFFido2MakeCredential, IfPm3Iso14443a, "FIDO2 MakeCredential command."},
919 {"assert", CmdHFFido2GetAssertion, IfPm3Iso14443a, "FIDO2 GetAssertion command."},
920 {NULL, NULL, 0, NULL}
923 int CmdHFFido(const char *Cmd) {
924 clearCommandBuffer();
925 return CmdsParse(CommandTable, Cmd);
928 int CmdHelp(const char *Cmd) {
929 (void)Cmd; // Cmd is not used so far
930 CmdsHelp(CommandTable);
931 return PM3_SUCCESS;