fd97ace5419a5788065649ab14f0dbb03bacd2e0
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 Merlok
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
7 //-----------------------------------------------------------------------------
8 // High frequency MIFARE Plus commands
9 //-----------------------------------------------------------------------------
11 // Documentation here:
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 //-----------------------------------------------------------------------------
22 #include "cmdhffido.h"
35 #include "proxmark3.h"
38 #include "emv/emvcore.h"
39 #include "emv/emvjson.h"
41 #include "cliparser/cliparser.h"
43 static int CmdHelp(const char *Cmd
);
45 int 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};
48 return EMVSelect(ActivateField
, LeaveFieldON
, data
, sizeof(data
), Result
, MaxResultLen
, ResultLen
, sw
, NULL
);
51 int 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
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
63 if (*ResultLen
> MaxResultLen
)
69 int 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
);
73 int 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
);
77 int 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
);
82 int CmdHFFidoInfo(const char *cmd
) {
84 if (cmd
&& strlen(cmd
) > 0)
85 PrintAndLog("WARNING: command don't have any parameters.\n");
87 // info about 14a part
91 PrintAndLog("--------------------------------------------");
92 SetAPDULogging(false);
94 uint8_t buf
[APDU_RES_LEN
] = {0};
97 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
106 PrintAndLog("Not a FIDO card! APDU response: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
108 PrintAndLog("APDU exchange error. Card returns 0x0000.");
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
);
118 PrintAndLog("FIDO authenricator detected (not standard U2F).");
119 PrintAndLog("Non U2F authenticator version:");
120 dump_buffer((const unsigned char *)buf
, len
, NULL
, 0);
123 PrintAndLog("FIDO U2F authenricator detected. Version: %.*s", len
, buf
);
126 res
= FIDO2GetInfo(buf
, sizeof(buf
), &len
, &sw
);
132 PrintAndLog("FIDO2 version not exists (%04x - %s).", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
137 PrintAndLog("FIDO2 version: (%d)", len
);
138 dump_buffer((const unsigned char *)buf
, len
, NULL
, 0);
143 json_t
*OpenJson(int paramnum
, char *fname
, void* argtable
[], bool *err
) {
148 uint8_t jsonname
[250] ={0};
149 char *cjsonname
= (char *)jsonname
;
152 // CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
153 if (CLIParamStrToBuf(arg_get_str(paramnum
), jsonname
, sizeof(jsonname
), &jsonnamelen
)) {
158 // current path + file name
159 if (!strstr(cjsonname
, ".json"))
160 strcat(cjsonname
, ".json");
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
);
168 PrintAndLog("ERROR: json error on line %d: %s", error
.line
, error
.text
);
173 if (!json_is_object(root
)) {
174 PrintAndLog("ERROR: Invalid json format. root must be an object.");
181 root
= json_object();
187 int CmdHFFidoRegister(const char *cmd
) {
188 uint8_t data
[64] = {0};
190 uint8_t cdata
[250] = {0};
192 uint8_t adata
[250] = {0};
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");
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
),
211 CLIExecWithReturn(cmd
, argtable
, true);
213 bool APDULogging
= arg_get_lit(1);
214 bool verbose
= arg_get_lit(2);
215 bool paramsPlain
= arg_get_lit(3);
217 char fname
[250] = {0};
219 root
= OpenJson(4, fname
, argtable
, &err
);
224 JsonLoadBufAsHex(root
, "$.ChallengeParam", data
, 32, &jlen
);
225 JsonLoadBufAsHex(root
, "$.ApplicationParam", &data
[32], 32, &jlen
);
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
);
236 CLIGetHexWithReturn(5, cdata
, &chlen
);
237 if (chlen
&& chlen
!= 32) {
238 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
243 memmove(data
, cdata
, 32);
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
);
254 CLIGetHexWithReturn(6, adata
, &applen
);
255 if (applen
&& applen
!= 32) {
256 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
261 memmove(&data
[32], adata
, 32);
265 SetAPDULogging(APDULogging
);
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
270 uint8_t buf
[2048] = {0};
275 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
278 PrintAndLog("Can't select authenticator. res=%x. Exit...", res
);
284 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
289 res
= FIDORegister(data
, buf
, sizeof(buf
), &len
, &sw
);
292 PrintAndLog("Can't execute register command. res=%x. Exit...", res
);
297 PrintAndLog("ERROR execute register command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
303 PrintAndLog("---------------------------------------------------------------");
304 PrintAndLog("data len: %d", len
);
306 PrintAndLog("--------------data----------------------");
307 dump_buffer((const unsigned char *)buf
, len
, NULL
, 0);
308 PrintAndLog("--------------data----------------------");
311 if (buf
[0] != 0x05) {
312 PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf
[0]);
315 PrintAndLog("User public key: %s", sprint_hex(&buf
[1], 65));
317 uint8_t keyHandleLen
= buf
[66];
318 PrintAndLog("Key handle[%d]: %s", keyHandleLen
, sprint_hex(&buf
[67], keyHandleLen
));
320 int derp
= 67 + keyHandleLen
;
321 int derLen
= (buf
[derp
+ 2] << 8) + buf
[derp
+ 3] + 4;
322 // needs to decode DER certificate
324 PrintAndLog("DER certificate[%d]:------------------DER-------------------", derLen
);
325 dump_buffer_simple((const unsigned char *)&buf
[67 + keyHandleLen
], derLen
, NULL
);
326 PrintAndLog("\n----------------DER---------------------");
328 PrintAndLog("DER certificate[%d]: %s...", derLen
, sprint_hex(&buf
[derp
], 20));
332 int hashp
= 1 + 65 + 1 + keyHandleLen
+ derLen
;
333 PrintAndLog("Hash[%d]: %s", len
- hashp
, sprint_hex(&buf
[hashp
], len
- hashp
));
335 // check ANSI X9.62 format ECDSA signature (on P-256)
337 PrintAndLog("\nauth command: ");
338 printf("hf fido auth %s%s", paramsPlain
?"-p ":"", sprint_hex_inrow(&buf
[67], keyHandleLen
));
340 printf(" %s", paramsPlain
?(char *)cdata
:sprint_hex_inrow(cdata
, 32));
342 printf(" %s", paramsPlain
?(char *)adata
:sprint_hex_inrow(adata
, 32));
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
);
352 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
354 PrintAndLog("ERROR: can't save the file: %s", fname
);
357 PrintAndLog("File `%s` saved.", fname
);
366 int CmdHFFidoAuthenticate(const char *cmd
) {
367 uint8_t data
[512] = {0};
368 uint8_t hdata
[250] = {0};
370 uint8_t keyHandleLen
= 0;
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");
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
),
393 CLIExecWithReturn(cmd
, argtable
, true);
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;
404 char fname
[250] = {0};
406 root
= OpenJson(7, fname
, argtable
, &err
);
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
;
418 CLIGetHexWithReturn(8, hdata
, &hdatalen
);
419 if (hdatalen
> 255) {
420 PrintAndLog("ERROR: application parameter length must be less than 255.");
424 keyHandleLen
= hdatalen
;
425 data
[64] = keyHandleLen
;
426 memmove(&data
[65], hdata
, keyHandleLen
);
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
);
437 CLIGetHexWithReturn(9, hdata
, &hdatalen
);
438 if (hdatalen
&& hdatalen
!= 32) {
439 PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
444 memmove(data
, hdata
, 32);
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
);
454 CLIGetHexWithReturn(10, hdata
, &hdatalen
);
455 if (hdatalen
&& hdatalen
!= 32) {
456 PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
461 memmove(&data
[32], hdata
, 32);
465 SetAPDULogging(APDULogging
);
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
473 uint8_t datalen
= 32 + 32 + 1 + keyHandleLen
;
475 uint8_t buf
[2048] = {0};
480 int res
= FIDOSelect(true, true, buf
, sizeof(buf
), &len
, &sw
);
483 PrintAndLog("Can't select authenticator. res=%x. Exit...", res
);
489 PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
494 res
= FIDOAuthentication(data
, datalen
, controlByte
, buf
, sizeof(buf
), &len
, &sw
);
497 PrintAndLog("Can't execute authentication command. res=%x. Exit...", res
);
502 PrintAndLog("ERROR execute authentication command. APDU response status: %04x - %s", sw
, GetAPDUCodeDescription(sw
>> 8, sw
& 0xff));
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));
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
);
519 res
= json_dump_file(root
, fname
, JSON_INDENT(2));
521 PrintAndLog("ERROR: can't save the file: %s", fname
);
524 PrintAndLog("File `%s` saved.", fname
);
532 static command_t CommandTable
[] =
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
}
541 int CmdHFFido(const char *Cmd
) {
542 (void)WaitForResponseTimeout(CMD_ACK
,NULL
,100);
543 CmdsParse(CommandTable
, Cmd
);
547 int CmdHelp(const char *Cmd
) {
548 CmdsHelp(CommandTable
);