X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/a2bb2735d5095aabeba4a891fe4ba867a9afc2b3..137dd5826ed8d69b8eb00f1a6bcdbbecf6b072e2:/client/emv/emv_tags.c diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c index 9bcf2004..a7c11c3e 100644 --- a/client/emv/emv_tags.c +++ b/client/emv/emv_tags.c @@ -22,15 +22,17 @@ #include +#define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} + enum emv_tag_t { EMV_TAG_GENERIC, EMV_TAG_BITMASK, EMV_TAG_DOL, EMV_TAG_CVM_LIST, + EMV_TAG_AFL, EMV_TAG_STRING, EMV_TAG_NUMERIC, EMV_TAG_YYMMDD, - EMV_TAG_FCI, }; struct emv_tag { @@ -56,7 +58,7 @@ static const struct emv_tag_bit EMV_AIP[] = { { 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, 8), "MSD is supported (Magnetic Stripe Data)" }, { 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" }, @@ -112,8 +114,44 @@ static const struct emv_tag_bit EMV_TVR[] = { EMV_BIT_FINISH, }; +static const struct emv_tag_bit EMV_CTQ[] = { + { EMV_BIT(1, 8), "Online PIN Required" }, + { EMV_BIT(1, 7), "Signature Required" }, + { EMV_BIT(1, 6), "Go Online if Offline Data Authentication Fails and Reader is online capable" }, + { EMV_BIT(1, 5), "Switch Interface if Offline Data Authentication fails and Reader supports VIS" }, + { EMV_BIT(1, 4), "Go Online if Application Expired" }, + { EMV_BIT(1, 3), "Switch Interface for Cash Transactions" }, + { EMV_BIT(1, 2), "Switch Interface for Cashback Transactions" }, + { EMV_BIT(2, 8), "Consumer Device CVM Performed" }, + { EMV_BIT(2, 7), "Card supports Issuer Update Processing at the POS" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_TTQ[] = { + { EMV_BIT(1, 8), "MSD supported" }, + { EMV_BIT(1, 7), "VSDC supported" }, + { EMV_BIT(1, 6), "qVSDC supported" }, + { EMV_BIT(1, 5), "EMV contact chip supported" }, + { EMV_BIT(1, 4), "Offline-only reader" }, + { EMV_BIT(1, 3), "Online PIN supported" }, + { EMV_BIT(1, 2), "Signature supported" }, + { EMV_BIT(1, 1), "Offline Data Authentication (ODA) for Online Authorizations supported\nWarning!!!! Readers compliant to this specification set TTQ byte 1 bit 1 (this field) to 0b" }, + { EMV_BIT(2, 8), "Online cryptogram required" }, + { EMV_BIT(2, 7), "CVM required" }, + { EMV_BIT(2, 6), "(Contact Chip) Offline PIN supported" }, + { EMV_BIT(3, 8), "Issuer Update Processing supported" }, + { EMV_BIT(3, 7), "Mobile functionality supported (Consumer Device CVM)" }, + EMV_BIT_FINISH, +}; + +// All Data Elements by Tags used in TLV structure (according to the EMV 4.2 Standard ) +// https://www.eftlab.co.uk/index.php/site-map/knowledge-base/145-emv-nfc-tags +// http://dexterous-programmer.blogspot.in/2012/05/emv-tags.html static const struct emv_tag emv_tags[] = { { 0x00 , "Unknown ???" }, + { 0x01 , "", EMV_TAG_STRING }, // string for headers + { 0x41 , "Country code and national data" }, + { 0x42 , "Issuer Identification Number (IIN)" }, { 0x4f , "Application Dedicated File (ADF) Name" }, { 0x50 , "Application Label", EMV_TAG_STRING }, { 0x56 , "Track 1 Data" }, @@ -128,7 +166,7 @@ static const struct emv_tag emv_tags[] = { { 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 }, + { 0x6f , "File Control Information (FCI) Template" }, { 0x70 , "READ RECORD Response Message Template" }, { 0x77 , "Response Message Template Format 2" }, { 0x80 , "Response Message Template Format 1" }, @@ -146,12 +184,13 @@ static const struct emv_tag emv_tags[] = { { 0x91 , "Issuer Authentication Data" }, { 0x92 , "Issuer Public Key Remainder" }, { 0x93 , "Signed Static Application Data" }, - { 0x94 , "Application File Locator (AFL)" }, + { 0x94 , "Application File Locator (AFL)", EMV_TAG_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, }, + { 0x9f06, "Application Identifier (AID), Terminal. ISO 7816-5" }, { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC }, { 0x9f08, "Application Version Number" }, { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, @@ -167,6 +206,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f21, "Transaction Time" }, { 0x9f26, "Application Cryptogram" }, { 0x9f27, "Cryptogram Information Data" }, + { 0x9f2a, "Kernel Identifier" }, { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, { 0x9f2f, "ICC PIN Encipherment Public Key Remainder" }, @@ -192,9 +232,12 @@ static const struct emv_tag emv_tags[] = { { 0x9f63, "PUNATC(Track1)" }, { 0x9f64, "NATC(Track1)" }, { 0x9f65, "PCVC3(Track2)" }, - { 0x9f66, "PUNATC(Track2)" }, - { 0x9f67, "NATC(Track2)" }, + { 0x9f66, "PUNATC(Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ }, + { 0x9f67, "NATC(Track2) / MSD Offset" }, + { 0x9f69, "Card Authentication Related Data" }, + { 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC }, { 0x9f6b, "Track 2 Data" }, + { 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ }, { 0xa5 , "File Control Information (FCI) Proprietary Template" }, { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, }; @@ -231,25 +274,28 @@ static const char *bitstrings[] = { "1.......", }; -static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { 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]; + PRINT_INDENT(level); fprintf(f, "\tByte %u (%02x)\n", byte, val); for (bit = 8; bit > 0; bit--, val <<= 1) { - if (val & 0x80) + if (val & 0x80){ + PRINT_INDENT(level); 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) +static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { const unsigned char *buf = tlv->value; size_t left = tlv->len; @@ -259,36 +305,19 @@ static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, F const struct emv_tag *doltag; if (!tlv_parse_tl(&buf, &left, &doltlv)) { + PRINT_INDENT(level); fprintf(f, "Invalid Tag-Len\n"); continue; } doltag = emv_get_tag(&doltlv); + PRINT_INDENT(level); 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) +static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { fprintf(f, "\tString value '"); fwrite(tlv->value, 1, tlv->len, f); @@ -326,13 +355,15 @@ static unsigned long emv_value_numeric(const struct tlv *tlv, unsigned start, un return ret; } -static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +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)); } -static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, FILE *f) +static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + PRINT_INDENT(level); fprintf(f, "\tDate: 20%02ld.%ld.%ld\n", emv_value_numeric(tlv, 0, 2), emv_value_numeric(tlv, 2, 4), @@ -344,12 +375,13 @@ 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) +static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { uint32_t X, Y; int i; if (tlv->len < 10 || tlv->len % 2) { + PRINT_INDENT(level); fprintf(f, "\tINVALID!\n"); return; } @@ -357,7 +389,9 @@ static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *t X = emv_get_binary(tlv->value); Y = emv_get_binary(tlv->value + 4); + PRINT_INDENT(level); fprintf(f, "\tX: %d\n", X); + PRINT_INDENT(level); fprintf(f, "\tY: %d\n", Y); for (i = 8; i < tlv->len; i+= 2) { @@ -433,13 +467,27 @@ static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *t break; } + PRINT_INDENT(level); 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) +static void emv_tag_dump_afl(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level){ + if (tlv->len < 4 || tlv->len % 4) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + for (int i = 0; i < tlv->len / 4; i++) { + PRINT_INDENT(level); + fprintf(f, "SFI[%02x] start:%02x end:%02x offline:%02x\n", tlv->value[i * 4 + 0] >> 3, tlv->value[i * 4 + 1], tlv->value[i * 4 + 2], tlv->value[i * 4 + 3]); + } +} + +bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) { if (!tlv) { fprintf(f, "NULL\n"); @@ -448,31 +496,37 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f) const struct emv_tag *tag = emv_get_tag(tlv); - fprintf(f, "--%2hx[%02zx] '%s':\n", tlv->tag, tlv->len, tag->name); + PRINT_INDENT(level); + fprintf(f, "--%2hx[%02zx] '%s':", tlv->tag, tlv->len, tag->name); switch (tag->type) { case EMV_TAG_GENERIC: + fprintf(f, "\n"); break; case EMV_TAG_BITMASK: - emv_tag_dump_bitmask(tlv, tag, f); + fprintf(f, "\n"); + emv_tag_dump_bitmask(tlv, tag, f, level); break; case EMV_TAG_DOL: - emv_tag_dump_dol(tlv, tag, f); + fprintf(f, "\n"); + emv_tag_dump_dol(tlv, tag, f, level); break; case EMV_TAG_CVM_LIST: - emv_tag_dump_cvm_list(tlv, tag, f); + fprintf(f, "\n"); + emv_tag_dump_cvm_list(tlv, tag, f, level); + break; + case EMV_TAG_AFL: + fprintf(f, "\n"); + emv_tag_dump_afl(tlv, tag, f, level); break; case EMV_TAG_STRING: - emv_tag_dump_string(tlv, tag, f); + emv_tag_dump_string(tlv, tag, f, level); break; case EMV_TAG_NUMERIC: - emv_tag_dump_numeric(tlv, tag, f); + emv_tag_dump_numeric(tlv, tag, f, level); break; case EMV_TAG_YYMMDD: - emv_tag_dump_yymmdd(tlv, tag, f); - break; - case EMV_TAG_FCI: - emv_tag_dump_fci(tlv, tag, f); + emv_tag_dump_yymmdd(tlv, tag, f, level); break; };