recover_pk.py: replace secp192r1 by prime192v1
[RRG-proxmark3.git] / client / src / cmdhffido.c
blob2032dca9572f78f74b1e711b9d5e76157648451b
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) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
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) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
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);
252 CLIParserFree(ctx);
254 SetAPDULogging(APDULogging);
256 // challenge parameter [32 bytes] - The challenge parameter is the SHA-256 hash of the Client Data, a stringified JSON data structure that the FIDO Client prepares
257 // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
259 uint8_t buf[2048] = {0};
260 size_t len = 0;
261 uint16_t sw = 0;
263 DropField();
264 res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
266 if (res) {
267 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
268 DropField();
269 json_decref(root);
270 return res;
273 if (sw != ISO7816_OK) {
274 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
275 DropField();
276 json_decref(root);
277 return PM3_ESOFT;
280 res = FIDORegister(data, buf, sizeof(buf), &len, &sw);
281 DropField();
282 if (res) {
283 PrintAndLogEx(ERR, "Can't execute register command. res=%x. Exit...", res);
284 json_decref(root);
285 return res;
288 if (sw != ISO7816_OK) {
289 PrintAndLogEx(ERR, "ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
290 return PM3_ESOFT;
293 PrintAndLogEx(NORMAL, "");
294 if (APDULogging)
295 PrintAndLogEx(INFO, "---------------------------------------------------------------");
297 PrintAndLogEx(INFO, "data len: %zu", len);
299 if (verbose2) {
300 PrintAndLogEx(INFO, "------------ " _CYAN_("data") " ----------------------");
301 print_buffer((const unsigned char *)buf, len, 1);
302 PrintAndLogEx(INFO, "-------------" _CYAN_("data") " ----------------------");
305 if (buf[0] != 0x05) {
306 PrintAndLogEx(ERR, "ERROR: First byte must be 0x05, but it %2x", buf[0]);
307 json_decref(root);
308 return PM3_ESOFT;
310 PrintAndLogEx(SUCCESS, "User public key: %s", sprint_hex(&buf[1], 65));
312 uint8_t keyHandleLen = buf[66];
313 PrintAndLogEx(SUCCESS, "Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen));
315 int derp = 67 + keyHandleLen;
316 int derLen = (buf[derp + 2] << 8) + buf[derp + 3] + 4;
317 if (verbose2) {
318 PrintAndLogEx(INFO, "DER certificate[%d]:", derLen);
319 PrintAndLogEx(INFO, "------------------DER-------------------");
320 PrintAndLogEx(INFO, "%s", sprint_hex(buf + derp, derLen));
321 PrintAndLogEx(INFO, "----------------DER---------------------");
322 } else {
323 if (verbose)
324 PrintAndLogEx(INFO, "------------------DER-------------------");
325 PrintAndLogEx(INFO, "DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20));
328 // check and print DER certificate
329 uint8_t public_key[65] = {0};
331 // print DER certificate in TLV view
332 if (showDERTLV) {
333 PrintAndLogEx(INFO, "----------------DER TLV-----------------");
334 asn1_print(&buf[derp], derLen, " ");
335 PrintAndLogEx(INFO, "----------------DER TLV-----------------");
338 FIDOCheckDERAndGetKey(&buf[derp], derLen, verbose, public_key, sizeof(public_key));
340 // get hash
341 int hashp = 1 + 65 + 1 + keyHandleLen + derLen;
342 PrintAndLogEx(SUCCESS, "Hash[%zu]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp));
344 // check ANSI X9.62 format ECDSA signature (on P-256)
345 uint8_t rval[300] = {0};
346 uint8_t sval[300] = {0};
347 res = ecdsa_asn1_get_signature(&buf[hashp], len - hashp, rval, sval);
348 if (res == PM3_SUCCESS) {
349 if (verbose) {
350 PrintAndLogEx(INFO, " r: %s", sprint_hex(rval, 32));
351 PrintAndLogEx(INFO, " s: %s", sprint_hex(sval, 32));
354 uint8_t xbuf[4096] = {0};
355 size_t xbuflen = 0;
356 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
357 "\x00", 1,
358 &data[32], 32, // application parameter
359 &data[0], 32, // challenge parameter
360 &buf[67], keyHandleLen, // keyHandle
361 &buf[1], 65, // user public key
362 (uint8_t *)NULL, 0);
363 (void)res;
364 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
365 res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[hashp], len - hashp, true);
366 if (res) {
367 if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) {
368 PrintAndLogEx(WARNING, "Signature is ( " _RED_("not valid") " )");
369 } else {
370 PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res));
372 } else {
373 PrintAndLogEx(SUCCESS, "Signature is ( " _GREEN_("ok") " )");
376 } else {
377 PrintAndLogEx(WARNING, "Invalid signature. res = %d. ( " _RED_("fail") " )", res);
380 PrintAndLogEx(INFO, "");
381 PrintAndLogEx(INFO, "auth command: ");
382 char command[500] = {0};
383 snprintf(command, sizeof(command), "hf fido auth --kh %s", sprint_hex_inrow(&buf[67], keyHandleLen));
384 if (chlen) {
385 size_t command_len = strlen(command);
386 snprintf(command + command_len, sizeof(command) - command_len, " --%s %s", cpplain ? "cp" : "cpx", cpplain ? (char *)cdata : sprint_hex_inrow(cdata, 32));
388 if (applen) {
389 size_t command_len = strlen(command);
390 snprintf(command + command_len, sizeof(command) - command_len, " --%s %s", applain ? "cp" : "cpx", applain ? (char *)adata : sprint_hex_inrow(adata, 32));
392 PrintAndLogEx(INFO, "%s", command);
394 if (root) {
395 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
396 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
397 JsonSaveBufAsHexCompact(root, "PublicKey", &buf[1], 65);
398 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
399 JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen);
400 JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen);
402 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true, spDump);
403 (void)res;
405 json_decref(root);
406 return PM3_SUCCESS;
409 static int CmdHFFidoAuthenticate(const char *cmd) {
410 CLIParserContext *ctx;
411 CLIParserInit(&ctx, "hf fido auth",
412 "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers.\n"
413 "key handle(var 0..255), challenge parameter (32b) and application parameter (32b)\n"
414 "The default config filename is `fido2_defparams.json`\n"
415 "\n",
416 "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
417 "hf fido auth \n"
418 "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n"
419 "--cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n"
420 "--apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
422 void *argtable[] = {
423 arg_param_begin,
424 arg_lit0("a", "apdu", "Show APDU requests and responses"),
425 arg_lit0("v", "verbose", "Verbose output"),
426 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
427 arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"),
428 arg_lit0("c", "check", "mode: check-only"),
429 arg_str0("f", "file", "<fn>", "JSON file name for parameters"),
430 arg_str0("k", "key", "<hex>", "Public key to verify signature"),
431 arg_str0(NULL, "kh", "<hex>", "Key handle (var 0..255b)"),
432 arg_str0(NULL, "cp", "<str>", "Challenge parameter (1..16 chars)"),
433 arg_str0(NULL, "ap", "<str>", "Application parameter (1..16 chars)"),
434 arg_str0(NULL, "cpx", "<hex>", "Challenge parameter (32 bytes hex)"),
435 arg_str0(NULL, "apx", "<hex>", "Application parameter (32 bytes hex)"),
436 arg_param_end
438 CLIExecWithReturn(ctx, cmd, argtable, true);
440 bool APDULogging = arg_get_lit(ctx, 1);
441 bool verbose = arg_get_lit(ctx, 2);
443 uint8_t controlByte = 0x08;
444 if (arg_get_lit(ctx, 4))
445 controlByte = 0x03;
447 if (arg_get_lit(ctx, 5))
448 controlByte = 0x07;
450 uint8_t data[512] = {0};
451 uint8_t hdata[256] = {0};
452 bool public_key_loaded = false;
453 uint8_t public_key[65] = {0};
454 int hdatalen = 0;
455 uint8_t keyHandleLen = 0;
457 int fnlen = 0;
458 char filename[FILE_PATH_SIZE] = {0};
459 CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
461 // default name
462 if (fnlen == 0) {
463 strcat(filename, DEF_FIDO_PARAM_FILE);
464 fnlen = strlen(filename);
467 json_t *root = NULL;
468 int res = loadFileJSONroot(filename, (void **)&root, verbose);
469 if (res != PM3_SUCCESS) {
470 CLIParserFree(ctx);
471 return PM3_EINVARG;
474 size_t jlen = 0;
475 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
476 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
477 JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen);
478 keyHandleLen = jlen & 0xff;
479 data[64] = keyHandleLen;
480 JsonLoadBufAsHex(root, "$.PublicKey", public_key, 65, &jlen);
481 public_key_loaded = (jlen > 0);
483 // public key
484 CLIGetHexWithReturn(ctx, 7, hdata, &hdatalen);
485 if (hdatalen && hdatalen != 65) {
486 PrintAndLogEx(ERR, "ERROR: public key length must be 65 bytes only.");
487 CLIParserFree(ctx);
488 json_decref(root);
489 return PM3_EINVARG;
492 if (hdatalen) {
493 memmove(public_key, hdata, hdatalen);
494 public_key_loaded = true;
497 CLIGetHexWithReturn(ctx, 8, hdata, &hdatalen);
498 if (hdatalen > 255) {
499 PrintAndLogEx(ERR, "ERROR: key handle length must be less than 255.");
500 CLIParserFree(ctx);
501 json_decref(root);
502 return PM3_EINVARG;
505 printf("-- hlen=%d\n", hdatalen);
506 if (hdatalen) {
507 keyHandleLen = hdatalen;
508 data[64] = keyHandleLen;
509 memmove(&data[65], hdata, keyHandleLen);
510 hdatalen = 0;
513 bool cpplain = arg_get_str_len(ctx, 9);
514 bool applain = arg_get_str_len(ctx, 10);
515 bool cphex = arg_get_str_len(ctx, 11);
516 bool aphex = arg_get_str_len(ctx, 12);
518 if (cpplain) {
519 memset(hdata, 0x00, 32);
520 hdatalen = sizeof(hdata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
521 CLIGetStrWithReturn(ctx, 9, hdata, &hdatalen);
522 if (hdatalen > 16) {
523 PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
524 CLIParserFree(ctx);
525 json_decref(root);
526 return PM3_EINVARG;
529 if (cphex && cpplain == false) {
530 hdatalen = sizeof(hdata);
531 CLIGetHexWithReturn(ctx, 11, hdata, &hdatalen);
532 if (hdatalen && hdatalen != 32) {
533 PrintAndLogEx(ERR, "ERROR: challenge parameter length must be 32 bytes only.");
534 CLIParserFree(ctx);
535 json_decref(root);
536 return PM3_EINVARG;
539 if (hdatalen) {
540 memmove(data, hdata, 32);
541 hdatalen = 0;
544 if (applain) {
545 memset(hdata, 0x00, 32);
546 hdatalen = sizeof(hdata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
547 CLIGetStrWithReturn(ctx, 10, hdata, &hdatalen);
548 if (hdatalen > 16) {
549 PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
550 CLIParserFree(ctx);
551 json_decref(root);
552 return PM3_EINVARG;
555 if (aphex && applain == false) {
556 hdatalen = sizeof(hdata);
557 CLIGetHexWithReturn(ctx, 12, hdata, &hdatalen);
558 if (hdatalen && hdatalen != 32) {
559 PrintAndLogEx(ERR, "ERROR: application parameter length must be 32 bytes only.");
560 CLIParserFree(ctx);
561 json_decref(root);
562 return PM3_EINVARG;
565 if (hdatalen) {
566 memmove(&data[32], hdata, 32);
567 hdatalen = 0;
570 CLIParserFree(ctx);
572 SetAPDULogging(APDULogging);
574 // (in parameter) control byte 0x07 - check only, 0x03 - user presence + cign. 0x08 - sign only
575 // challenge parameter [32 bytes]
576 // application parameter [32 bytes]
577 // key handle length [1b] = N
578 // key handle [N]
580 uint8_t datalen = 32 + 32 + 1 + keyHandleLen;
582 uint8_t buf[2048] = {0};
583 size_t len = 0;
584 uint16_t sw = 0;
586 DropField();
587 res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
588 if (res) {
589 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
590 DropField();
591 json_decref(root);
592 return res;
595 if (sw != ISO7816_OK) {
596 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
597 DropField();
598 json_decref(root);
599 return PM3_ESOFT;
602 res = FIDOAuthentication(data, datalen, controlByte, buf, sizeof(buf), &len, &sw);
603 DropField();
604 if (res) {
605 PrintAndLogEx(ERR, "Can't execute authentication command. res=%x. Exit...", res);
606 json_decref(root);
607 return res;
610 if (sw != ISO7816_OK) {
611 PrintAndLogEx(ERR, "ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
612 json_decref(root);
613 return PM3_ESOFT;
616 PrintAndLogEx(INFO, "---------------------------------------------------------------");
617 PrintAndLogEx(SUCCESS, "User presence: %s", (buf[0] ? "verified" : "not verified"));
618 uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4);
619 PrintAndLogEx(SUCCESS, "Counter: %d", cntr);
620 PrintAndLogEx(SUCCESS, "Hash[%zu]: %s", len - 5, sprint_hex(&buf[5], len - 5));
622 // check ANSI X9.62 format ECDSA signature (on P-256)
623 uint8_t rval[300] = {0};
624 uint8_t sval[300] = {0};
625 res = ecdsa_asn1_get_signature(&buf[5], len - 5, rval, sval);
626 if (res == PM3_SUCCESS) {
627 if (verbose) {
628 PrintAndLogEx(INFO, " r: %s", sprint_hex(rval, 32));
629 PrintAndLogEx(INFO, " s: %s", sprint_hex(sval, 32));
631 if (public_key_loaded) {
632 uint8_t xbuf[4096] = {0};
633 size_t xbuflen = 0;
634 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
635 &data[32], 32, // application parameter
636 &buf[0], 1, // user presence
637 &buf[1], 4, // counter
638 data, 32, // challenge parameter
639 (uint8_t *)NULL, 0);
640 (void)res;
641 //PrintAndLogEx(INFO, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
642 res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[5], len - 5, true);
643 if (res) {
644 if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) {
645 PrintAndLogEx(WARNING, "Signature is ( " _RED_("not valid") " )");
646 } else {
647 PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res));
649 } else {
650 PrintAndLogEx(SUCCESS, "Signature is ( " _GREEN_("ok") " )");
652 } else {
653 PrintAndLogEx(WARNING, "No public key provided. can't check signature.");
655 } else {
656 PrintAndLogEx(WARNING, "Invalid signature. res = %d. ( " _RED_("fail") " )", res);
659 if (root) {
660 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
661 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
662 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
663 JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen);
664 JsonSaveInt(root, "Counter", cntr);
666 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true, spDump);
667 (void)res;
669 json_decref(root);
670 return PM3_ESOFT;
673 static int CmdHFFido2MakeCredential(const char *cmd) {
674 CLIParserContext *ctx;
675 CLIParserInit(&ctx, "hf fido make",
676 "Execute a FIDO2 Make Credential command. Needs json file with parameters.\n"
677 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
678 "- for yubikey there must be only one option `\"rk\": true` or false\n"
679 "note:\n"
680 " `-vv` shows full certificates data\n",
681 "hf fido make --> use default parameters file `fido2_defparams.json`\n"
682 "hf fido make -f test.json --> use parameters file `text.json`"
685 void *argtable[] = {
686 arg_param_begin,
687 arg_lit0("a", "apdu", "Show APDU requests and responses"),
688 arg_litn("v", "verbose", 0, 2, "Verbose output"),
689 arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"),
690 arg_lit0("c", "cbor", "Show CBOR decoded data"),
691 arg_str0("f", "file", "<fn>", "Parameter JSON file name"),
692 arg_param_end
694 CLIExecWithReturn(ctx, cmd, argtable, true);
696 bool APDULogging = arg_get_lit(ctx, 1);
697 bool verbose = arg_get_lit(ctx, 2);
698 bool verbose2 = arg_get_lit(ctx, 2) > 1;
699 bool showDERTLV = arg_get_lit(ctx, 3);
700 bool showCBOR = arg_get_lit(ctx, 4);
702 int fnlen = 0;
703 char filename[FILE_PATH_SIZE] = {0};
704 CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
705 CLIParserFree(ctx);
707 // default name
708 if (fnlen == 0) {
709 strcat(filename, DEF_FIDO_PARAM_FILE);
710 fnlen = strlen(filename);
713 json_t *root = NULL;
714 loadFileJSONroot(filename, (void **)&root, verbose);
715 if (root == NULL) {
716 return PM3_EFILE;
719 SetAPDULogging(APDULogging);
721 uint8_t data[DEF_FIDO_SIZE] = {0};
722 size_t datalen = 0;
723 uint8_t buf[DEF_FIDO_SIZE] = {0};
724 size_t len = 0;
725 uint16_t sw = 0;
727 DropField();
728 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
729 if (res) {
730 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. Exit...", res);
731 DropField();
732 json_decref(root);
733 return res;
736 if (sw != ISO7816_OK) {
737 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
738 DropField();
739 json_decref(root);
740 return PM3_ESOFT;
743 res = FIDO2CreateMakeCredentionalReq(root, data, sizeof(data), &datalen);
744 if (res) {
745 json_decref(root);
746 return res;
749 if (showCBOR) {
750 PrintAndLogEx(INFO, "CBOR make credential request:");
751 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
752 TinyCborPrintFIDOPackage(fido2CmdMakeCredential, false, data, datalen);
753 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
756 res = FIDO2MakeCredential(data, datalen, buf, sizeof(buf), &len, &sw);
757 DropField();
758 if (res) {
759 PrintAndLogEx(ERR, "Can't execute make credential command. res=%x. exit...", res);
760 json_decref(root);
761 return res;
764 if (sw != ISO7816_OK) {
765 PrintAndLogEx(ERR, "ERROR execute make credential command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
766 json_decref(root);
767 return PM3_EFILE;
770 if (buf[0]) {
771 PrintAndLogEx(ERR, "FIDO2 make credential error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
772 json_decref(root);
773 return PM3_ESOFT;
776 PrintAndLogEx(SUCCESS, "MakeCredential result %zu b ( ok )", len);
777 if (showCBOR) {
778 PrintAndLogEx(SUCCESS, "CBOR make credential response:");
779 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
780 TinyCborPrintFIDOPackage(fido2CmdMakeCredential, true, &buf[1], len - 1);
781 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
784 // parse returned cbor
785 FIDO2MakeCredentionalParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR, showDERTLV);
787 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true, spDump);
788 (void)res;
789 json_decref(root);
790 return res;
793 static int CmdHFFido2GetAssertion(const char *cmd) {
794 CLIParserContext *ctx;
795 CLIParserInit(&ctx, "hf fido assert",
796 "Execute a FIDO2 Get Assertion command. Needs json file with parameters.\n"
797 "Sample file `fido2_defparams.json` in `client/resources/`.\n"
798 "- Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)\n"
799 "- for yubikey there must be only one option `\"up\": true` or false\n"
800 "note:\n"
801 " `-vv` shows full certificates data\n",
802 "hf fido assert --> default parameters file `fido2_defparams.json`\n"
803 "hf fido assert -f test.json -l --> use parameters file `text.json` and add to request CredentialId");
805 void *argtable[] = {
806 arg_param_begin,
807 arg_lit0("a", "apdu", "Show APDU requests and responses"),
808 arg_litn("v", "verbose", 0, 2, "Verbose output"),
809 arg_lit0("c", "cbor", "Show CBOR decoded data"),
810 arg_lit0("l", "list", "Add CredentialId from json to allowList"),
811 arg_str0("f", "file", "<fn>", "Parameter JSON file name"),
812 arg_param_end
814 CLIExecWithReturn(ctx, cmd, argtable, true);
816 bool APDULogging = arg_get_lit(ctx, 1);
817 bool verbose = arg_get_lit(ctx, 2);
818 bool verbose2 = arg_get_lit(ctx, 2) > 1;
819 bool showCBOR = arg_get_lit(ctx, 3);
820 bool createAllowList = arg_get_lit(ctx, 4);
822 int fnlen = 0;
823 char filename[FILE_PATH_SIZE] = {0};
824 CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
825 CLIParserFree(ctx);
827 // default name
828 if (fnlen == 0) {
829 strcat(filename, DEF_FIDO_PARAM_FILE);
830 fnlen = strlen(filename);
833 json_t *root = NULL;
834 loadFileJSONroot(filename, (void **)&root, verbose);
835 if (root == NULL) {
836 return PM3_EFILE;
839 SetAPDULogging(APDULogging);
841 uint8_t data[DEF_FIDO_SIZE] = {0};
842 size_t datalen = 0;
843 uint8_t buf[DEF_FIDO_SIZE] = {0};
844 size_t len = 0;
845 uint16_t sw = 0;
847 DropField();
848 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
849 if (res) {
850 PrintAndLogEx(ERR, "Can't select authenticator. res=%x. exiting...", res);
851 DropField();
852 json_decref(root);
853 return res;
856 if (sw != ISO7816_OK) {
857 PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
858 DropField();
859 json_decref(root);
860 return PM3_ESOFT;
863 res = FIDO2CreateGetAssertionReq(root, data, sizeof(data), &datalen, createAllowList);
864 if (res) {
865 json_decref(root);
866 return res;
869 if (showCBOR) {
870 PrintAndLogEx(SUCCESS, "CBOR get assertion request:");
871 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
872 TinyCborPrintFIDOPackage(fido2CmdGetAssertion, false, data, datalen);
873 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
876 res = FIDO2GetAssertion(data, datalen, buf, sizeof(buf), &len, &sw);
877 DropField();
878 if (res) {
879 PrintAndLogEx(ERR, "Can't execute get assertion command. res=%x. Exit...", res);
880 json_decref(root);
881 return res;
884 if (sw != ISO7816_OK) {
885 PrintAndLogEx(ERR, "ERROR execute get assertion command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
886 json_decref(root);
887 return PM3_ESOFT;
890 if (buf[0]) {
891 PrintAndLogEx(ERR, "FIDO2 get assertion error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
892 json_decref(root);
893 return PM3_ESOFT;
896 PrintAndLogEx(SUCCESS, "GetAssertion result (%zu b) OK.", len);
897 if (showCBOR) {
898 PrintAndLogEx(SUCCESS, "CBOR get assertion response:");
899 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
900 TinyCborPrintFIDOPackage(fido2CmdGetAssertion, true, &buf[1], len - 1);
901 PrintAndLogEx(INFO, "---------------- " _CYAN_("CBOR") " ------------------");
904 // parse returned cbor
905 FIDO2GetAssertionParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR);
907 res = saveFileJSONrootEx(filename, root, JSON_INDENT(2), verbose, true, spDump);
908 (void)res;
909 json_decref(root);
910 return res;
913 static command_t CommandTable[] = {
914 {"help", CmdHelp, AlwaysAvailable, "This help."},
915 {"list", CmdHFFidoList, AlwaysAvailable, "List ISO 14443A history"},
916 {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Info about FIDO tag."},
917 {"reg", CmdHFFidoRegister, IfPm3Iso14443a, "FIDO U2F Registration Message."},
918 {"auth", CmdHFFidoAuthenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."},
919 {"make", CmdHFFido2MakeCredential, IfPm3Iso14443a, "FIDO2 MakeCredential command."},
920 {"assert", CmdHFFido2GetAssertion, IfPm3Iso14443a, "FIDO2 GetAssertion command."},
921 {NULL, NULL, 0, NULL}
924 int CmdHFFido(const char *Cmd) {
925 clearCommandBuffer();
926 return CmdsParse(CommandTable, Cmd);
929 int CmdHelp(const char *Cmd) {
930 (void)Cmd; // Cmd is not used so far
931 CmdsHelp(CommandTable);
932 return PM3_SUCCESS;