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