Merge pull request #50 from merlokk/fido

fido authenticators comands
This commit is contained in:
RFID Research Group
2018-11-13 18:32:09 +01:00
committed by GitHub
50 changed files with 8448 additions and 63 deletions
+3
View File
@@ -80,6 +80,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Added 'hf emv' commands (@merlokk)
- lots of bug fixes (many many)
- Changed hf mfp security. Now it works in all the modes. (drHatson)
- Added `hf fido` commands that work with FIDO U2F authenticators (@merlokk)
- Added mbedtls instead of old polarssl (@merlokk)
- Added jansson (@merlokk)
### Fixed
- Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi)
- Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok)
+1 -1
View File
@@ -108,7 +108,7 @@ int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response)
switch(iso_type)
{
case 'a':
return iso14_apdu(apdu, (uint16_t) length, response);
return iso14_apdu(apdu, (uint16_t) length, response, NULL);
break;
case 'b':
return iso14443b_apdu(apdu, length, response);
+20 -9
View File
@@ -2212,15 +2212,21 @@ b8 b7 b6 b5 b4 b3 b2 b1
b5,b6 = 00 - DESELECT
11 - WTX
*/
int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) {
int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data, uint8_t *res) {
uint8_t parity[MAX_PARITY_SIZE] = {0x00};
uint8_t real_cmd[cmd_len+4];
uint8_t real_cmd[cmd_len + 4];
// ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00)
// put block number into the PCB
real_cmd[0] |= iso14_pcb_blocknum;
memcpy(real_cmd + 1, cmd, cmd_len);
if (cmd_len) {
// ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00)
// put block number into the PCB
real_cmd[0] |= iso14_pcb_blocknum;
memcpy(real_cmd + 1, cmd, cmd_len);
} else {
// R-block. ACK
real_cmd[0] = 0xA2; // r-block + ACK
real_cmd[0] |= iso14_pcb_blocknum;
}
AddCrc14A(real_cmd, cmd_len + 1);
ReaderTransmit(real_cmd, cmd_len + 3, NULL);
@@ -2259,6 +2265,10 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) {
{
iso14_pcb_blocknum ^= 1;
}
// if we received I-block with chaining we need to send ACK and receive another block of data
if (res)
*res = data_bytes[0];
// crc check
if (len >=3 && !check_crc(CRC_14443_A, data_bytes, len)) {
@@ -2320,8 +2330,9 @@ void ReaderIso14443a(UsbCommand *c) {
iso14a_set_timeout(timeout);
if ((param & ISO14A_APDU)) {
arg0 = iso14_apdu(cmd, len, buf);
cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf));
uint8_t res;
arg0 = iso14_apdu(cmd, len, buf, &res);
cmd_send(CMD_ACK, arg0, res, 0, buf, sizeof(buf));
}
if ((param & ISO14A_RAW)) {
+1 -1
View File
@@ -114,7 +114,7 @@ extern void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32
extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par);
extern void iso14443a_setup(uint8_t fpga_minor_mode);
extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data);
extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data, uint8_t *res);
extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats);
extern int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades);
extern void iso14a_set_trigger(bool enable);
+13 -3
View File
@@ -25,10 +25,12 @@ OBJDIR = obj
LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm
LUALIB = ../liblua/liblua.a
JANSSONLIBPATH = ./jansson
JANSSONLIB = $(JANSSONLIBPATH)/libjansson.a
MBEDTLSLIBPATH = ../common/mbedtls
MBEDTLSLIB = $(MBEDTLSLIBPATH)/libmbedtls.a
LDFLAGS = $(ENV_LDFLAGS)
INCLUDES_CLIENT = -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -I$(MBEDTLSLIBPATH)
INCLUDES_CLIENT = -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -I$(MBEDTLSLIBPATH) -I$(JANSSONLIBPATH)
CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -DPRESETS $(INCLUDES_CLIENT) -Wall -g -O3
CXXFLAGS = -I../include -Wall -O3
@@ -105,6 +107,7 @@ CMDSRCS = crapto1/crapto1.c \
crapto1/crypto1.c \
mfkey.c \
tea.c \
fido/additional_ca.c \
polarssl/des.c \
crypto/libpcrypto.c\
crypto/asn1utils.c\
@@ -139,6 +142,7 @@ CMDSRCS = crapto1/crapto1.c \
emv/tlv.c \
emv/emv_tags.c \
emv/dol.c \
emv/emvjson.c\
emv/emvcore.c \
emv/test/crypto_test.c\
emv/test/sda_test.c\
@@ -162,6 +166,7 @@ CMDSRCS = crapto1/crapto1.c \
hardnested/hardnested_bruteforce.c \
cmdhfmfdes.c \
cmdhftopaz.c \
cmdhffido.c \
cmdhffelica.c \
cmdhw.c \
cmdlf.c \
@@ -256,12 +261,12 @@ WINBINS = $(patsubst %, %.exe, $(BINS))
CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h lualibs/usb_cmd.lua lualibs/mf_default_keys.lua
# need to assign dependancies to build these first...
all: lua_build mbedtls_build $(BINS)
all: lua_build jansson_build mbedtls_build $(BINS)
all-static: LDLIBS:=-static $(LDLIBS)
all-static: proxmark3 flasher fpga_compress
proxmark3: LDLIBS+=$(LUALIB) $(QTLDLIBS) $(MBEDTLSLIB)
proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(MBEDTLSLIB) $(QTLDLIBS)
proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua lualibs/mf_default_keys.lua
$(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@
@@ -288,6 +293,7 @@ lualibs/mf_default_keys.lua : default_keys.dic
clean:
$(RM) $(CLEAN)
cd ../liblua && make clean
cd $(JANSSONLIBPATH) && make clean
cd $(MBEDTLSLIBPATH) && make clean
tarbin: $(BINS)
@@ -297,6 +303,10 @@ lua_build:
@echo Compiling liblua, using platform $(LUAPLATFORM)
cd ../liblua && make $(LUAPLATFORM)
jansson_build:
@echo Compiling jansson
cd $(JANSSONLIBPATH) && make all
mbedtls_build:
@echo Compiling mbedtls
cd $(MBEDTLSLIBPATH) && make all
+14 -13
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]"},
+1
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);
+54 -23
View File
@@ -767,20 +767,20 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
return 0;
}
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
int CmdExchangeAPDU(uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chaining) {
uint16_t cmdc = 0;
*chaining = false;
if (activateField) {
cmdc |= ISO14A_CONNECT;
}
if (leaveSignalON)
cmdc |= ISO14A_NO_DISCONNECT;
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
// https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size
// here length USB_CMD_DATA_SIZE=512
// timeout must be authomatically set by "get ATS"
UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | cmdc, (datainlen & 0xFFFF), 0}};
UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0xFFFF), 0}};
memcpy(c.d.asBytes, datain, datainlen);
SendCommand(&c);
@@ -789,11 +789,12 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
if (activateField) {
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
PrintAndLogEx(NORMAL, "APDU ERROR: Proxmark connection timeout.");
PrintAndLogEx(ERR, "APDU: Proxmark connection timeout.");
return 1;
}
if (resp.arg[0] != 1) {
PrintAndLogEx(NORMAL, "APDU ERROR: Proxmark error %d.", resp.arg[0]);
PrintAndLogEx(ERR, "APDU: Proxmark error %d.", resp.arg[0]);
DropField();
return 1;
}
}
@@ -801,45 +802,75 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
recv = resp.d.asBytes;
int iLen = resp.arg[0];
uint8_t res = resp.arg[1];
*dataoutlen = iLen - 2;
if (*dataoutlen < 0)
*dataoutlen = 0;
int dlen = iLen - 2;
if (dlen < 0)
dlen = 0;
*dataoutlen += dlen;
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
PrintAndLogEx(NORMAL, "APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
PrintAndLogEx(ERR, "APDU: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
return 2;
}
memcpy(dataout, recv, *dataoutlen);
if(!iLen) {
PrintAndLogEx(NORMAL, "APDU ERROR: No APDU response.");
PrintAndLogEx(ERR, "APDU: No APDU response.");
return 1;
}
// check apdu length
if (iLen < 4 && iLen >= 0) {
PrintAndLogEx(ERR, "APDU: Small APDU response. Len=%d", iLen);
return 2;
}
// check block TODO
if (iLen == -2) {
PrintAndLogEx(NORMAL, "APDU ERROR: Block type mismatch.");
PrintAndLogEx(ERR, "APDU: Block type mismatch.");
return 2;
}
memcpy(dataout, recv, dlen);
// chaining
if ((res & 0x10) != 0) {
*chaining = true;
}
// CRC Check
if (iLen == -1) {
PrintAndLogEx(NORMAL, "APDU ERROR: ISO 14443A CRC error.");
PrintAndLogEx(ERR, "APDU: ISO 14443A CRC error.");
return 3;
}
// check apdu length
if (iLen < 4) {
PrintAndLogEx(NORMAL, "APDU ERROR: Small APDU response. Len=%d", iLen);
return 2;
}
} else {
PrintAndLogEx(NORMAL, "APDU ERROR: Reply timeout.");
PrintAndLogEx(ERR, "APDU: Reply timeout.");
return 4;
}
return 0;
}
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
*dataoutlen = 0;
bool chaining = false;
int res = CmdExchangeAPDU(datain, datainlen, activateField, dataout, maxdataoutlen, dataoutlen, &chaining);
while (chaining) {
// I-block with chaining
res = CmdExchangeAPDU(NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining);
if (res) {
if (!leaveSignalON)
DropField();
return 100;
}
}
if (!leaveSignalON)
DropField();
return 0;
}
+704
View File
File diff suppressed because it is too large Load Diff
+27
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
+26
View File
@@ -16,6 +16,7 @@
#include <mbedtls/asn1.h>
#include <mbedtls/aes.h>
#include <mbedtls/cmac.h>
#include <mbedtls/pk.h>
#include <mbedtls/ecdsa.h>
#include <mbedtls/sha256.h>
#include <mbedtls/ctr_drbg.h>
@@ -208,6 +209,31 @@ char *ecdsa_get_error(int ret) {
return retstr;
}
int ecdsa_public_key_from_pk(mbedtls_pk_context *pk, uint8_t *key, size_t keylen) {
int res = 0;
size_t realkeylen = 0;
if (keylen < 65)
return 1;
mbedtls_ecdsa_context ctx;
mbedtls_ecdsa_init(&ctx);
res = mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_SECP256R1); // secp256r1
if (res)
goto exit;
res = mbedtls_ecdsa_from_keypair(&ctx, mbedtls_pk_ec(*pk) );
if (res)
goto exit;
res = mbedtls_ecp_point_write_binary(&ctx.grp, &ctx.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &realkeylen, key, keylen);
if (realkeylen != 65)
res = 2;
exit:
mbedtls_ecdsa_free(&ctx);
return res;
}
int ecdsa_signature_create(uint8_t *key_d, uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t *signaturelen) {
int res;
*signaturelen = 0;
+2
View File
@@ -14,6 +14,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <mbedtls/pk.h>
extern int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
extern int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
@@ -23,6 +24,7 @@ extern int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, in
extern int sha256hash(uint8_t *input, int length, uint8_t *hash);
extern int ecdsa_key_create(uint8_t * key_d, uint8_t *key_xy);
extern int ecdsa_public_key_from_pk(mbedtls_pk_context *pk, uint8_t *key, size_t keylen);
extern int ecdsa_signature_create(uint8_t *key_d, uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t *signaturelen);
extern int ecdsa_signature_verify(uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t signaturelen);
extern char *ecdsa_get_error(int ret);
+16 -2
View File
@@ -272,7 +272,7 @@ static const struct emv_tag emv_tags[] = {
{ 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC },
{ 0x9f6b, "Track 2 Data" },
{ 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ },
{ 0x9f6e, "Form Factor Indicator" },
{ 0x9f6e, "Form Factor Indicator" },
{ 0xa5 , "File Control Information (FCI) Proprietary Template" },
{ 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" },
{ 0xdf20, "Issuer Proprietary Bitmap (IPB)" },
@@ -391,7 +391,7 @@ static unsigned long emv_value_numeric(const struct tlv *tlv, unsigned start, un
static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) {
PRINT_INDENT(level);
fprintf(f, "\tNumeric value %lu \n", emv_value_numeric(tlv, 0, tlv->len * 2));
fprintf(f, "\tNumeric value %lu\n", emv_value_numeric(tlv, 0, tlv->len * 2));
}
static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) {
@@ -681,3 +681,17 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level)
return true;
}
char *emv_get_tag_name(const struct tlv *tlv)
{
static char *defstr = "";
if (!tlv)
return defstr;
const struct emv_tag *tag = emv_get_tag(tlv);
if (tag)
return tag->name;
return defstr;
}
+1 -1
View File
@@ -18,7 +18,6 @@
#include "tlv.h"
#include <stdio.h>
#include <inttypes.h>
// AC
# define EMVAC_AC_MASK 0xC0
@@ -32,5 +31,6 @@
# define EMVCID_REASON_MASK 0x07
bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level);
char *emv_get_tag_name(const struct tlv *tlv);
#endif
+3 -1
View File
@@ -228,8 +228,10 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ
if (sw) *sw = 0;
uint16_t isw = 0;
if (ActivateField)
if (ActivateField) {
DropField();
msleep(50);
}
// COMPUTE APDU
memcpy(data, &apdu, 5);
+3
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
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
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
+83
View File
@@ -318,6 +318,24 @@ struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag) {
return NULL;
}
struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag) {
if (!tlvdb)
return NULL;
for (; tlvdb; tlvdb = tlvdb->next) {
if (tlvdb->tag.tag == tag)
return tlvdb;
if (tlvdb->children) {
struct tlvdb * ch = tlvdb_find_full(tlvdb->children, tag);
if (ch)
return ch;
}
}
return NULL;
}
struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) {
int i = 0;
struct tlvdb *tnext = tlvdb;
@@ -342,6 +360,52 @@ void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other)
tlvdb->next = other;
}
void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value)
{
struct tlvdb *telm = tlvdb_find_full(tlvdb, tag);
if (telm == NULL) {
// new tlv element
tlvdb_add(tlvdb, tlvdb_fixed(tag, len, value));
} else {
// the same tlv structure
if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len))
return;
// replace tlv element
struct tlvdb *tnewelm = tlvdb_fixed(tag, len, value);
tnewelm->next = telm->next;
tnewelm->parent = telm->parent;
// if telm stayed first in children chain
if (telm->parent && telm->parent->children == telm) {
telm->parent->children = tnewelm;
}
// if telm have previous element
if (telm != tlvdb) {
// elm in root
struct tlvdb *celm = tlvdb;
// elm in child list of node
if (telm->parent && telm->parent->children)
celm = telm->parent->children;
// find previous element
for (; celm; celm = celm->next) {
if (celm->next == telm) {
celm->next = tnewelm;
break;
}
}
}
// free old element with childrens
telm->next = NULL;
tlvdb_free(telm);
}
return;
}
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level)
{
struct tlvdb *next = NULL;
@@ -394,6 +458,10 @@ const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, co
return tlvdb_get(tlvdb, tag, prev);
}
const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb) {
return &tlvdb->tag;
}
unsigned char *tlv_encode(const struct tlv *tlv, size_t *len)
{
size_t size = tlv->len;
@@ -452,3 +520,18 @@ bool tlv_equal(const struct tlv *a, const struct tlv *b)
return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len);
}
struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb)
{
return tlvdb->next;
}
struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb)
{
return tlvdb->children;
}
struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb)
{
return tlvdb->parent;
}
+7
View File
@@ -39,15 +39,22 @@ struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len);
struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len);
void tlvdb_free(struct tlvdb *tlvdb);
struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb);
struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb);
struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb);
struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag); // search also in childrens
struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag);
struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag);
struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]);
void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other);
void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value);
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level);
const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb);
bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv);
unsigned char *tlv_encode(const struct tlv *tlv, size_t *len);

Some files were not shown because too many files have changed in this diff Show More