From a2bb2735d5095aabeba4a891fe4ba867a9afc2b3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 13:38:29 +0200 Subject: [PATCH 1/1] TLV decoding works --- client/Makefile | 4 + client/cmdhf14a.c | 4 +- client/cmdhf14a.h | 1 + client/emv/dump.c | 57 +++++ client/emv/dump.h | 24 +++ client/emv/emv_tags.c | 480 ++++++++++++++++++++++++++++++++++++++++++ client/emv/emv_tags.h | 24 +++ client/emv/emvcore.c | 31 +++ client/emv/emvcore.h | 31 +++ client/emv/tlv.c | 415 ++++++++++++++++++++++++++++++++++++ client/emv/tlv.h | 52 +++++ 11 files changed, 1121 insertions(+), 2 deletions(-) create mode 100644 client/emv/dump.c create mode 100644 client/emv/dump.h create mode 100644 client/emv/emv_tags.c create mode 100644 client/emv/emv_tags.h create mode 100644 client/emv/emvcore.c create mode 100644 client/emv/emvcore.h create mode 100644 client/emv/tlv.c create mode 100644 client/emv/tlv.h diff --git a/client/Makefile b/client/Makefile index 8e1574d4..30e91efb 100644 --- a/client/Makefile +++ b/client/Makefile @@ -108,6 +108,10 @@ CMDSRCS = crapto1/crapto1.c\ cmddata.c \ lfdemod.c \ emv/apduinfo.c\ + emv/dump.c\ + emv/tlv.c\ + emv/emv_tags.c\ + emv/emvcore.c\ cmdhf.c \ cmdhf14a.c \ cmdhf14b.c \ diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index aebe2463..1a7f6970 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -688,8 +688,8 @@ int CmdHF14AAPDU(const char *cmd) { PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); // here TLV decoder... - if (decodeTLV) { - PrintAndLog("--- TLV decoded:"); + if (decodeTLV && datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); } return 0; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 919ece5a..d11d17eb 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -31,6 +31,7 @@ #include "cmdhfmfu.h" #include "mifarehost.h" #include "emv/apduinfo.h" +#include "emv/emvcore.h" // structure and database for uid -> tagtype lookups typedef struct { diff --git a/client/emv/dump.c b/client/emv/dump.c new file mode 100644 index 00000000..9915ad73 --- /dev/null +++ b/client/emv/dump.c @@ -0,0 +1,57 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "dump.h" + +#include + +void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f) +{ + int i; + + if (!f) + f = stdout; + + for (i = 0; i < len; i ++) + fprintf(f, "%s%02hhX", i ? " " : "", ptr[i]); +} + +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f) +{ + int i, j; + + if (!f) + f = stdout; + + for (i = 0; i < len; i += 16) { + fprintf(f, "\t%02x:", i); + for (j = 0; j < 16; j++) { + if (i + j < len) + fprintf(f, " %02hhx", ptr[i + j]); + else + fprintf(f, " "); + } + fprintf(f, " |"); + for (j = 0; j < 16 && i + j < len; j++) { + fprintf(f, "%c", (ptr[i+j] >= 0x20 && ptr[i+j] < 0x7f) ? ptr[i+j] : '.' ); + } + fprintf(f, "\n"); + } +} + diff --git a/client/emv/dump.h b/client/emv/dump.h new file mode 100644 index 00000000..5976da44 --- /dev/null +++ b/client/emv/dump.h @@ -0,0 +1,24 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef DUMP_H +#define DUMP_H + +#include + +void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f); + +#endif diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c new file mode 100644 index 00000000..9bcf2004 --- /dev/null +++ b/client/emv/emv_tags.c @@ -0,0 +1,480 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tlv.h" +#include "emv_tags.h" + +#include + +enum emv_tag_t { + EMV_TAG_GENERIC, + EMV_TAG_BITMASK, + EMV_TAG_DOL, + EMV_TAG_CVM_LIST, + EMV_TAG_STRING, + EMV_TAG_NUMERIC, + EMV_TAG_YYMMDD, + EMV_TAG_FCI, +}; + +struct emv_tag { + tlv_tag_t tag; + char *name; + enum emv_tag_t type; + const void *data; +}; + +struct emv_tag_bit { + unsigned bit; + const char *name; +}; + +#define EMV_BIT(byte, bit) ((byte - 1) * 8 + (8 - bit)) +#define EMV_BIT_FINISH { (~0), NULL } + +static const struct emv_tag_bit EMV_AIP[] = { + { EMV_BIT(1, 7), "SDA supported" }, + { EMV_BIT(1, 6), "DDA supported" }, + { EMV_BIT(1, 5), "Cardholder verification is supported" }, + { EMV_BIT(1, 4), "Terminal risk management is to be performed" }, + { EMV_BIT(1, 3), "Issuer authentication is supported" }, + { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(1, 1), "CDA supported" }, + { EMV_BIT(2, 8), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 1), "Reserved for use by the EMV Contactless Specifications" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_AUC[] = { + { EMV_BIT(1, 8), "Valid for domestic cash transactions" }, + { EMV_BIT(1, 7), "Valid for international cash transactions" }, + { EMV_BIT(1, 6), "Valid for domestic goods" }, + { EMV_BIT(1, 5), "Valid for international goods" }, + { EMV_BIT(1, 4), "Valid for domestic services" }, + { EMV_BIT(1, 3), "Valid for international services" }, + { EMV_BIT(1, 2), "Valid for ATMs" }, + { EMV_BIT(1, 1), "Valid at terminals other than ATMs" }, + { EMV_BIT(2, 8), "Domestic cashback allowed" }, + { EMV_BIT(2, 7), "International cashback allowed" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_TVR[] = { + { EMV_BIT(1, 8), "Offline data authentication was not performed" }, + { EMV_BIT(1, 7), "SDA failed" }, + { EMV_BIT(1, 6), "ICC data missing" }, + { EMV_BIT(1, 5), "Card appears on terminal exception file" }, + { EMV_BIT(1, 4), "DDA failed" }, + { EMV_BIT(1, 3), "CDA failed" }, + { EMV_BIT(1, 2), "SDA selected" }, + { EMV_BIT(2, 8), "ICC and terminal have different application versions" }, + { EMV_BIT(2, 7), "Expired application" }, + { EMV_BIT(2, 6), "Application not yet effective" }, + { EMV_BIT(2, 5), "Requested service not allowed for card product" }, + { EMV_BIT(2, 4), "New card" }, + { EMV_BIT(3, 8), "Cardholder verification was not successful" }, + { EMV_BIT(3, 7), "Unrecognised CVM" }, + { EMV_BIT(3, 6), "PIN Try Limit exceeded" }, + { EMV_BIT(3, 5), "PIN entry required and PIN pad not present or not working" }, + { EMV_BIT(3, 4), "PIN entry required, PIN pad present, but PIN was not entered" }, + { EMV_BIT(3, 3), "Online PIN entered" }, + { EMV_BIT(4, 8), "Transaction exceeds floor limit" }, + { EMV_BIT(4, 7), "Lower consecutive offline limit exceeded" }, + { EMV_BIT(4, 6), "Upper consecutive offline limit exceeded" }, + { EMV_BIT(4, 5), "Transaction selected randomly for online processing" }, + { EMV_BIT(4, 4), "Merchant forced transaction online" }, + { EMV_BIT(5, 8), "Default TDOL used" }, + { EMV_BIT(5, 7), "Issuer authentication failed" }, + { EMV_BIT(5, 6), "Script processing failed before final GENERATE AC" }, + { EMV_BIT(5, 5), "Script processing failed after final GENERATE AC" }, + { EMV_BIT(5, 4), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 3), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 2), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 1), "Reserved for use by the EMV Contactless Specifications" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag emv_tags[] = { + { 0x00 , "Unknown ???" }, + { 0x4f , "Application Dedicated File (ADF) Name" }, + { 0x50 , "Application Label", EMV_TAG_STRING }, + { 0x56 , "Track 1 Data" }, + { 0x57 , "Track 2 Equivalent Data" }, + { 0x5a , "Application Primary Account Number (PAN)" }, + { 0x5f20, "Cardholder Name", EMV_TAG_STRING }, + { 0x5f24, "Application Expiration Date", EMV_TAG_YYMMDD }, + { 0x5f25, "Application Effective Date", EMV_TAG_YYMMDD }, + { 0x5f28, "Issuer Country Code", EMV_TAG_NUMERIC }, + { 0x5f2a, "Transaction Currency Code", EMV_TAG_NUMERIC }, + { 0x5f2d, "Language Preference", EMV_TAG_STRING }, + { 0x5f30, "Service Code", EMV_TAG_NUMERIC }, + { 0x5f34, "Application Primary Account Number (PAN) Sequence Number", EMV_TAG_NUMERIC }, + { 0x61 , "Application Template" }, + { 0x6f , "File Control Information (FCI) Template", EMV_TAG_FCI }, + { 0x70 , "READ RECORD Response Message Template" }, + { 0x77 , "Response Message Template Format 2" }, + { 0x80 , "Response Message Template Format 1" }, + { 0x82 , "Application Interchange Profile", EMV_TAG_BITMASK, &EMV_AIP }, + { 0x83 , "Command Template" }, + { 0x84 , "Dedicated File (DF) Name" }, + { 0x87 , "Application Priority Indicator" }, + { 0x88 , "Short File Identifier (SFI)" }, + { 0x8a , "Authorisation Response Code" }, + { 0x8c , "Card Risk Management Data Object List 1 (CDOL1)", EMV_TAG_DOL }, + { 0x8d , "Card Risk Management Data Object List 2 (CDOL2)", EMV_TAG_DOL }, + { 0x8e , "Cardholder Verification Method (CVM) List", EMV_TAG_CVM_LIST }, + { 0x8f , "Certification Authority Public Key Index" }, + { 0x90 , "Issuer Public Key Certificate" }, + { 0x91 , "Issuer Authentication Data" }, + { 0x92 , "Issuer Public Key Remainder" }, + { 0x93 , "Signed Static Application Data" }, + { 0x94 , "Application File Locator (AFL)" }, + { 0x95 , "Terminal Verification Results" }, + { 0x9a , "Transaction Date", EMV_TAG_YYMMDD }, + { 0x9c , "Transaction Type" }, + { 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC }, + { 0x9f03, "Amount, Other (Numeric)", EMV_TAG_NUMERIC, }, + { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC }, + { 0x9f08, "Application Version Number" }, + { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f0e, "Issuer Action Code - Denial", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f0f, "Issuer Action Code - Online", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f10, "Issuer Application Data" }, + { 0x9f11, "Issuer Code Table Index", EMV_TAG_NUMERIC }, + { 0x9f12, "Application Preferred Name", EMV_TAG_STRING }, + { 0x9f13, "Last Online Application Transaction Counter (ATC) Register" }, + { 0x9f17, "Personal Identification Number (PIN) Try Counter" }, + { 0x9f1a, "Terminal Country Code" }, + { 0x9f1f, "Track 1 Discretionary Data", EMV_TAG_STRING }, + { 0x9f21, "Transaction Time" }, + { 0x9f26, "Application Cryptogram" }, + { 0x9f27, "Cryptogram Information Data" }, + { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, + { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, + { 0x9f2f, "ICC PIN Encipherment Public Key Remainder" }, + { 0x9f32, "Issuer Public Key Exponent" }, + { 0x9f34, "Cardholder Verification Method (CVM) Results" }, + { 0x9f35, "Terminal Type" }, + { 0x9f36, "Application Transaction Counter (ATC)" }, + { 0x9f37, "Unpredictable Number" }, + { 0x9f38, "Processing Options Data Object List (PDOL)", EMV_TAG_DOL }, + { 0x9f42, "Application Currency Code", EMV_TAG_NUMERIC }, + { 0x9f44, "Application Currency Exponent", EMV_TAG_NUMERIC }, + { 0x9f45, "Data Authentication Code" }, + { 0x9f46, "ICC Public Key Certificate" }, + { 0x9f47, "ICC Public Key Exponent" }, + { 0x9f48, "ICC Public Key Remainder" }, + { 0x9f49, "Dynamic Data Authentication Data Object List (DDOL)", EMV_TAG_DOL }, + { 0x9f4a, "Static Data Authentication Tag List" }, + { 0x9f4b, "Signed Dynamic Application Data" }, + { 0x9f4c, "ICC Dynamic Number" }, + { 0x9f4d, "Log Entry" }, + { 0x9f4f, "Log Format", EMV_TAG_DOL }, + { 0x9f62, "PCVC3(Track1)" }, + { 0x9f63, "PUNATC(Track1)" }, + { 0x9f64, "NATC(Track1)" }, + { 0x9f65, "PCVC3(Track2)" }, + { 0x9f66, "PUNATC(Track2)" }, + { 0x9f67, "NATC(Track2)" }, + { 0x9f6b, "Track 2 Data" }, + { 0xa5 , "File Control Information (FCI) Proprietary Template" }, + { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, +}; + +static int emv_sort_tag(tlv_tag_t tag) +{ + return (int)(tag >= 0x100 ? tag : tag << 8); +} + +static int emv_tlv_compare(const void *a, const void *b) +{ + const struct tlv *tlv = a; + const struct emv_tag *tag = b; + + return emv_sort_tag(tlv->tag) - (emv_sort_tag(tag->tag)); +} + +static const struct emv_tag *emv_get_tag(const struct tlv *tlv) +{ + struct emv_tag *tag = bsearch(tlv, emv_tags, sizeof(emv_tags)/sizeof(emv_tags[0]), + sizeof(emv_tags[0]), emv_tlv_compare); + + return tag ? tag : &emv_tags[0]; +} + +static const char *bitstrings[] = { + ".......1", + "......1.", + ".....1..", + "....1...", + "...1....", + "..1.....", + ".1......", + "1.......", +}; + +static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + const struct emv_tag_bit *bits = tag->data; + unsigned bit, byte; + + for (byte = 1; byte <= tlv->len; byte ++) { + unsigned char val = tlv->value[byte - 1]; + fprintf(f, "\tByte %u (%02x)\n", byte, val); + for (bit = 8; bit > 0; bit--, val <<= 1) { + if (val & 0x80) + fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1], + bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown"); + if (bits->bit == EMV_BIT(byte, bit)) + bits ++; + } + } +} + +static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + + while (left) { + struct tlv doltlv; + const struct emv_tag *doltag; + + if (!tlv_parse_tl(&buf, &left, &doltlv)) { + fprintf(f, "Invalid Tag-Len\n"); + continue; + } + + doltag = emv_get_tag(&doltlv); + + fprintf(f, "\tTag %4hx len %02zx ('%s')\n", doltlv.tag, doltlv.len, doltag->name); + } +} + +static void emv_tag_dump_fci(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) { + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + + while (left) { + struct tlv doltlv; + const struct emv_tag *doltag; + + if (!tlv_parse_tl(&buf, &left, &doltlv)) { + fprintf(f, "Invalid Tag-Len\n"); + continue; + } + + doltag = emv_get_tag(&doltlv); + + fprintf(f, "\t--%2hx[%02zx]'%s'\n", doltlv.tag, doltlv.len, doltag->name); + } +} + +static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + fprintf(f, "\tString value '"); + fwrite(tlv->value, 1, tlv->len, f); + fprintf(f, "'\n"); +} + +static unsigned long emv_value_numeric(const struct tlv *tlv, unsigned start, unsigned end) +{ + unsigned long ret = 0; + int i; + + if (end > tlv->len * 2) + return ret; + if (start >= end) + return ret; + + if (start & 1) { + ret += tlv->value[start/2] & 0xf; + i = start + 1; + } else + i = start; + + for (; i < end - 1; i += 2) { + ret *= 10; + ret += tlv->value[i/2] >> 4; + ret *= 10; + ret += tlv->value[i/2] & 0xf; + } + + if (end & 1) { + ret *= 10; + ret += tlv->value[end/2] >> 4; + } + + return ret; +} + +static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + 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) +{ + fprintf(f, "\tDate: 20%02ld.%ld.%ld\n", + emv_value_numeric(tlv, 0, 2), + emv_value_numeric(tlv, 2, 4), + emv_value_numeric(tlv, 4, 6)); +} + +static uint32_t emv_get_binary(const unsigned char *S) +{ + return (S[0] << 24) | (S[1] << 16) | (S[2] << 8) | (S[3] << 0); +} + +static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +{ + uint32_t X, Y; + int i; + + if (tlv->len < 10 || tlv->len % 2) { + fprintf(f, "\tINVALID!\n"); + return; + } + + X = emv_get_binary(tlv->value); + Y = emv_get_binary(tlv->value + 4); + + fprintf(f, "\tX: %d\n", X); + fprintf(f, "\tY: %d\n", Y); + + for (i = 8; i < tlv->len; i+= 2) { + const char *method; + const char *condition; + + switch (tlv->value[i] & 0x3f) { + case 0x0: + method = "Fail CVM processing"; + break; + case 0x1: + method = "Plaintext PIN verification performed by ICC"; + break; + case 0x2: + method = "Enciphered PIN verified online"; + break; + case 0x3: + method = "Plaintext PIN verification performed by ICC and signature (paper)"; + break; + case 0x4: + method = "Enciphered PIN verification performed by ICC"; + break; + case 0x5: + method = "Enciphered PIN verification performed by ICC and signature (paper)"; + break; + case 0x1e: + method = "Signature (paper)"; + break; + case 0x1f: + method = "No CVM required"; + break; + case 0x3f: + method = "NOT AVAILABLE!"; + break; + default: + method = "Unknown"; + break; + } + + switch (tlv->value[i+1]) { + case 0x00: + condition = "Always"; + break; + case 0x01: + condition = "If unattended cash"; + break; + case 0x02: + condition = "If not unattended cash and not manual cash and not purchase with cashback"; + break; + case 0x03: + condition = "If terminal supports the CVM"; + break; + case 0x04: + condition = "If manual cash"; + break; + case 0x05: + condition = "If purchase with cashback"; + break; + case 0x06: + condition = "If transaction is in the application currency and is under X value"; + break; + case 0x07: + condition = "If transaction is in the application currency and is over X value"; + break; + case 0x08: + condition = "If transaction is in the application currency and is under Y value"; + break; + case 0x09: + condition = "If transaction is in the application currency and is over Y value"; + break; + default: + condition = "Unknown"; + break; + } + + fprintf(f, "\t%02x %02x: '%s' '%s' and '%s' if this CVM is unsuccessful\n", + tlv->value[i], tlv->value[i+1], + method, condition, (tlv->value[i] & 0x40) ? "continue" : "fail"); + } +} + +bool emv_tag_dump(const struct tlv *tlv, FILE *f) +{ + if (!tlv) { + fprintf(f, "NULL\n"); + return false; + } + + const struct emv_tag *tag = emv_get_tag(tlv); + + fprintf(f, "--%2hx[%02zx] '%s':\n", tlv->tag, tlv->len, tag->name); + + switch (tag->type) { + case EMV_TAG_GENERIC: + break; + case EMV_TAG_BITMASK: + emv_tag_dump_bitmask(tlv, tag, f); + break; + case EMV_TAG_DOL: + emv_tag_dump_dol(tlv, tag, f); + break; + case EMV_TAG_CVM_LIST: + emv_tag_dump_cvm_list(tlv, tag, f); + break; + case EMV_TAG_STRING: + emv_tag_dump_string(tlv, tag, f); + break; + case EMV_TAG_NUMERIC: + emv_tag_dump_numeric(tlv, tag, f); + break; + case EMV_TAG_YYMMDD: + emv_tag_dump_yymmdd(tlv, tag, f); + break; + case EMV_TAG_FCI: + emv_tag_dump_fci(tlv, tag, f); + break; + }; + + return true; +} diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h new file mode 100644 index 00000000..de6d9d1e --- /dev/null +++ b/client/emv/emv_tags.h @@ -0,0 +1,24 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef TAGS_H +#define TAGS_H + +#include "tlv.h" +#include + +bool emv_tag_dump(const struct tlv *tlv, FILE *f); + +#endif diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c new file mode 100644 index 00000000..6f8892fe --- /dev/null +++ b/client/emv/emvcore.c @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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 core functions +//----------------------------------------------------------------------------- + +#include "emvcore.h" + +static bool print_cb(void *data, const struct tlv *tlv) { + emv_tag_dump(tlv, stdout); + dump_buffer(tlv->value, tlv->len, stdout); + + return true; +} + +void TLVPrintFromBuffer(uint8_t *data, int datalen) { + struct tlvdb *t = NULL; + t = tlvdb_parse(data, datalen); + if (t) { + PrintAndLog("TLV decoded:"); + + tlvdb_visit(t, print_cb, NULL); + tlvdb_free(t); + } else { + PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); + } +} diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h new file mode 100644 index 00000000..523b92ac --- /dev/null +++ b/client/emv/emvcore.h @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 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 core functions +//----------------------------------------------------------------------------- + +#ifndef EMVCORE_H__ +#define EMVCORE_H__ + +#include +#include +#include +#include +#include "util.h" +#include "common.h" +#include "ui.h" +#include "emv/tlv.h" +#include "emv/dump.h" +#include "emv/emv_tags.h" + +extern void TLVPrintFromBuffer(uint8_t *data, int datalen); + +#endif + + + + diff --git a/client/emv/tlv.c b/client/emv/tlv.c new file mode 100644 index 00000000..d78f049e --- /dev/null +++ b/client/emv/tlv.c @@ -0,0 +1,415 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * https://github.com/lumag/emv-tools/blob/master/lib/tlv.c + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tlv.h" + +#include +#include +#include +#include + +#define TLV_TAG_CLASS_MASK 0xc0 +#define TLV_TAG_COMPLEX 0x20 +#define TLV_TAG_VALUE_MASK 0x1f +#define TLV_TAG_VALUE_CONT 0x1f +#define TLV_TAG_INVALID 0 + +#define TLV_LEN_LONG 0x80 +#define TLV_LEN_MASK 0x7f +#define TLV_LEN_INVALID (~0) + +// http://radek.io/2012/11/10/magical-container_of-macro/ +//#define container_of(ptr, type, member) ({ +// const typeof( ((type *)0)->member ) *__mptr = (ptr); +// (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct tlvdb { + struct tlv tag; + struct tlvdb *next; + struct tlvdb *parent; + struct tlvdb *children; +}; + +struct tlvdb_root { + struct tlvdb db; + size_t len; + unsigned char buf[0]; +}; + +static tlv_tag_t tlv_parse_tag(const unsigned char **buf, size_t *len) +{ + tlv_tag_t tag; + + if (*len == 0) + return TLV_TAG_INVALID; + tag = **buf; + --*len; + ++*buf; + if ((tag & TLV_TAG_VALUE_MASK) != TLV_TAG_VALUE_CONT) + return tag; + + if (*len == 0) + return TLV_TAG_INVALID; + + tag <<= 8; + tag |= **buf; + --*len; + ++*buf; + + return tag; +} + +static size_t tlv_parse_len(const unsigned char **buf, size_t *len) +{ + size_t l; + + if (*len == 0) + return TLV_LEN_INVALID; + + l = **buf; + --*len; + ++*buf; + + if (!(l & TLV_LEN_LONG)) + return l; + + size_t ll = l &~ TLV_LEN_LONG; + if (*len < ll) + return TLV_LEN_INVALID; + + /* FIXME */ + if (ll != 1) + return TLV_LEN_INVALID; + + l = **buf; + --*len; + ++*buf; + + return l; +} + +bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv) +{ + tlv->value = 0; + + tlv->tag = tlv_parse_tag(buf, len); + if (tlv->tag == TLV_TAG_INVALID) + return false; + + tlv->len = tlv_parse_len(buf, len); + if (tlv->len == TLV_LEN_INVALID) + return false; + + return true; +} + +static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent); + +static bool tlvdb_parse_one(struct tlvdb *tlvdb, + struct tlvdb *parent, + const unsigned char **tmp, + size_t *left) +{ + tlvdb->next = tlvdb->children = NULL; + tlvdb->parent = parent; + + tlvdb->tag.tag = tlv_parse_tag(tmp, left); + if (tlvdb->tag.tag == TLV_TAG_INVALID) + goto err; + + tlvdb->tag.len = tlv_parse_len(tmp, left); + if (tlvdb->tag.len == TLV_LEN_INVALID) + goto err; + + if (tlvdb->tag.len > *left) + goto err; + + tlvdb->tag.value = *tmp; + + *tmp += tlvdb->tag.len; + *left -= tlvdb->tag.len; + + if (tlv_is_constructed(&tlvdb->tag) && (tlvdb->tag.len != 0)) { + tlvdb->children = tlvdb_parse_children(tlvdb); + if (!tlvdb->children) + goto err; + } else { + tlvdb->children = NULL; + } + + return true; + +err: + return false; +} + +static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent) +{ + const unsigned char *tmp = parent->tag.value; + size_t left = parent->tag.len; + struct tlvdb *tlvdb, *first = NULL, *prev = NULL; + + while (left != 0) { + tlvdb = malloc(sizeof(*tlvdb)); + if (prev) + prev->next = tlvdb; + else + first = tlvdb; + prev = tlvdb; + + if (!tlvdb_parse_one(tlvdb, parent, &tmp, &left)) + goto err; + + tlvdb->parent = parent; + } + + return first; + +err: + tlvdb_free(first); + + return NULL; +} + +struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len) +{ + struct tlvdb_root *root; + const unsigned char *tmp; + size_t left; + + if (!len || !buf) + return NULL; + + root = malloc(sizeof(*root) + len); + root->len = len; + memcpy(root->buf, buf, len); + + tmp = root->buf; + left = len; + + if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + goto err; + + if (left) + goto err; + + return &root->db; + +err: + tlvdb_free(&root->db); + + return NULL; +} + +struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len) +{ + struct tlvdb_root *root; + const unsigned char *tmp; + size_t left; + + if (!len || !buf) + return NULL; + + root = malloc(sizeof(*root) + len); + root->len = len; + memcpy(root->buf, buf, len); + + tmp = root->buf; + left = len; + + if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + goto err; + + while (left != 0) { + struct tlvdb *db = malloc(sizeof(*db)); + if (!tlvdb_parse_one(db, NULL, &tmp, &left)) { + free (db); + goto err; + } + + tlvdb_add(&root->db, db); + } + + return &root->db; + +err: + tlvdb_free(&root->db); + + return NULL; +} + +struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb_root *root = malloc(sizeof(*root) + len); + + root->len = len; + memcpy(root->buf, value, len); + + root->db.parent = root->db.next = root->db.children = NULL; + root->db.tag.tag = tag; + root->db.tag.len = len; + root->db.tag.value = root->buf; + + return &root->db; +} + +struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb_root *root = malloc(sizeof(*root)); + + root->len = 0; + + root->db.parent = root->db.next = root->db.children = NULL; + root->db.tag.tag = tag; + root->db.tag.len = len; + root->db.tag.value = value; + + return &root->db; +} + +void tlvdb_free(struct tlvdb *tlvdb) +{ + struct tlvdb *next = NULL; + + if (!tlvdb) + return; + + for (; tlvdb; tlvdb = next) { + next = tlvdb->next; + tlvdb_free(tlvdb->children); + free(tlvdb); + } +} + +void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) +{ + while (tlvdb->next) { + tlvdb = tlvdb->next; + } + + tlvdb->next = other; +} + +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data) +{ + struct tlvdb *next = NULL; + + if (!tlvdb) + return; + + for (; tlvdb; tlvdb = next) { + next = tlvdb->next; + cb(data, &tlvdb->tag); + tlvdb_visit(tlvdb->children, cb, data); + } +} + +static const struct tlvdb *tlvdb_next(const struct tlvdb *tlvdb) +{ + if (tlvdb->children) + return tlvdb->children; + + while (tlvdb) { + if (tlvdb->next) + return tlvdb->next; + + tlvdb = tlvdb->parent; + } + + return NULL; +} + +const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev) +{ + if (prev) { +// tlvdb = tlvdb_next(container_of(prev, struct tlvdb, tag)); + tlvdb = tlvdb_next((struct tlvdb *)prev); + } + + + while (tlvdb) { + if (tlvdb->tag.tag == tag) + return &tlvdb->tag; + + tlvdb = tlvdb_next(tlvdb); + } + + return NULL; +} + +unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) +{ + size_t size = tlv->len; + unsigned char *data; + size_t pos; + + if (tlv->tag > 0x100) + size += 2; + else + size += 1; + + if (tlv->len > 0x7f) + size += 2; + else + size += 1; + + data = malloc(size); + if (!data) { + *len = 0; + return NULL; + } + + pos = 0; + + if (tlv->tag > 0x100) { + data[pos++] = tlv->tag >> 8; + data[pos++] = tlv->tag & 0xff; + } else + data[pos++] = tlv->tag; + + if (tlv->len > 0x7f) { + data[pos++] = 0x81; + data[pos++] = tlv->len; + } else + data[pos++] = tlv->len; + + memcpy(data + pos, tlv->value, tlv->len); + pos += tlv->len; + + *len = pos; + return data; +} + +bool tlv_is_constructed(const struct tlv *tlv) +{ + return (tlv->tag < 0x100 ? tlv->tag : tlv->tag >> 8) & TLV_TAG_COMPLEX; +} + +bool tlv_equal(const struct tlv *a, const struct tlv *b) +{ + if (!a && !b) + return true; + + if (!a || !b) + return false; + + return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len); +} diff --git a/client/emv/tlv.h b/client/emv/tlv.h new file mode 100644 index 00000000..3fd3f347 --- /dev/null +++ b/client/emv/tlv.h @@ -0,0 +1,52 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * https://github.com/lumag/emv-tools/blob/master/lib/include/openemv/tlv.h + */ + +#ifndef TLV_H +#define TLV_H + +#include +#include +#include + +typedef uint16_t tlv_tag_t; + +struct tlv { + tlv_tag_t tag; + size_t len; + const unsigned char *value; +}; + +struct tlvdb; +typedef bool (*tlv_cb)(void *data, const struct tlv *tlv); + +struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value); +struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value); +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); + +void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); + +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data); +const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); + +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); +bool tlv_is_constructed(const struct tlv *tlv); +bool tlv_equal(const struct tlv *a, const struct tlv *b); + +#endif -- 2.39.5