8a98cf19fdfdf41bd513b4c4bac3966e4a8c1df3
[proxmark3-svn] / client / cmdhffido.c
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 //-----------------------------------------------------------------------------
10 //
11 // Documentation here:
12 //
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 //-----------------------------------------------------------------------------
20
21
22 #include "cmdhffido.h"
23
24 #include <inttypes.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <jansson.h>
31 #include <mbedtls/x509_crt.h>
32 #include <mbedtls/x509.h>
33 #include <mbedtls/pk.h>
34 #include "comms.h"
35 #include "cmdmain.h"
36 #include "util.h"
37 #include "ui.h"
38 #include "proxmark3.h"
39 #include "mifare.h"
40 #include "emv/emvcore.h"
41 #include "emv/emvjson.h"
42 #include "emv/dump.h"
43 #include "emv/apduinfo.h"
44 #include "cliparser/cliparser.h"
45 #include "crypto/asn1utils.h"
46 #include "crypto/libpcrypto.h"
47 #include "fido/cbortools.h"
48 #include "fido/fidocore.h"
49 #include "fido/cose.h"
50
51 static int CmdHelp(const char *Cmd);
52
53 int CmdHFFidoInfo(const char *cmd) {
54
55 if (cmd && strlen(cmd) > 0)
56 PrintAndLog("WARNING: command don't have any parameters.\n");
57
58 // info about 14a part
59 CmdHF14AInfo("");
60
61 // FIDO info
62 PrintAndLog("--------------------------------------------");
63 SetAPDULogging(false);
64
65 uint8_t buf[APDU_RESPONSE_LEN] = {0};
66 size_t len = 0;
67 uint16_t sw = 0;
68 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
69
70 if (res) {
71 DropField();
72 return res;
73 }
74
75 if (sw != 0x9000) {
76 if (sw)
77 PrintAndLog("Not a FIDO card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
78 else
79 PrintAndLog("APDU exchange error. Card returns 0x0000.");
80
81 DropField();
82 return 0;
83 }
84
85 if (!strncmp((char *)buf, "U2F_V2", 7)) {
86 if (!strncmp((char *)buf, "FIDO_2_0", 8)) {
87 PrintAndLog("FIDO2 authenricator detected. Version: %.*s", len, buf);
88 } else {
89 PrintAndLog("FIDO authenricator detected (not standard U2F).");
90 PrintAndLog("Non U2F authenticator version:");
91 dump_buffer((const unsigned char *)buf, len, NULL, 0);
92 }
93 } else {
94 PrintAndLog("FIDO U2F authenricator detected. Version: %.*s", len, buf);
95 }
96
97 res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw);
98 DropField();
99 if (res) {
100 return res;
101 }
102 if (sw != 0x9000) {
103 PrintAndLog("FIDO2 version not exists (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
104
105 return 0;
106 }
107
108 if(buf[0]) {
109 PrintAndLog("FIDO2 ger version error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
110 return 0;
111 }
112
113 if (len > 1) {
114 // if (false) {
115 // PrintAndLog("FIDO2 version: (len=%d)", len);
116 // dump_buffer((const unsigned char *)buf, len, NULL, 0);
117 // }
118
119 PrintAndLog("FIDO2 version CBOR decoded:");
120 TinyCborPrintFIDOPackage(fido2CmdGetInfo, true, &buf[1], len - 1);
121 } else {
122 PrintAndLog("FIDO2 version length error");
123 }
124
125 return 0;
126 }
127
128 json_t *OpenJson(int paramnum, char *fname, void* argtable[], bool *err) {
129 json_t *root = NULL;
130 json_error_t error;
131 *err = false;
132
133 uint8_t jsonname[250] ={0};
134 char *cjsonname = (char *)jsonname;
135 int jsonnamelen = 0;
136
137 // CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
138 if (CLIParamStrToBuf(arg_get_str(paramnum), jsonname, sizeof(jsonname), &jsonnamelen)) {
139 CLIParserFree();
140 return NULL;
141 }
142
143 // current path + file name
144 if (!strstr(cjsonname, ".json"))
145 strcat(cjsonname, ".json");
146
147 if (jsonnamelen) {
148 strcpy(fname, get_my_executable_directory());
149 strcat(fname, cjsonname);
150 if (access(fname, F_OK) != -1) {
151 root = json_load_file(fname, 0, &error);
152 if (!root) {
153 PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
154 *err = true;
155 return NULL;
156 }
157
158 if (!json_is_object(root)) {
159 PrintAndLog("ERROR: Invalid json format. root must be an object.");
160 json_decref(root);
161 *err = true;
162 return NULL;
163 }
164
165 } else {
166 root = json_object();
167 }
168 }
169 return root;
170 }
171
172 int CmdHFFidoRegister(const char *cmd) {
173 uint8_t data[64] = {0};
174 int chlen = 0;
175 uint8_t cdata[250] = {0};
176 int applen = 0;
177 uint8_t adata[250] = {0};
178 json_t *root = NULL;
179
180 CLIParserInit("hf fido reg",
181 "Initiate a U2F token registration. Needs two 32-byte hash number. \nchallenge parameter (32b) and application parameter (32b).",
182 "Usage:\n\thf fido reg -> execute command with 2 parameters, filled 0x00\n"
183 "\thf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"
184 "\thf fido reg -p s0 s1 -> execute command with plain parameters");
185
186 void* argtable[] = {
187 arg_param_begin,
188 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
189 arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
190 arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
191 arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"),
192 arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
193 arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
194 arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
195 arg_param_end
196 };
197 CLIExecWithReturn(cmd, argtable, true);
198
199 bool APDULogging = arg_get_lit(1);
200 bool verbose = arg_get_lit(2);
201 bool verbose2 = arg_get_lit(2) > 1;
202 bool paramsPlain = arg_get_lit(3);
203 bool showDERTLV = arg_get_lit(4);
204
205 char fname[250] = {0};
206 bool err;
207 root = OpenJson(5, fname, argtable, &err);
208 if(err)
209 return 1;
210 if (root) {
211 size_t jlen;
212 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
213 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
214 }
215
216 if (paramsPlain) {
217 memset(cdata, 0x00, 32);
218 CLIGetStrWithReturn(6, cdata, &chlen);
219 if (chlen && chlen > 16) {
220 PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen);
221 return 1;
222 }
223 } else {
224 CLIGetHexWithReturn(6, cdata, &chlen);
225 if (chlen && chlen != 32) {
226 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
227 return 1;
228 }
229 }
230 if (chlen)
231 memmove(data, cdata, 32);
232
233
234 if (paramsPlain) {
235 memset(adata, 0x00, 32);
236 CLIGetStrWithReturn(7, adata, &applen);
237 if (applen && applen > 16) {
238 PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen);
239 return 1;
240 }
241 } else {
242 CLIGetHexWithReturn(7, adata, &applen);
243 if (applen && applen != 32) {
244 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
245 return 1;
246 }
247 }
248 if (applen)
249 memmove(&data[32], adata, 32);
250
251 CLIParserFree();
252
253 SetAPDULogging(APDULogging);
254
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
257
258 uint8_t buf[2048] = {0};
259 size_t len = 0;
260 uint16_t sw = 0;
261
262 DropField();
263 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
264
265 if (res) {
266 PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
267 DropField();
268 return res;
269 }
270
271 if (sw != 0x9000) {
272 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
273 DropField();
274 return 2;
275 }
276
277 res = FIDORegister(data, buf, sizeof(buf), &len, &sw);
278 DropField();
279 if (res) {
280 PrintAndLog("Can't execute register command. res=%x. Exit...", res);
281 return res;
282 }
283
284 if (sw != 0x9000) {
285 PrintAndLog("ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
286 return 3;
287 }
288
289 PrintAndLog("");
290 if (APDULogging)
291 PrintAndLog("---------------------------------------------------------------");
292 PrintAndLog("data len: %d", len);
293 if (verbose2) {
294 PrintAndLog("--------------data----------------------");
295 dump_buffer((const unsigned char *)buf, len, NULL, 0);
296 PrintAndLog("--------------data----------------------");
297 }
298
299 if (buf[0] != 0x05) {
300 PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf[0]);
301 return 5;
302 }
303 PrintAndLog("User public key: %s", sprint_hex(&buf[1], 65));
304
305 uint8_t keyHandleLen = buf[66];
306 PrintAndLog("Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen));
307
308 int derp = 67 + keyHandleLen;
309 int derLen = (buf[derp + 2] << 8) + buf[derp + 3] + 4;
310 if (verbose2) {
311 PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen);
312 dump_buffer_simple((const unsigned char *)&buf[derp], derLen, NULL);
313 PrintAndLog("\n----------------DER---------------------");
314 } else {
315 if (verbose)
316 PrintAndLog("------------------DER-------------------");
317 PrintAndLog("DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20));
318 }
319
320 // check and print DER certificate
321 uint8_t public_key[65] = {0};
322
323 // print DER certificate in TLV view
324 if (showDERTLV) {
325 PrintAndLog("----------------DER TLV-----------------");
326 asn1_print(&buf[derp], derLen, " ");
327 PrintAndLog("----------------DER TLV-----------------");
328 }
329
330 FIDOCheckDERAndGetKey(&buf[derp], derLen, verbose, public_key, sizeof(public_key));
331
332 // get hash
333 int hashp = 1 + 65 + 1 + keyHandleLen + derLen;
334 PrintAndLog("Hash[%d]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp));
335
336 // check ANSI X9.62 format ECDSA signature (on P-256)
337 uint8_t rval[300] = {0};
338 uint8_t sval[300] = {0};
339 res = ecdsa_asn1_get_signature(&buf[hashp], len - hashp, rval, sval);
340 if (!res) {
341 if (verbose) {
342 PrintAndLog(" r: %s", sprint_hex(rval, 32));
343 PrintAndLog(" s: %s", sprint_hex(sval, 32));
344 }
345
346 uint8_t xbuf[4096] = {0};
347 size_t xbuflen = 0;
348 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
349 "\x00", 1,
350 &data[32], 32, // application parameter
351 &data[0], 32, // challenge parameter
352 &buf[67], keyHandleLen, // keyHandle
353 &buf[1], 65, // user public key
354 NULL, 0);
355 //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
356 res = ecdsa_signature_verify(public_key, xbuf, xbuflen, &buf[hashp], len - hashp);
357 if (res) {
358 if (res == -0x4e00) {
359 PrintAndLog("Signature is NOT VALID.");
360 } else {
361 PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res));
362 }
363 } else {
364 PrintAndLog("Signature is OK.");
365 }
366
367 } else {
368 PrintAndLog("Invalid signature. res=%d.", res);
369 }
370
371 PrintAndLog("\nauth command: ");
372 printf("hf fido auth %s%s", paramsPlain?"-p ":"", sprint_hex_inrow(&buf[67], keyHandleLen));
373 if(chlen || applen)
374 printf(" %s", paramsPlain?(char *)cdata:sprint_hex_inrow(cdata, 32));
375 if(applen)
376 printf(" %s", paramsPlain?(char *)adata:sprint_hex_inrow(adata, 32));
377 printf("\n");
378
379 if (root) {
380 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
381 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
382 JsonSaveBufAsHexCompact(root, "PublicKey", &buf[1], 65);
383 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
384 JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen);
385 JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen);
386
387 res = json_dump_file(root, fname, JSON_INDENT(2));
388 if (res) {
389 PrintAndLog("ERROR: can't save the file: %s", fname);
390 return 200;
391 }
392 PrintAndLog("File `%s` saved.", fname);
393
394 // free json object
395 json_decref(root);
396 }
397
398 return 0;
399 };
400
401 int CmdHFFidoAuthenticate(const char *cmd) {
402 uint8_t data[512] = {0};
403 uint8_t hdata[250] = {0};
404 bool public_key_loaded = false;
405 uint8_t public_key[65] = {0};
406 int hdatalen = 0;
407 uint8_t keyHandleLen = 0;
408 json_t *root = NULL;
409
410 CLIParserInit("hf fido auth",
411 "Initiate a U2F token authentication. Needs key handle and two 32-byte hash number. \nkey handle(var 0..255), challenge parameter (32b) and application parameter (32b).",
412 "Usage:\n\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
413 "\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
414 "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
415
416 void* argtable[] = {
417 arg_param_begin,
418 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
419 arg_lit0("vV", "verbose", "show technical data"),
420 arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
421 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
422 arg_lit0("uU", "user", "mode: enforce-user-presence-and-sign"),
423 arg_lit0("cC", "check", "mode: check-only"),
424 arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
425 arg_str0("kK", "key", "public key to verify signature", NULL),
426 arg_str0(NULL, NULL, "<HEX key handle (var 0..255b)>", NULL),
427 arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
428 arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
429 arg_param_end
430 };
431 CLIExecWithReturn(cmd, argtable, true);
432
433 bool APDULogging = arg_get_lit(1);
434 bool verbose = arg_get_lit(2);
435 bool paramsPlain = arg_get_lit(3);
436 uint8_t controlByte = 0x08;
437 if (arg_get_lit(5))
438 controlByte = 0x03;
439 if (arg_get_lit(6))
440 controlByte = 0x07;
441
442 char fname[250] = {0};
443 bool err;
444 root = OpenJson(7, fname, argtable, &err);
445 if(err)
446 return 1;
447 if (root) {
448 size_t jlen;
449 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
450 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
451 JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen);
452 keyHandleLen = jlen & 0xff;
453 data[64] = keyHandleLen;
454 JsonLoadBufAsHex(root, "$.PublicKey", public_key, 65, &jlen);
455 public_key_loaded = (jlen > 0);
456 }
457
458 // public key
459 CLIGetHexWithReturn(8, hdata, &hdatalen);
460 if (hdatalen && hdatalen != 65) {
461 PrintAndLog("ERROR: public key length must be 65 bytes only.");
462 return 1;
463 }
464 if (hdatalen) {
465 memmove(public_key, hdata, hdatalen);
466 public_key_loaded = true;
467 }
468
469 CLIGetHexWithReturn(9, hdata, &hdatalen);
470 if (hdatalen > 255) {
471 PrintAndLog("ERROR: application parameter length must be less than 255.");
472 return 1;
473 }
474 if (hdatalen) {
475 keyHandleLen = hdatalen;
476 data[64] = keyHandleLen;
477 memmove(&data[65], hdata, keyHandleLen);
478 }
479
480 if (paramsPlain) {
481 memset(hdata, 0x00, 32);
482 CLIGetStrWithReturn(9, hdata, &hdatalen);
483 if (hdatalen && hdatalen > 16) {
484 PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
485 return 1;
486 }
487 } else {
488 CLIGetHexWithReturn(10, hdata, &hdatalen);
489 if (hdatalen && hdatalen != 32) {
490 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
491 return 1;
492 }
493 }
494 if (hdatalen)
495 memmove(data, hdata, 32);
496
497 if (paramsPlain) {
498 memset(hdata, 0x00, 32);
499 CLIGetStrWithReturn(11, hdata, &hdatalen);
500 if (hdatalen && hdatalen > 16) {
501 PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
502 return 1;
503 }
504 } else {
505 CLIGetHexWithReturn(10, hdata, &hdatalen);
506 if (hdatalen && hdatalen != 32) {
507 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
508 return 1;
509 }
510 }
511 if (hdatalen)
512 memmove(&data[32], hdata, 32);
513
514 CLIParserFree();
515
516 SetAPDULogging(APDULogging);
517
518 // (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
519 // challenge parameter [32 bytes]
520 // application parameter [32 bytes]
521 // key handle length [1b] = N
522 // key handle [N]
523
524 uint8_t datalen = 32 + 32 + 1 + keyHandleLen;
525
526 uint8_t buf[2048] = {0};
527 size_t len = 0;
528 uint16_t sw = 0;
529
530 DropField();
531 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
532
533 if (res) {
534 PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
535 DropField();
536 return res;
537 }
538
539 if (sw != 0x9000) {
540 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
541 DropField();
542 return 2;
543 }
544
545 res = FIDOAuthentication(data, datalen, controlByte, buf, sizeof(buf), &len, &sw);
546 DropField();
547 if (res) {
548 PrintAndLog("Can't execute authentication command. res=%x. Exit...", res);
549 return res;
550 }
551
552 if (sw != 0x9000) {
553 PrintAndLog("ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
554 return 3;
555 }
556
557 PrintAndLog("---------------------------------------------------------------");
558 PrintAndLog("User presence: %s", (buf[0]?"verified":"not verified"));
559 uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4);
560 PrintAndLog("Counter: %d", cntr);
561 PrintAndLog("Hash[%d]: %s", len - 5, sprint_hex(&buf[5], len - 5));
562
563 // check ANSI X9.62 format ECDSA signature (on P-256)
564 uint8_t rval[300] = {0};
565 uint8_t sval[300] = {0};
566 res = ecdsa_asn1_get_signature(&buf[5], len - 5, rval, sval);
567 if (!res) {
568 if (verbose) {
569 PrintAndLog(" r: %s", sprint_hex(rval, 32));
570 PrintAndLog(" s: %s", sprint_hex(sval, 32));
571 }
572 if (public_key_loaded) {
573 uint8_t xbuf[4096] = {0};
574 size_t xbuflen = 0;
575 res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
576 &data[32], 32, // application parameter
577 &buf[0], 1, // user presence
578 &buf[1], 4, // counter
579 data, 32, // challenge parameter
580 NULL, 0);
581 //PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
582 res = ecdsa_signature_verify(public_key, xbuf, xbuflen, &buf[5], len - 5);
583 if (res) {
584 if (res == -0x4e00) {
585 PrintAndLog("Signature is NOT VALID.");
586 } else {
587 PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res));
588 }
589 } else {
590 PrintAndLog("Signature is OK.");
591 }
592 } else {
593 PrintAndLog("No public key provided. can't check signature.");
594 }
595 } else {
596 PrintAndLog("Invalid signature. res=%d.", res);
597 }
598
599 if (root) {
600 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
601 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
602 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
603 JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen);
604 JsonSaveInt(root, "Counter", cntr);
605
606 res = json_dump_file(root, fname, JSON_INDENT(2));
607 if (res) {
608 PrintAndLog("ERROR: can't save the file: %s", fname);
609 return 200;
610 }
611 PrintAndLog("File `%s` saved.", fname);
612
613 // free json object
614 json_decref(root);
615 }
616 return 0;
617 };
618
619 void CheckSlash(char *fileName) {
620 if ((fileName[strlen(fileName) - 1] != '/') &&
621 (fileName[strlen(fileName) - 1] != '\\'))
622 strcat(fileName, "/");
623 }
624
625 int GetExistsFileNameJson(char *prefixDir, char *reqestedFileName, char *fileName) {
626 fileName[0] = 0x00;
627 strcpy(fileName, get_my_executable_directory());
628 CheckSlash(fileName);
629
630 strcat(fileName, prefixDir);
631 CheckSlash(fileName);
632
633 strcat(fileName, reqestedFileName);
634 if (!strstr(fileName, ".json"))
635 strcat(fileName, ".json");
636
637 if (access(fileName, F_OK) < 0) {
638 strcpy(fileName, get_my_executable_directory());
639 CheckSlash(fileName);
640
641 strcat(fileName, reqestedFileName);
642 if (!strstr(fileName, ".json"))
643 strcat(fileName, ".json");
644
645 if (access(fileName, F_OK) < 0) {
646 return 1; // file not found
647 }
648 }
649 return 0;
650 }
651
652 int CmdHFFido2MakeCredential(const char *cmd) {
653 json_error_t error;
654 json_t *root = NULL;
655 char fname[300] = {0};
656
657 CLIParserInit("hf fido make",
658 "Execute a FIDO2 Make Credentional command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.",
659 "Usage:\n\thf fido make -> execute command default parameters file `fido2.json`\n"
660 "\thf fido make test.json -> execute command with parameters file `text.json`");
661
662 void* argtable[] = {
663 arg_param_begin,
664 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
665 arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
666 arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"),
667 arg_lit0("cC", "cbor", "show CBOR decoded data"),
668 arg_str0(NULL, NULL, "<json file name>", "JSON input / output file name for parameters. Default `fido2.json`"),
669 arg_param_end
670 };
671 CLIExecWithReturn(cmd, argtable, true);
672
673 bool APDULogging = arg_get_lit(1);
674 bool verbose = arg_get_lit(2);
675 bool verbose2 = arg_get_lit(2) > 1;
676 bool showDERTLV = arg_get_lit(3);
677 bool showCBOR = arg_get_lit(4);
678
679 uint8_t jsonname[250] ={0};
680 char *cjsonname = (char *)jsonname;
681 int jsonnamelen = 0;
682 CLIGetStrWithReturn(5, jsonname, &jsonnamelen);
683
684 if (!jsonnamelen) {
685 strcat(cjsonname, "fido2");
686 jsonnamelen = strlen(cjsonname);
687 }
688
689 CLIParserFree();
690
691 SetAPDULogging(APDULogging);
692
693 int res = GetExistsFileNameJson("fido", cjsonname, fname);
694 if(res) {
695 PrintAndLog("ERROR: Can't found the json file.");
696 return res;
697 }
698 PrintAndLog("fname: %s\n", fname);
699 root = json_load_file(fname, 0, &error);
700 if (!root) {
701 PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
702 return 1;
703 }
704
705 uint8_t data[2048] = {0};
706 size_t datalen = 0;
707 uint8_t buf[2048] = {0};
708 size_t len = 0;
709 uint16_t sw = 0;
710
711 DropField();
712 res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
713
714 if (res) {
715 PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
716 DropField();
717 return res;
718 }
719
720 if (sw != 0x9000) {
721 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
722 DropField();
723 return 2;
724 }
725
726 res = FIDO2CreateMakeCredentionalReq(root, data, sizeof(data), &datalen);
727 if (res)
728 return res;
729
730 if (showCBOR) {
731 PrintAndLog("CBOR make credentional request:");
732 PrintAndLog("---------------- CBOR ------------------");
733 TinyCborPrintFIDOPackage(fido2CmdMakeCredential, false, data, datalen);
734 PrintAndLog("---------------- CBOR ------------------");
735 }
736
737 res = FIDO2MakeCredential(data, datalen, buf, sizeof(buf), &len, &sw);
738 DropField();
739 if (res) {
740 PrintAndLog("Can't execute make credential command. res=%x. Exit...", res);
741 return res;
742 }
743
744 if (sw != 0x9000) {
745 PrintAndLog("ERROR execute make credential command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
746 return 3;
747 }
748
749 if(buf[0]) {
750 PrintAndLog("FIDO2 make credential error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
751 return 0;
752 }
753
754 PrintAndLog("MakeCredential result (%d b) OK.", len);
755 if (showCBOR) {
756 PrintAndLog("CBOR make credentional response:");
757 PrintAndLog("---------------- CBOR ------------------");
758 TinyCborPrintFIDOPackage(fido2CmdMakeCredential, true, &buf[1], len - 1);
759 PrintAndLog("---------------- CBOR ------------------");
760 }
761
762 // parse returned cbor
763 FIDO2MakeCredentionalParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR, showDERTLV);
764
765 if (root) {
766 res = json_dump_file(root, fname, JSON_INDENT(2));
767 if (res) {
768 PrintAndLog("ERROR: can't save the file: %s", fname);
769 return 200;
770 }
771 PrintAndLog("File `%s` saved.", fname);
772 }
773
774 json_decref(root);
775
776 return 0;
777 };
778
779 int CmdHFFido2GetAssertion(const char *cmd) {
780 json_error_t error;
781 json_t *root = NULL;
782 char fname[300] = {0};
783
784 CLIParserInit("hf fido assert",
785 "Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.",
786 "Usage:\n\thf fido assert -> execute command default parameters file `fido2.json`\n"
787 "\thf fido assert test.json -l -> execute command with parameters file `text.json` and add to request CredentialId");
788
789 void* argtable[] = {
790 arg_param_begin,
791 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
792 arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
793 arg_lit0("cC", "cbor", "show CBOR decoded data"),
794 arg_lit0("lL", "list", "add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator don't store credential to its memory)"),
795 arg_str0(NULL, NULL, "<json file name>", "JSON input / output file name for parameters. Default `fido2.json`"),
796 arg_param_end
797 };
798 CLIExecWithReturn(cmd, argtable, true);
799
800 bool APDULogging = arg_get_lit(1);
801 bool verbose = arg_get_lit(2);
802 bool verbose2 = arg_get_lit(2) > 1;
803 bool showCBOR = arg_get_lit(3);
804 bool createAllowList = arg_get_lit(4);
805
806 uint8_t jsonname[250] ={0};
807 char *cjsonname = (char *)jsonname;
808 int jsonnamelen = 0;
809 CLIGetStrWithReturn(5, jsonname, &jsonnamelen);
810
811 if (!jsonnamelen) {
812 strcat(cjsonname, "fido2");
813 jsonnamelen = strlen(cjsonname);
814 }
815
816 CLIParserFree();
817
818 SetAPDULogging(APDULogging);
819
820 int res = GetExistsFileNameJson("fido", "fido2", fname);
821 if(res) {
822 PrintAndLog("ERROR: Can't found the json file.");
823 return res;
824 }
825 PrintAndLog("fname: %s\n", fname);
826 root = json_load_file(fname, 0, &error);
827 if (!root) {
828 PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
829 return 1;
830 }
831
832 uint8_t data[2048] = {0};
833 size_t datalen = 0;
834 uint8_t buf[2048] = {0};
835 size_t len = 0;
836 uint16_t sw = 0;
837
838 DropField();
839 res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
840
841 if (res) {
842 PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
843 DropField();
844 return res;
845 }
846
847 if (sw != 0x9000) {
848 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
849 DropField();
850 return 2;
851 }
852
853 res = FIDO2CreateGetAssertionReq(root, data, sizeof(data), &datalen, createAllowList);
854 if (res)
855 return res;
856
857 if (showCBOR) {
858 PrintAndLog("CBOR get assertion request:");
859 PrintAndLog("---------------- CBOR ------------------");
860 TinyCborPrintFIDOPackage(fido2CmdGetAssertion, false, data, datalen);
861 PrintAndLog("---------------- CBOR ------------------");
862 }
863
864 res = FIDO2GetAssertion(data, datalen, buf, sizeof(buf), &len, &sw);
865 DropField();
866 if (res) {
867 PrintAndLog("Can't execute get assertion command. res=%x. Exit...", res);
868 return res;
869 }
870
871 if (sw != 0x9000) {
872 PrintAndLog("ERROR execute get assertion command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
873 return 3;
874 }
875
876 if(buf[0]) {
877 PrintAndLog("FIDO2 get assertion error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
878 return 0;
879 }
880
881 PrintAndLog("GetAssertion result (%d b) OK.", len);
882 if (showCBOR) {
883 PrintAndLog("CBOR get assertion response:");
884 PrintAndLog("---------------- CBOR ------------------");
885 TinyCborPrintFIDOPackage(fido2CmdGetAssertion, true, &buf[1], len - 1);
886 PrintAndLog("---------------- CBOR ------------------");
887 }
888
889 // parse returned cbor
890 FIDO2GetAssertionParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR);
891
892 if (root) {
893 res = json_dump_file(root, fname, JSON_INDENT(2));
894 if (res) {
895 PrintAndLog("ERROR: can't save the file: %s", fname);
896 return 200;
897 }
898 PrintAndLog("File `%s` saved.", fname);
899 }
900
901 json_decref(root);
902
903 return 0;
904 };
905
906 static command_t CommandTable[] =
907 {
908 {"help", CmdHelp, 1, "This help."},
909 {"info", CmdHFFidoInfo, 0, "Info about FIDO tag."},
910 {"reg", CmdHFFidoRegister, 0, "FIDO U2F Registration Message."},
911 {"auth", CmdHFFidoAuthenticate, 0, "FIDO U2F Authentication Message."},
912 {"make", CmdHFFido2MakeCredential, 0, "FIDO2 MakeCredential command."},
913 {"assert", CmdHFFido2GetAssertion, 0, "FIDO2 GetAssertion command."},
914 {NULL, NULL, 0, NULL}
915 };
916
917 int CmdHFFido(const char *Cmd) {
918 (void)WaitForResponseTimeout(CMD_ACK,NULL,100);
919 CmdsParse(CommandTable, Cmd);
920 return 0;
921 }
922
923 int CmdHelp(const char *Cmd) {
924 CmdsHelp(CommandTable);
925 return 0;
926 }
Impressum, Datenschutz