]> git.zerfleddert.de Git - proxmark3-svn/blame - client/cmdhffido.c
Get rid of polarssl (#717)
[proxmark3-svn] / client / cmdhffido.c
CommitLineData
39cc1c87
OM
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 "comms.h"
32#include "cmdmain.h"
33#include "util.h"
34#include "ui.h"
35#include "proxmark3.h"
36#include "cmdhf14a.h"
37#include "mifare.h"
38#include "emv/emvcore.h"
39#include "emv/emvjson.h"
40#include "emv/dump.h"
41#include "cliparser/cliparser.h"
42
43static int CmdHelp(const char *Cmd);
44
45int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
46 uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
47
48 return EMVSelect(ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
49}
50
51int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
52 int res = EMVExchange(true, apdu, Result, MaxResultLen, ResultLen, sw, NULL);
53 if (res == 5) // apdu result (sw) not a 0x9000
54 res = 0;
55 // software chaining
56 while (!res && (*sw >> 8) == 0x61) {
57 size_t oldlen = *ResultLen;
58 res = EMVExchange(true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL);
59 if (res == 5) // apdu result (sw) not a 0x9000
60 res = 0;
61
62 *ResultLen += oldlen;
63 if (*ResultLen > MaxResultLen)
64 return 100;
65 }
66 return res;
67}
68
69int FIDORegister(uint8_t *params, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
70 return FIDOExchange((sAPDU){0x00, 0x01, 0x03, 0x00, 64, params}, Result, MaxResultLen, ResultLen, sw);
71}
72
73int FIDOAuthentication(uint8_t *params, uint8_t paramslen, uint8_t controlb, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
74 return FIDOExchange((sAPDU){0x00, 0x02, controlb, 0x00, paramslen, params}, Result, MaxResultLen, ResultLen, sw);
75}
76
77int FIDO2GetInfo(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
78 uint8_t data[] = {0x04};
79 return FIDOExchange((sAPDU){0x80, 0x10, 0x00, 0x00, sizeof(data), data}, Result, MaxResultLen, ResultLen, sw);
80}
81
82int CmdHFFidoInfo(const char *cmd) {
83
84 if (cmd && strlen(cmd) > 0)
85 PrintAndLog("WARNING: command don't have any parameters.\n");
86
87 // info about 14a part
88 CmdHF14AInfo("");
89
90 // FIDO info
91 PrintAndLog("--------------------------------------------");
92 SetAPDULogging(false);
93
94 uint8_t buf[APDU_RES_LEN] = {0};
95 size_t len = 0;
96 uint16_t sw = 0;
97 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
98
99 if (res) {
100 DropField();
101 return res;
102 }
103
104 if (sw != 0x9000) {
105 if (sw)
106 PrintAndLog("Not a FIDO card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
107 else
108 PrintAndLog("APDU exchange error. Card returns 0x0000.");
109
110 DropField();
111 return 0;
112 }
113
114 if (!strncmp((char *)buf, "U2F_V2", 7)) {
115 if (!strncmp((char *)buf, "FIDO_2_0", 8)) {
116 PrintAndLog("FIDO2 authenricator detected. Version: %.*s", len, buf);
117 } else {
118 PrintAndLog("FIDO authenricator detected (not standard U2F).");
119 PrintAndLog("Non U2F authenticator version:");
120 dump_buffer((const unsigned char *)buf, len, NULL, 0);
121 }
122 } else {
123 PrintAndLog("FIDO U2F authenricator detected. Version: %.*s", len, buf);
124 }
125
126 res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw);
127 DropField();
128 if (res) {
129 return res;
130 }
131 if (sw != 0x9000) {
132 PrintAndLog("FIDO2 version not exists (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
133
134 return 0;
135 }
136
137 PrintAndLog("FIDO2 version: (%d)", len);
138 dump_buffer((const unsigned char *)buf, len, NULL, 0);
139
140 return 0;
141}
142
143json_t *OpenJson(int paramnum, char *fname, void* argtable[], bool *err) {
144 json_t *root = NULL;
145 json_error_t error;
146 *err = false;
147
148 uint8_t jsonname[250] ={0};
149 char *cjsonname = (char *)jsonname;
150 int jsonnamelen = 0;
151
152 // CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
153 if (CLIParamStrToBuf(arg_get_str(paramnum), jsonname, sizeof(jsonname), &jsonnamelen)) {
154 CLIParserFree();
155 return NULL;
156 }
157
158 // current path + file name
159 if (!strstr(cjsonname, ".json"))
160 strcat(cjsonname, ".json");
161
162 if (jsonnamelen) {
163 strcpy(fname, get_my_executable_directory());
164 strcat(fname, cjsonname);
165 if (access(fname, F_OK) != -1) {
166 root = json_load_file(fname, 0, &error);
167 if (!root) {
168 PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
169 *err = true;
170 return NULL;
171 }
172
173 if (!json_is_object(root)) {
174 PrintAndLog("ERROR: Invalid json format. root must be an object.");
175 json_decref(root);
176 *err = true;
177 return NULL;
178 }
179
180 } else {
181 root = json_object();
182 }
183 }
184 return root;
185}
186
187int CmdHFFidoRegister(const char *cmd) {
188 uint8_t data[64] = {0};
189 int chlen = 0;
190 uint8_t cdata[250] = {0};
191 int applen = 0;
192 uint8_t adata[250] = {0};
193 json_t *root = NULL;
194
195 CLIParserInit("hf fido reg",
196 "Initiate a U2F token registration. Needs two 32-byte hash number. \nchallenge parameter (32b) and application parameter (32b).",
197 "Usage:\n\thf fido reg -> execute command with 2 parameters, filled 0x00\n"
198 "\thf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"
199 "\thf fido reg -p s0 s1 -> execute command with plain parameters");
200
201 void* argtable[] = {
202 arg_param_begin,
203 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
204 arg_lit0("vV", "verbose", "show technical data"),
205 arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
206 arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
207 arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
208 arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
209 arg_param_end
210 };
211 CLIExecWithReturn(cmd, argtable, true);
212
213 bool APDULogging = arg_get_lit(1);
214 bool verbose = arg_get_lit(2);
215 bool paramsPlain = arg_get_lit(3);
216
217 char fname[250] = {0};
218 bool err;
219 root = OpenJson(4, fname, argtable, &err);
220 if(err)
221 return 1;
222 if (root) {
223 size_t jlen;
224 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
225 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
226 }
227
228 if (paramsPlain) {
229 memset(cdata, 0x00, 32);
230 CLIGetStrWithReturn(5, cdata, &chlen);
231 if (chlen && chlen > 16) {
232 PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen);
233 return 1;
234 }
235 } else {
236 CLIGetHexWithReturn(5, cdata, &chlen);
237 if (chlen && chlen != 32) {
238 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
239 return 1;
240 }
241 }
242 if (chlen)
243 memmove(data, cdata, 32);
244
245
246 if (paramsPlain) {
247 memset(adata, 0x00, 32);
248 CLIGetStrWithReturn(6, adata, &applen);
249 if (applen && applen > 16) {
250 PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen);
251 return 1;
252 }
253 } else {
254 CLIGetHexWithReturn(6, adata, &applen);
255 if (applen && applen != 32) {
256 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
257 return 1;
258 }
259 }
260 if (applen)
261 memmove(&data[32], adata, 32);
262
263 CLIParserFree();
264
265 SetAPDULogging(APDULogging);
266
267 // 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
268 // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
269
270 uint8_t buf[2048] = {0};
271 size_t len = 0;
272 uint16_t sw = 0;
273
274 DropField();
275 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
276
277 if (res) {
278 PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
279 DropField();
280 return res;
281 }
282
283 if (sw != 0x9000) {
284 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
285 DropField();
286 return 2;
287 }
288
289 res = FIDORegister(data, buf, sizeof(buf), &len, &sw);
290 DropField();
291 if (res) {
292 PrintAndLog("Can't execute register command. res=%x. Exit...", res);
293 return res;
294 }
295
296 if (sw != 0x9000) {
297 PrintAndLog("ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
298 return 3;
299 }
300
301 PrintAndLog("");
302 if (APDULogging)
303 PrintAndLog("---------------------------------------------------------------");
304 PrintAndLog("data len: %d", len);
305 if (verbose) {
306 PrintAndLog("--------------data----------------------");
307 dump_buffer((const unsigned char *)buf, len, NULL, 0);
308 PrintAndLog("--------------data----------------------");
309 }
310
311 if (buf[0] != 0x05) {
312 PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf[0]);
313 return 5;
314 }
315 PrintAndLog("User public key: %s", sprint_hex(&buf[1], 65));
316
317 uint8_t keyHandleLen = buf[66];
318 PrintAndLog("Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen));
319
320 int derp = 67 + keyHandleLen;
321 int derLen = (buf[derp + 2] << 8) + buf[derp + 3] + 4;
322 // needs to decode DER certificate
323 if (verbose) {
324 PrintAndLog("DER certificate[%d]:------------------DER-------------------", derLen);
325 dump_buffer_simple((const unsigned char *)&buf[67 + keyHandleLen], derLen, NULL);
326 PrintAndLog("\n----------------DER---------------------");
327 } else {
328 PrintAndLog("DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20));
329 }
330
331
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
337 PrintAndLog("\nauth command: ");
338 printf("hf fido auth %s%s", paramsPlain?"-p ":"", sprint_hex_inrow(&buf[67], keyHandleLen));
339 if(chlen || applen)
340 printf(" %s", paramsPlain?(char *)cdata:sprint_hex_inrow(cdata, 32));
341 if(applen)
342 printf(" %s", paramsPlain?(char *)adata:sprint_hex_inrow(adata, 32));
343 printf("\n");
344
345 if (root) {
346 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
347 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
348 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
349 JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen);
350 JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen);
351
352 res = json_dump_file(root, fname, JSON_INDENT(2));
353 if (res) {
354 PrintAndLog("ERROR: can't save the file: %s", fname);
355 return 200;
356 }
357 PrintAndLog("File `%s` saved.", fname);
358
359 // free json object
360 json_decref(root);
361 }
362
363 return 0;
364};
365
366int CmdHFFidoAuthenticate(const char *cmd) {
367 uint8_t data[512] = {0};
368 uint8_t hdata[250] = {0};
369 int hdatalen = 0;
370 uint8_t keyHandleLen = 0;
371 json_t *root = NULL;
372
373 CLIParserInit("hf fido auth",
374 "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).",
375 "Usage:\n\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
376 "\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
377 "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
378
379 void* argtable[] = {
380 arg_param_begin,
381 arg_lit0("aA", "apdu", "show APDU reqests and responses"),
382 arg_lit0("vV", "verbose", "show technical data"),
383 arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
384 arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
385 arg_lit0("uU", "user", "mode: enforce-user-presence-and-sign"),
386 arg_lit0("cC", "check", "mode: check-only"),
387 arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
388 arg_str0(NULL, NULL, "<HEX key handle (var 0..255b)>", NULL),
389 arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
390 arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
391 arg_param_end
392 };
393 CLIExecWithReturn(cmd, argtable, true);
394
395 bool APDULogging = arg_get_lit(1);
396 //bool verbose = arg_get_lit(2);
397 bool paramsPlain = arg_get_lit(3);
398 uint8_t controlByte = 0x08;
399 if (arg_get_lit(5))
400 controlByte = 0x03;
401 if (arg_get_lit(6))
402 controlByte = 0x07;
403
404 char fname[250] = {0};
405 bool err;
406 root = OpenJson(7, fname, argtable, &err);
407 if(err)
408 return 1;
409 if (root) {
410 size_t jlen;
411 JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
412 JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
413 JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen);
414 keyHandleLen = jlen & 0xff;
415 data[64] = keyHandleLen;
416 }
417
418 CLIGetHexWithReturn(8, hdata, &hdatalen);
419 if (hdatalen > 255) {
420 PrintAndLog("ERROR: application parameter length must be less than 255.");
421 return 1;
422 }
423 if (hdatalen) {
424 keyHandleLen = hdatalen;
425 data[64] = keyHandleLen;
426 memmove(&data[65], hdata, keyHandleLen);
427 }
428
429 if (paramsPlain) {
430 memset(hdata, 0x00, 32);
431 CLIGetStrWithReturn(9, hdata, &hdatalen);
432 if (hdatalen && hdatalen > 16) {
433 PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
434 return 1;
435 }
436 } else {
437 CLIGetHexWithReturn(9, hdata, &hdatalen);
438 if (hdatalen && hdatalen != 32) {
439 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
440 return 1;
441 }
442 }
443 if (hdatalen)
444 memmove(data, hdata, 32);
445
446 if (paramsPlain) {
447 memset(hdata, 0x00, 32);
448 CLIGetStrWithReturn(10, hdata, &hdatalen);
449 if (hdatalen && hdatalen > 16) {
450 PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
451 return 1;
452 }
453 } else {
454 CLIGetHexWithReturn(10, hdata, &hdatalen);
455 if (hdatalen && hdatalen != 32) {
456 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
457 return 1;
458 }
459 }
460 if (hdatalen)
461 memmove(&data[32], hdata, 32);
462
463 CLIParserFree();
464
465 SetAPDULogging(APDULogging);
466
467 // (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
468 // challenge parameter [32 bytes]
469 // application parameter [32 bytes]
470 // key handle length [1b] = N
471 // key handle [N]
472
473 uint8_t datalen = 32 + 32 + 1 + keyHandleLen;
474
475 uint8_t buf[2048] = {0};
476 size_t len = 0;
477 uint16_t sw = 0;
478
479 DropField();
480 int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
481
482 if (res) {
483 PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
484 DropField();
485 return res;
486 }
487
488 if (sw != 0x9000) {
489 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
490 DropField();
491 return 2;
492 }
493
494 res = FIDOAuthentication(data, datalen, controlByte, buf, sizeof(buf), &len, &sw);
495 DropField();
496 if (res) {
497 PrintAndLog("Can't execute authentication command. res=%x. Exit...", res);
498 return res;
499 }
500
501 if (sw != 0x9000) {
502 PrintAndLog("ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
503 return 3;
504 }
505
506 PrintAndLog("---------------------------------------------------------------");
507 PrintAndLog("User presence: %s", (buf[0]?"verified":"not verified"));
508 uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4);
509 PrintAndLog("Counter: %d", cntr);
510 PrintAndLog("Hash[%d]: %s", len - 5, sprint_hex(&buf[5], len - 5));
511
512 if (root) {
513 JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
514 JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
515 JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
516 JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen);
517 JsonSaveInt(root, "Counter", cntr);
518
519 res = json_dump_file(root, fname, JSON_INDENT(2));
520 if (res) {
521 PrintAndLog("ERROR: can't save the file: %s", fname);
522 return 200;
523 }
524 PrintAndLog("File `%s` saved.", fname);
525
526 // free json object
527 json_decref(root);
528 }
529 return 0;
530};
531
532static command_t CommandTable[] =
533{
534 {"help", CmdHelp, 1, "This help."},
535 {"info", CmdHFFidoInfo, 0, "Info about FIDO tag."},
536 {"reg", CmdHFFidoRegister, 0, "FIDO U2F Registration Message."},
537 {"auth", CmdHFFidoAuthenticate, 0, "FIDO U2F Authentication Message."},
538 {NULL, NULL, 0, NULL}
539};
540
541int CmdHFFido(const char *Cmd) {
542 (void)WaitForResponseTimeout(CMD_ACK,NULL,100);
543 CmdsParse(CommandTable, Cmd);
544 return 0;
545}
546
547int CmdHelp(const char *Cmd) {
548 CmdsHelp(CommandTable);
549 return 0;
550}
Impressum, Datenschutz