added core files.

need to add:
1. jansson (maybe needs jansson-devel)
2. arm and client side of exchangeapdu14a
This commit is contained in:
merlokk 2018-11-08 17:29:58 +02:00
parent affee79b69
commit 4fed815b88
9 changed files with 1031 additions and 23 deletions

View file

@ -23,7 +23,7 @@ ENV_CFLAGS := $(CFLAGS)
VPATH = ../common ../zlib ../uart
OBJDIR = obj
LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm
LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm -ljansson
LUALIB = ../liblua/liblua.a
LDFLAGS = $(ENV_LDFLAGS)
INCLUDES_CLIENT = -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua
@ -134,6 +134,7 @@ CMDSRCS = crapto1/crapto1.c \
lfdemod.c \
emv/crypto_polarssl.c\
emv/crypto.c\
emv/emvjson.c\
emv/emv_pk.c\
emv/emv_pki.c\
emv/emv_pki_priv.c\
@ -166,6 +167,7 @@ CMDSRCS = crapto1/crapto1.c \
hardnested/hardnested_bruteforce.c \
cmdhfmfdes.c \
cmdhftopaz.c \
cmdhffido.c \
cmdhffelica.c \
cmdhw.c \
cmdlf.c \

View file

@ -108,19 +108,20 @@ int CmdHFSnoop(const char *Cmd) {
static command_t CommandTable[] = {
{"help", CmdHelp, 1, "This help"},
{"14a", CmdHF14A, 1, "{ ISO14443A RFIDs... }"},
{"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"},
{"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"},
{"epa", CmdHFEPA, 1, "{ German Identification Card... }"},
{"emv", CmdHFEMV, 1, "{ EMV RFIDs... }"},
{"felica", CmdHFFelica, 1, "{ ISO18092 / Felica RFIDs... }"},
{"legic", CmdHFLegic, 1, "{ LEGIC RFIDs... }"},
{"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"},
{"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"},
{"mfp", CmdHFMFP, 1, "{ MIFARE Plus RFIDs... }"},
{"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"},
{"mfdes", CmdHFMFDes, 1, "{ MIFARE Desfire RFIDs... }"},
{"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"},
{"14a", CmdHF14A, 1, "{ ISO14443A RFIDs... }"},
{"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"},
{"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"},
{"epa", CmdHFEPA, 1, "{ German Identification Card... }"},
{"emv", CmdHFEMV, 1, "{ EMV RFIDs... }"},
{"felica", CmdHFFelica, 1, "{ ISO18092 / Felica RFIDs... }"},
{"legic", CmdHFLegic, 1, "{ LEGIC RFIDs... }"},
{"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"},
{"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"},
{"mfp", CmdHFMFP, 1, "{ MIFARE Plus RFIDs... }"},
{"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"},
{"mfdes", CmdHFMFDes, 1, "{ MIFARE Desfire RFIDs... }"},
{"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"},
{"fido", CmdHFFido, 1, "{ FIDO and FIDO2 authenticators... }"},
{"list", CmdTraceList, 0, "List protocol data in trace buffer"},
{"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"},
{"search", CmdHFSearch, 1, "Search for known HF tags [preliminary]"},

View file

@ -31,6 +31,7 @@
#include "cmdhftopaz.h" // TOPAZ
#include "cmdhffelica.h" // ISO18092 / FeliCa
#include "emv/cmdemv.h" // EMV
#include "cmdhffido.h" // FIDO authenticators
#include "cmdtrace.h" // trace list
extern int CmdHF(const char *Cmd);

550
client/cmdhffido.c Normal file
View file

@ -0,0 +1,550 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency MIFARE Plus commands
//-----------------------------------------------------------------------------
//
// Documentation here:
//
// FIDO Alliance specifications
// https://fidoalliance.org/download/
// FIDO NFC Protocol Specification v1.0
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
// FIDO U2F Raw Message Formats
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
//-----------------------------------------------------------------------------
#include "cmdhffido.h"
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <jansson.h>
#include "comms.h"
#include "cmdmain.h"
#include "util.h"
#include "ui.h"
#include "proxmark3.h"
#include "cmdhf14a.h"
#include "mifare.h"
#include "emv/emvcore.h"
#include "emv/emvjson.h"
#include "emv/dump.h"
#include "cliparser/cliparser.h"
static int CmdHelp(const char *Cmd);
int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
return EMVSelect(ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
}
int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
int res = EMVExchange(true, apdu, Result, MaxResultLen, ResultLen, sw, NULL);
if (res == 5) // apdu result (sw) not a 0x9000
res = 0;
// software chaining
while (!res && (*sw >> 8) == 0x61) {
size_t oldlen = *ResultLen;
res = EMVExchange(true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL);
if (res == 5) // apdu result (sw) not a 0x9000
res = 0;
*ResultLen += oldlen;
if (*ResultLen > MaxResultLen)
return 100;
}
return res;
}
int FIDORegister(uint8_t *params, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return FIDOExchange((sAPDU){0x00, 0x01, 0x03, 0x00, 64, params}, Result, MaxResultLen, ResultLen, sw);
}
int FIDOAuthentication(uint8_t *params, uint8_t paramslen, uint8_t controlb, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return FIDOExchange((sAPDU){0x00, 0x02, controlb, 0x00, paramslen, params}, Result, MaxResultLen, ResultLen, sw);
}
int FIDO2GetInfo(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t data[] = {0x04};
return FIDOExchange((sAPDU){0x80, 0x10, 0x00, 0x00, sizeof(data), data}, Result, MaxResultLen, ResultLen, sw);
}
int CmdHFFidoInfo(const char *cmd) {
if (cmd && strlen(cmd) > 0)
PrintAndLog("WARNING: command don't have any parameters.\n");
// info about 14a part
CmdHF14AInfo("");
// FIDO info
PrintAndLog("--------------------------------------------");
SetAPDULogging(false);
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
if (res) {
DropField();
return res;
}
if (sw != 0x9000) {
if (sw)
PrintAndLog("Not a FIDO card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
else
PrintAndLog("APDU exchange error. Card returns 0x0000.");
DropField();
return 0;
}
if (!strncmp((char *)buf, "U2F_V2", 7)) {
if (!strncmp((char *)buf, "FIDO_2_0", 8)) {
PrintAndLog("FIDO2 authenricator detected. Version: %.*s", len, buf);
} else {
PrintAndLog("FIDO authenricator detected (not standard U2F).");
PrintAndLog("Non U2F authenticator version:");
dump_buffer((const unsigned char *)buf, len, NULL, 0);
}
} else {
PrintAndLog("FIDO U2F authenricator detected. Version: %.*s", len, buf);
}
res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw);
DropField();
if (res) {
return res;
}
if (sw != 0x9000) {
PrintAndLog("FIDO2 version not exists (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
return 0;
}
PrintAndLog("FIDO2 version: (%d)", len);
dump_buffer((const unsigned char *)buf, len, NULL, 0);
return 0;
}
json_t *OpenJson(int paramnum, char *fname, void* argtable[], bool *err) {
json_t *root = NULL;
json_error_t error;
*err = false;
uint8_t jsonname[250] ={0};
char *cjsonname = (char *)jsonname;
int jsonnamelen = 0;
// CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
if (CLIParamStrToBuf(arg_get_str(paramnum), jsonname, sizeof(jsonname), &jsonnamelen)) {
CLIParserFree();
return NULL;
}
// current path + file name
if (!strstr(cjsonname, ".json"))
strcat(cjsonname, ".json");
if (jsonnamelen) {
strcpy(fname, get_my_executable_directory());
strcat(fname, cjsonname);
if (access(fname, F_OK) != -1) {
root = json_load_file(fname, 0, &error);
if (!root) {
PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
*err = true;
return NULL;
}
if (!json_is_object(root)) {
PrintAndLog("ERROR: Invalid json format. root must be an object.");
json_decref(root);
*err = true;
return NULL;
}
} else {
root = json_object();
}
}
return root;
}
int CmdHFFidoRegister(const char *cmd) {
uint8_t data[64] = {0};
int chlen = 0;
uint8_t cdata[250] = {0};
int applen = 0;
uint8_t adata[250] = {0};
json_t *root = NULL;
CLIParserInit("hf fido reg",
"Initiate a U2F token registration. Needs two 32-byte hash number. \nchallenge parameter (32b) and application parameter (32b).",
"Usage:\n\thf fido reg -> execute command with 2 parameters, filled 0x00\n"
"\thf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"
"\thf fido reg -p s0 s1 -> execute command with plain parameters");
void* argtable[] = {
arg_param_begin,
arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("vV", "verbose", "show technical data"),
arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
arg_param_end
};
CLIExecWithReturn(cmd, argtable, true);
bool APDULogging = arg_get_lit(1);
bool verbose = arg_get_lit(2);
bool paramsPlain = arg_get_lit(3);
char fname[250] = {0};
bool err;
root = OpenJson(4, fname, argtable, &err);
if(err)
return 1;
if (root) {
size_t jlen;
JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
}
if (paramsPlain) {
memset(cdata, 0x00, 32);
CLIGetStrWithReturn(5, cdata, &chlen);
if (chlen && chlen > 16) {
PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen);
return 1;
}
} else {
CLIGetHexWithReturn(5, cdata, &chlen);
if (chlen && chlen != 32) {
PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
return 1;
}
}
if (chlen)
memmove(data, cdata, 32);
if (paramsPlain) {
memset(adata, 0x00, 32);
CLIGetStrWithReturn(6, adata, &applen);
if (applen && applen > 16) {
PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen);
return 1;
}
} else {
CLIGetHexWithReturn(6, adata, &applen);
if (applen && applen != 32) {
PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
return 1;
}
}
if (applen)
memmove(&data[32], adata, 32);
CLIParserFree();
SetAPDULogging(APDULogging);
// 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
// application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
uint8_t buf[2048] = {0};
size_t len = 0;
uint16_t sw = 0;
DropField();
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
if (res) {
PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
DropField();
return res;
}
if (sw != 0x9000) {
PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
DropField();
return 2;
}
res = FIDORegister(data, buf, sizeof(buf), &len, &sw);
DropField();
if (res) {
PrintAndLog("Can't execute register command. res=%x. Exit...", res);
return res;
}
if (sw != 0x9000) {
PrintAndLog("ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
return 3;
}
PrintAndLog("");
if (APDULogging)
PrintAndLog("---------------------------------------------------------------");
PrintAndLog("data len: %d", len);
if (verbose) {
PrintAndLog("--------------data----------------------");
dump_buffer((const unsigned char *)buf, len, NULL, 0);
PrintAndLog("--------------data----------------------");
}
if (buf[0] != 0x05) {
PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf[0]);
return 5;
}
PrintAndLog("User public key: %s", sprint_hex(&buf[1], 65));
uint8_t keyHandleLen = buf[66];
PrintAndLog("Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen));
int derp = 67 + keyHandleLen;
int derLen = (buf[derp + 2] << 8) + buf[derp + 3] + 4;
// needs to decode DER certificate
if (verbose) {
PrintAndLog("DER certificate[%d]:------------------DER-------------------", derLen);
dump_buffer_simple((const unsigned char *)&buf[67 + keyHandleLen], derLen, NULL);
PrintAndLog("\n----------------DER---------------------");
} else {
PrintAndLog("DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20));
}
int hashp = 1 + 65 + 1 + keyHandleLen + derLen;
PrintAndLog("Hash[%d]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp));
// check ANSI X9.62 format ECDSA signature (on P-256)
PrintAndLog("\nauth command: ");
printf("hf fido auth %s%s", paramsPlain?"-p ":"", sprint_hex_inrow(&buf[67], keyHandleLen));
if(chlen || applen)
printf(" %s", paramsPlain?(char *)cdata:sprint_hex_inrow(cdata, 32));
if(applen)
printf(" %s", paramsPlain?(char *)adata:sprint_hex_inrow(adata, 32));
printf("\n");
if (root) {
JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen);
JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen);
res = json_dump_file(root, fname, JSON_INDENT(2));
if (res) {
PrintAndLog("ERROR: can't save the file: %s", fname);
return 200;
}
PrintAndLog("File `%s` saved.", fname);
// free json object
json_decref(root);
}
return 0;
};
int CmdHFFidoAuthenticate(const char *cmd) {
uint8_t data[512] = {0};
uint8_t hdata[250] = {0};
int hdatalen = 0;
uint8_t keyHandleLen = 0;
json_t *root = NULL;
CLIParserInit("hf fido auth",
"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).",
"Usage:\n\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
"\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
"000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
void* argtable[] = {
arg_param_begin,
arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("vV", "verbose", "show technical data"),
arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
arg_lit0("uU", "user", "mode: enforce-user-presence-and-sign"),
arg_lit0("cC", "check", "mode: check-only"),
arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
arg_str0(NULL, NULL, "<HEX key handle (var 0..255b)>", NULL),
arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
arg_param_end
};
CLIExecWithReturn(cmd, argtable, true);
bool APDULogging = arg_get_lit(1);
//bool verbose = arg_get_lit(2);
bool paramsPlain = arg_get_lit(3);
uint8_t controlByte = 0x08;
if (arg_get_lit(5))
controlByte = 0x03;
if (arg_get_lit(6))
controlByte = 0x07;
char fname[250] = {0};
bool err;
root = OpenJson(7, fname, argtable, &err);
if(err)
return 1;
if (root) {
size_t jlen;
JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen);
keyHandleLen = jlen & 0xff;
data[64] = keyHandleLen;
}
CLIGetHexWithReturn(8, hdata, &hdatalen);
if (hdatalen > 255) {
PrintAndLog("ERROR: application parameter length must be less than 255.");
return 1;
}
if (hdatalen) {
keyHandleLen = hdatalen;
data[64] = keyHandleLen;
memmove(&data[65], hdata, keyHandleLen);
}
if (paramsPlain) {
memset(hdata, 0x00, 32);
CLIGetStrWithReturn(9, hdata, &hdatalen);
if (hdatalen && hdatalen > 16) {
PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
return 1;
}
} else {
CLIGetHexWithReturn(9, hdata, &hdatalen);
if (hdatalen && hdatalen != 32) {
PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
return 1;
}
}
if (hdatalen)
memmove(data, hdata, 32);
if (paramsPlain) {
memset(hdata, 0x00, 32);
CLIGetStrWithReturn(10, hdata, &hdatalen);
if (hdatalen && hdatalen > 16) {
PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
return 1;
}
} else {
CLIGetHexWithReturn(10, hdata, &hdatalen);
if (hdatalen && hdatalen != 32) {
PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
return 1;
}
}
if (hdatalen)
memmove(&data[32], hdata, 32);
CLIParserFree();
SetAPDULogging(APDULogging);
// (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
// challenge parameter [32 bytes]
// application parameter [32 bytes]
// key handle length [1b] = N
// key handle [N]
uint8_t datalen = 32 + 32 + 1 + keyHandleLen;
uint8_t buf[2048] = {0};
size_t len = 0;
uint16_t sw = 0;
DropField();
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
if (res) {
PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
DropField();
return res;
}
if (sw != 0x9000) {
PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
DropField();
return 2;
}
res = FIDOAuthentication(data, datalen, controlByte, buf, sizeof(buf), &len, &sw);
DropField();
if (res) {
PrintAndLog("Can't execute authentication command. res=%x. Exit...", res);
return res;
}
if (sw != 0x9000) {
PrintAndLog("ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
return 3;
}
PrintAndLog("---------------------------------------------------------------");
PrintAndLog("User presence: %s", (buf[0]?"verified":"not verified"));
uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4);
PrintAndLog("Counter: %d", cntr);
PrintAndLog("Hash[%d]: %s", len - 5, sprint_hex(&buf[5], len - 5));
if (root) {
JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen);
JsonSaveInt(root, "Counter", cntr);
res = json_dump_file(root, fname, JSON_INDENT(2));
if (res) {
PrintAndLog("ERROR: can't save the file: %s", fname);
return 200;
}
PrintAndLog("File `%s` saved.", fname);
// free json object
json_decref(root);
}
return 0;
};
static command_t CommandTable[] =
{
{"help", CmdHelp, 1, "This help."},
{"info", CmdHFFidoInfo, 0, "Info about FIDO tag."},
{"reg", CmdHFFidoRegister, 0, "FIDO U2F Registration Message."},
{"auth", CmdHFFidoAuthenticate, 0, "FIDO U2F Authentication Message."},
{NULL, NULL, 0, NULL}
};
int CmdHFFido(const char *Cmd) {
(void)WaitForResponseTimeout(CMD_ACK,NULL,100);
CmdsParse(CommandTable, Cmd);
return 0;
}
int CmdHelp(const char *Cmd) {
CmdsHelp(CommandTable);
return 0;
}

27
client/cmdhffido.h Normal file
View file

@ -0,0 +1,27 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency FIDO U2F and FIDO2 contactless authenticators
//-----------------------------------------------------------------------------
//
// Documentation here:
//
// FIDO Alliance specifications
// https://fidoalliance.org/download/
// FIDO NFC Protocol Specification v1.0
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
// FIDO U2F Raw Message Formats
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
//-----------------------------------------------------------------------------
#ifndef CMDHFFIDO_H__
#define CMDHFFIDO_H__
extern int CmdHFFido(const char *Cmd);
#endif

View file

@ -68,6 +68,9 @@ extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2);
extern void SetAPDULogging(bool logging);
// exchange
extern int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
// search application
extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);

385
client/emv/emvjson.c Normal file
View file

@ -0,0 +1,385 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// EMV json logic
//-----------------------------------------------------------------------------
#include "emvjson.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include "util.h"
#include "ui.h"
#include "proxmark3.h"
#include "emv_tags.h"
static const ApplicationDataElm ApplicationData[] = {
{0x82, "AIP"},
{0x94, "AFL"},
{0x5A, "PAN"},
{0x5F34, "PANSeqNo"},
{0x5F24, "ExpirationDate"},
{0x5F25, "EffectiveDate"},
{0x5F28, "IssuerCountryCode"},
{0x50, "ApplicationLabel"},
{0x9F08, "VersionNumber"},
{0x9F42, "CurrencyCode"},
{0x5F2D, "LanguagePreference"},
{0x87, "PriorityIndicator"},
{0x9F36, "ATC"}, //Application Transaction Counter
{0x5F20, "CardholderName"},
{0x9F38, "PDOL"},
{0x8C, "CDOL1"},
{0x8D, "CDOL2"},
{0x9F07, "AUC"}, // Application Usage Control
{0x9F6C, "CTQ"},
{0x8E, "CVMList"},
{0x9F0D, "IACDefault"},
{0x9F0E, "IACDeny"},
{0x9F0F, "IACOnline"},
{0x8F, "CertificationAuthorityPublicKeyIndex"},
{0x9F32, "IssuerPublicKeyExponent"},
{0x92, "IssuerPublicKeyRemainder"},
{0x90, "IssuerPublicKeyCertificate"},
{0x9F47, "ICCPublicKeyExponent"},
{0x9F46, "ICCPublicKeyCertificate"},
{0x00, "end..."}
};
int ApplicationDataLen = sizeof(ApplicationData) / sizeof(ApplicationDataElm);
char* GetApplicationDataName(tlv_tag_t tag) {
for (int i = 0; i < ApplicationDataLen; i++)
if (ApplicationData[i].Tag == tag)
return ApplicationData[i].Name;
return NULL;
}
int JsonSaveJsonObject(json_t *root, char *path, json_t *value) {
json_error_t error;
if (strlen(path) < 1)
return 1;
if (path[0] == '$') {
if (json_path_set(root, path, value, 0, &error)) {
PrintAndLog("ERROR: can't set json path: ", error.text);
return 2;
} else {
return 0;
}
} else {
return json_object_set_new(root, path, value);
}
}
int JsonSaveInt(json_t *root, char *path, int value) {
return JsonSaveJsonObject(root, path, json_integer(value));
}
int JsonSaveStr(json_t *root, char *path, char *value) {
return JsonSaveJsonObject(root, path, json_string(value));
};
int JsonSaveBufAsHexCompact(json_t *elm, char *path, uint8_t *data, size_t datalen) {
char * msg = sprint_hex_inrow(data, datalen);
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
msg[strlen(msg) - 1] = '\0';
return JsonSaveStr(elm, path, msg);
}
int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) {
char * msg = sprint_hex(data, datalen);
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
msg[strlen(msg) - 1] = '\0';
return JsonSaveStr(elm, path, msg);
}
int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen) {
uint8_t bdata[8] = {0};
int len = 0;
if (!datalen) {
for (uint64_t u = 0xffffffffffffffff; u; u = u << 8) {
if (!(data & u)) {
break;
}
len++;
}
if (!len)
len = 1;
} else {
len = datalen;
}
num_to_bytes(data, len, bdata);
return JsonSaveBufAsHex(elm, path, bdata, len);
}
int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm) {
const struct tlv *tlvelm = tlvdb_get_tlv(tlvdbelm);
if (tlvelm)
return JsonSaveBufAsHex(root, path, (uint8_t *)tlvelm->value, tlvelm->len);
else
return 1;
}
int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink) {
json_error_t error;
if (strlen(path) < 1 || !tlvelm)
return 1;
if (path[0] == '$') {
json_t *obj = json_path_get(elm, path);
if (!obj) {
obj = json_object();
if (json_is_array(elm)) {
if (json_array_append_new(elm, obj)) {
PrintAndLog("ERROR: can't append array: %s", path);
return 2;
}
} else {
if (json_path_set(elm, path, obj, 0, &error)) {
PrintAndLog("ERROR: can't set json path: ", error.text);
return 2;
}
}
}
if (saveAppDataLink) {
char * AppDataName = GetApplicationDataName(tlvelm->tag);
if (AppDataName)
JsonSaveStr(obj, "appdata", AppDataName);
} else {
char * name = emv_get_tag_name(tlvelm);
if (saveName && name && strlen(name) > 0 && strncmp(name, "Unknown", 7))
JsonSaveStr(obj, "name", emv_get_tag_name(tlvelm));
JsonSaveHex(obj, "tag", tlvelm->tag, 0);
if (saveValue) {
JsonSaveHex(obj, "length", tlvelm->len, 0);
JsonSaveBufAsHex(obj, "value", (uint8_t *)tlvelm->value, tlvelm->len);
};
}
}
return 0;
}
int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink) {
return JsonSaveTLVElm(elm, path, (struct tlv *)tlvdb_get_tlv(tlvdbelm), saveName, saveValue, saveAppDataLink);
}
int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm) {
struct tlvdb *tlvp = tlvdbelm;
while (tlvp) {
const struct tlv * tlvpelm = tlvdb_get_tlv(tlvp);
char * AppDataName = NULL;
if (tlvpelm)
AppDataName = GetApplicationDataName(tlvpelm->tag);
if (AppDataName) {
char appdatalink[200] = {0};
sprintf(appdatalink, "$.ApplicationData.%s", AppDataName);
JsonSaveBufAsHex(root, appdatalink, (uint8_t *)tlvpelm->value, tlvpelm->len);
}
json_t *pelm = json_path_get(elm, path);
if (pelm && json_is_array(pelm)) {
json_t *appendelm = json_object();
json_array_append_new(pelm, appendelm);
JsonSaveTLVTreeElm(appendelm, "$", tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName);
pelm = appendelm;
} else {
JsonSaveTLVTreeElm(elm, path, tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName);
pelm = json_path_get(elm, path);
}
if (tlvdb_elm_get_children(tlvp)) {
// get path element
if(!pelm)
return 1;
// check childs element and add it if not found
json_t *chjson = json_path_get(pelm, "$.Childs");
if (!chjson) {
json_object_set_new(pelm, "Childs", json_array());
chjson = json_path_get(pelm, "$.Childs");
}
// check
if (!json_is_array(chjson)) {
PrintAndLog("E->Internal logic error. `$.Childs` is not an array.");
break;
}
// Recursion
JsonSaveTLVTree(root, chjson, "$", tlvdb_elm_get_children(tlvp));
}
tlvp = tlvdb_elm_get_next(tlvp);
}
return 0;
}
bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) {
int buflen = 0;
switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) {
case 1:
PrintAndLog("%s Invalid HEX value.", errormsg);
return false;
case 2:
PrintAndLog("%s Hex value too large.", errormsg);
return false;
case 3:
PrintAndLog("%s Hex value must have even number of digits.", errormsg);
return false;
}
if (buflen > maxbufferlen) {
PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen);
return false;
}
*bufferlen = buflen;
return true;
}
int JsonLoadBufAsHex(json_t *elm, char *path, uint8_t *data, size_t maxbufferlen, size_t *datalen) {
if (datalen)
*datalen = 0;
json_t *jelm = json_path_get((const json_t *)elm, path);
if (!jelm || !json_is_string(jelm))
return 1;
if (!HexToBuffer("ERROR load", json_string_value(jelm), data, maxbufferlen, datalen))
return 2;
return 0;
};
bool ParamLoadFromJson(struct tlvdb *tlv) {
json_t *root;
json_error_t error;
if (!tlv) {
PrintAndLog("ERROR load params: tlv tree is NULL.");
return false;
}
// current path + file name
const char *relfname = "emv/defparams.json";
char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1];
strcpy(fname, get_my_executable_directory());
strcat(fname, relfname);
root = json_load_file(fname, 0, &error);
if (!root) {
PrintAndLog("Load params: json error on line %d: %s", error.line, error.text);
return false;
}
if (!json_is_array(root)) {
PrintAndLog("Load params: Invalid json format. root must be array.");
return false;
}
PrintAndLog("Load params: json(%d) OK", json_array_size(root));
for(int i = 0; i < json_array_size(root); i++) {
json_t *data, *jtag, *jlength, *jvalue;
data = json_array_get(root, i);
if(!json_is_object(data))
{
PrintAndLog("Load params: data [%d] is not an object", i + 1);
json_decref(root);
return false;
}
jtag = json_object_get(data, "tag");
if(!json_is_string(jtag))
{
PrintAndLog("Load params: data [%d] tag is not a string", i + 1);
json_decref(root);
return false;
}
const char *tlvTag = json_string_value(jtag);
jvalue = json_object_get(data, "value");
if(!json_is_string(jvalue))
{
PrintAndLog("Load params: data [%d] value is not a string", i + 1);
json_decref(root);
return false;
}
const char *tlvValue = json_string_value(jvalue);
jlength = json_object_get(data, "length");
if(!json_is_number(jlength))
{
PrintAndLog("Load params: data [%d] length is not a number", i + 1);
json_decref(root);
return false;
}
int tlvLength = json_integer_value(jlength);
if (tlvLength > 250) {
PrintAndLog("Load params: data [%d] length more than 250", i + 1);
json_decref(root);
return false;
}
PrintAndLog("TLV param: %s[%d]=%s", tlvTag, tlvLength, tlvValue);
uint8_t buf[251] = {0};
size_t buflen = 0;
// here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t...
if (!HexToBuffer("TLV Error type:", tlvTag, buf, 2, &buflen)) {
json_decref(root);
return false;
}
tlv_tag_t tag = 0;
for (int i = 0; i < buflen; i++) {
tag = (tag << 8) + buf[i];
}
if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) {
json_decref(root);
return false;
}
if (buflen != tlvLength) {
PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength);
json_decref(root);
return false;
}
tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf);
}
json_decref(root);
return true;
}

40
client/emv/emvjson.h Normal file
View file

@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// EMV json logic
//-----------------------------------------------------------------------------
#ifndef EMVJSON_H__
#define EMVJSON_H__
#include <jansson.h>
#include "tlv.h"
typedef struct {
tlv_tag_t Tag;
char *Name;
} ApplicationDataElm;
extern char* GetApplicationDataName(tlv_tag_t tag);
extern int JsonSaveJsonObject(json_t *root, char *path, json_t *value);
extern int JsonSaveStr(json_t *root, char *path, char *value);
extern int JsonSaveInt(json_t *root, char *path, int value);
extern int JsonSaveBufAsHexCompact(json_t *elm, char *path, uint8_t *data, size_t datalen);
extern int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen);
extern int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen);
extern int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm);
extern int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink);
extern int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink);
extern int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm);
extern int JsonLoadBufAsHex(json_t *elm, char *path, uint8_t *data, size_t maxbufferlen, size_t *datalen);
extern bool ParamLoadFromJson(struct tlvdb *tlv);
#endif

View file

@ -9,6 +9,7 @@
//-----------------------------------------------------------------------------
#include "util.h"
#define UTIL_BUFFER_SIZE_SPRINT 4097
// global client debug variable
uint8_t g_debugMode = 0;
@ -170,13 +171,13 @@ void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) {
}
char *sprint_hex(const uint8_t *data, const size_t len) {
static char buf[1025] = {0};
static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0};
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, true);
return buf;
}
char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) {
static char buf[1025] = {0};
static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0};
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true);
return buf;
}
@ -185,13 +186,11 @@ char *sprint_hex_inrow(const uint8_t *data, const size_t len) {
return sprint_hex_inrow_ex(data, len, 0);
}
char *sprint_hex_inrow_spaces(const uint8_t *data, const size_t len, size_t spaces_between) {
static char buf[1025] = {0};
static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0};
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, spaces_between, true);
return buf;
}
char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks) {
// make sure we don't go beyond our char array memory
@ -268,9 +267,9 @@ char *sprint_bin(const uint8_t *data, const size_t len) {
}
char *sprint_hex_ascii(const uint8_t *data, const size_t len) {
static char buf[1024];
static char buf[UTIL_BUFFER_SIZE_SPRINT];
char *tmp = buf;
memset(buf, 0x00, 1024);
memset(buf, 0x00, UTIL_BUFFER_SIZE_SPRINT);
size_t max_len = (len > 1010) ? 1010 : len;
sprintf(tmp, "%s| ", sprint_hex(data, max_len) );
@ -288,9 +287,9 @@ char *sprint_hex_ascii(const uint8_t *data, const size_t len) {
}
char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len) {
static char buf[1024];
static char buf[UTIL_BUFFER_SIZE_SPRINT];
char *tmp = buf;
memset(buf, 0x00, 1024);
memset(buf, 0x00, UTIL_BUFFER_SIZE_SPRINT);
size_t max_len = (len > 1010) ? 1010 : len;
size_t i = 0;
while(i < max_len){