diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c new file mode 100644 index 000000000..fbdd714a3 --- /dev/null +++ b/client/emv/apduinfo.c @@ -0,0 +1,318 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// APDU status bytes information +//----------------------------------------------------------------------------- + +#include "apduinfo.h" + +const APDUCode APDUCodeTable[] = { + // ID Type Description + {"XXXX", APDUCODE_TYPE_NONE, ""}, // blank string + {"6---", APDUCODE_TYPE_ERROR, "Class not supported."}, + {"61--", APDUCODE_TYPE_INFO, "Response bytes still available"}, + {"61XX", APDUCODE_TYPE_INFO, "Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE."}, + {"62--", APDUCODE_TYPE_WARNING, "State of non-volatile memory unchanged"}, + {"6200", APDUCODE_TYPE_WARNING, "No information given (NV-Ram not changed)"}, + {"6201", APDUCODE_TYPE_WARNING, "NV-Ram not changed 1."}, + {"6281", APDUCODE_TYPE_WARNING, "Part of returned data may be corrupted"}, + {"6282", APDUCODE_TYPE_WARNING, "End of file/record reached before reading Le bytes"}, + {"6283", APDUCODE_TYPE_WARNING, "Selected file invalidated"}, + {"6284", APDUCODE_TYPE_WARNING, "Selected file is not valid. FCI not formated according to ISO"}, + {"6285", APDUCODE_TYPE_WARNING, "No input data available from a sensor on the card. No Purse Engine enslaved for R3bc"}, + {"62A2", APDUCODE_TYPE_WARNING, "Wrong R-MAC"}, + {"62A4", APDUCODE_TYPE_WARNING, "Card locked (during reset( ))"}, + {"62CX", APDUCODE_TYPE_WARNING, "Counter with value x (command dependent)"}, + {"62F1", APDUCODE_TYPE_WARNING, "Wrong C-MAC"}, + {"62F3", APDUCODE_TYPE_WARNING, "Internal reset"}, + {"62F5", APDUCODE_TYPE_WARNING, "Default agent locked"}, + {"62F7", APDUCODE_TYPE_WARNING, "Cardholder locked"}, + {"62F8", APDUCODE_TYPE_WARNING, "Basement is current agent"}, + {"62F9", APDUCODE_TYPE_WARNING, "CALC Key Set not unblocked"}, + {"62FX", APDUCODE_TYPE_WARNING, "-"}, + {"62XX", APDUCODE_TYPE_WARNING, "RFU"}, + {"63--", APDUCODE_TYPE_WARNING, "State of non-volatile memory changed"}, + {"6300", APDUCODE_TYPE_WARNING, "No information given (NV-Ram changed)"}, + {"6381", APDUCODE_TYPE_WARNING, "File filled up by the last write. Loading/updating is not allowed."}, + {"6382", APDUCODE_TYPE_WARNING, "Card key not supported."}, + {"6383", APDUCODE_TYPE_WARNING, "Reader key not supported."}, + {"6384", APDUCODE_TYPE_WARNING, "Plaintext transmission not supported."}, + {"6385", APDUCODE_TYPE_WARNING, "Secured transmission not supported."}, + {"6386", APDUCODE_TYPE_WARNING, "Volatile memory is not available."}, + {"6387", APDUCODE_TYPE_WARNING, "Non-volatile memory is not available."}, + {"6388", APDUCODE_TYPE_WARNING, "Key number not valid."}, + {"6389", APDUCODE_TYPE_WARNING, "Key length is not correct."}, + {"63C0", APDUCODE_TYPE_WARNING, "Verify fail, no try left."}, + {"63C1", APDUCODE_TYPE_WARNING, "Verify fail, 1 try left."}, + {"63C2", APDUCODE_TYPE_WARNING, "Verify fail, 2 tries left."}, + {"63C3", APDUCODE_TYPE_WARNING, "Verify fail, 3 tries left."}, + {"63CX", APDUCODE_TYPE_WARNING, "The counter has reached the value 'x' (0 = x = 15) (command dependent)."}, + {"63F1", APDUCODE_TYPE_WARNING, "More data expected."}, + {"63F2", APDUCODE_TYPE_WARNING, "More data expected and proactive command pending."}, + {"63FX", APDUCODE_TYPE_WARNING, "-"}, + {"63XX", APDUCODE_TYPE_WARNING, "RFU"}, + {"64--", APDUCODE_TYPE_ERROR, "State of non-volatile memory unchanged"}, + {"6400", APDUCODE_TYPE_ERROR, "No information given (NV-Ram not changed)"}, + {"6401", APDUCODE_TYPE_ERROR, "Command timeout. Immediate response required by the card."}, + {"64XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"65--", APDUCODE_TYPE_ERROR, "State of non-volatile memory changed"}, + {"6500", APDUCODE_TYPE_ERROR, "No information given"}, + {"6501", APDUCODE_TYPE_ERROR, "Write error. Memory failure. There have been problems in writing or reading the EEPROM. Other hardware problems may also bring this error."}, + {"6581", APDUCODE_TYPE_ERROR, "Memory failure"}, + {"65FX", APDUCODE_TYPE_ERROR, "-"}, + {"65XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"66--", APDUCODE_TYPE_SECURITY, " "}, + {"6600", APDUCODE_TYPE_SECURITY, "Error while receiving (timeout)"}, + {"6601", APDUCODE_TYPE_SECURITY, "Error while receiving (character parity error)"}, + {"6602", APDUCODE_TYPE_SECURITY, "Wrong checksum"}, + {"6603", APDUCODE_TYPE_SECURITY, "The current DF file without FCI"}, + {"6604", APDUCODE_TYPE_SECURITY, "No SF or KF under the current DF"}, + {"6669", APDUCODE_TYPE_SECURITY, "Incorrect Encryption/Decryption Padding"}, + {"66XX", APDUCODE_TYPE_SECURITY, "-"}, + {"67--", APDUCODE_TYPE_ERROR, " "}, + {"6700", APDUCODE_TYPE_ERROR, "Wrong length"}, + {"67XX", APDUCODE_TYPE_ERROR, "length incorrect (procedure)(ISO 7816-3)"}, + {"68--", APDUCODE_TYPE_ERROR, "Functions in CLA not supported"}, + {"6800", APDUCODE_TYPE_ERROR, "No information given (The request function is not supported by the card)"}, + {"6881", APDUCODE_TYPE_ERROR, "Logical channel not supported"}, + {"6882", APDUCODE_TYPE_ERROR, "Secure messaging not supported"}, + {"6883", APDUCODE_TYPE_ERROR, "Last command of the chain expected"}, + {"6884", APDUCODE_TYPE_ERROR, "Command chaining not supported"}, + {"68FX", APDUCODE_TYPE_ERROR, "-"}, + {"68XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"69--", APDUCODE_TYPE_ERROR, "Command not allowed"}, + {"6900", APDUCODE_TYPE_ERROR, "No information given (Command not allowed)"}, + {"6901", APDUCODE_TYPE_ERROR, "Command not accepted (inactive state)"}, + {"6981", APDUCODE_TYPE_ERROR, "Command incompatible with file structure"}, + {"6982", APDUCODE_TYPE_ERROR, "Security condition not satisfied."}, + {"6983", APDUCODE_TYPE_ERROR, "Authentication method blocked"}, + {"6984", APDUCODE_TYPE_ERROR, "Referenced data reversibly blocked (invalidated)"}, + {"6985", APDUCODE_TYPE_ERROR, "Conditions of use not satisfied."}, + {"6986", APDUCODE_TYPE_ERROR, "Command not allowed (no current EF)"}, + {"6987", APDUCODE_TYPE_ERROR, "Expected secure messaging (SM) object missing"}, + {"6988", APDUCODE_TYPE_ERROR, "Incorrect secure messaging (SM) data object"}, + {"698D", APDUCODE_TYPE_NONE, "Reserved"}, + {"6996", APDUCODE_TYPE_ERROR, "Data must be updated again"}, + {"69E1", APDUCODE_TYPE_ERROR, "POL1 of the currently Enabled Profile prevents this action."}, + {"69F0", APDUCODE_TYPE_ERROR, "Permission Denied"}, + {"69F1", APDUCODE_TYPE_ERROR, "Permission Denied - Missing Privilege"}, + {"69FX", APDUCODE_TYPE_ERROR, "-"}, + {"69XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"6A--", APDUCODE_TYPE_ERROR, "Wrong parameter(s) P1-P2"}, + {"6A00", APDUCODE_TYPE_ERROR, "No information given (Bytes P1 and/or P2 are incorrect)"}, + {"6A80", APDUCODE_TYPE_ERROR, "The parameters in the data field are incorrect."}, + {"6A81", APDUCODE_TYPE_ERROR, "Function not supported"}, + {"6A82", APDUCODE_TYPE_ERROR, "File not found"}, + {"6A83", APDUCODE_TYPE_ERROR, "Record not found"}, + {"6A84", APDUCODE_TYPE_ERROR, "There is insufficient memory space in record or file"}, + {"6A85", APDUCODE_TYPE_ERROR, "Lc inconsistent with TLV structure"}, + {"6A86", APDUCODE_TYPE_ERROR, "Incorrect P1 or P2 parameter."}, + {"6A87", APDUCODE_TYPE_ERROR, "Lc inconsistent with P1-P2"}, + {"6A88", APDUCODE_TYPE_ERROR, "Referenced data not found"}, + {"6A89", APDUCODE_TYPE_ERROR, "File already exists"}, + {"6A8A", APDUCODE_TYPE_ERROR, "DF name already exists."}, + {"6AF0", APDUCODE_TYPE_ERROR, "Wrong parameter value"}, + {"6AFX", APDUCODE_TYPE_ERROR, "-"}, + {"6AXX", APDUCODE_TYPE_ERROR, "RFU"}, + {"6B--", APDUCODE_TYPE_ERROR, " "}, + {"6B00", APDUCODE_TYPE_ERROR, "Wrong parameter(s) P1-P2"}, + {"6BXX", APDUCODE_TYPE_ERROR, "Reference incorrect (procedure byte), (ISO 7816-3)"}, + {"6C--", APDUCODE_TYPE_ERROR, "Wrong length Le"}, + {"6C00", APDUCODE_TYPE_ERROR, "Incorrect P3 length."}, + {"6CXX", APDUCODE_TYPE_ERROR, "Bad length value in Le; 'xx' is the correct exact Le"}, + {"6D--", APDUCODE_TYPE_ERROR, " "}, + {"6D00", APDUCODE_TYPE_ERROR, "Instruction code not supported or invalid"}, + {"6DXX", APDUCODE_TYPE_ERROR, "Instruction code not programmed or invalid (procedure byte), (ISO 7816-3)"}, + {"6E--", APDUCODE_TYPE_ERROR, " "}, + {"6E00", APDUCODE_TYPE_ERROR, "Class not supported"}, + {"6EXX", APDUCODE_TYPE_ERROR, "Instruction class not supported (procedure byte), (ISO 7816-3)"}, + {"6F--", APDUCODE_TYPE_ERROR, "Internal exception"}, + {"6F00", APDUCODE_TYPE_ERROR, "Command aborted - more exact diagnosis not possible (e.g., operating system error)."}, + {"6FFF", APDUCODE_TYPE_ERROR, "Card dead (overuse, …)"}, + {"6FXX", APDUCODE_TYPE_ERROR, "No precise diagnosis (procedure byte), (ISO 7816-3)"}, + {"9---", APDUCODE_TYPE_NONE, ""}, + {"9000", APDUCODE_TYPE_INFO, "Command successfully executed (OK)."}, + {"9004", APDUCODE_TYPE_WARNING, "PIN not succesfully verified, 3 or more PIN tries left"}, + {"9008", APDUCODE_TYPE_NONE, "Key/file not found"}, + {"9080", APDUCODE_TYPE_WARNING, "Unblock Try Counter has reached zero"}, + {"9100", APDUCODE_TYPE_NONE, "OK"}, + {"9101", APDUCODE_TYPE_NONE, "States.activity, States.lock Status or States.lockable has wrong value"}, + {"9102", APDUCODE_TYPE_NONE, "Transaction number reached its limit"}, + {"910C", APDUCODE_TYPE_NONE, "No changes"}, + {"910E", APDUCODE_TYPE_NONE, "Insufficient NV-Memory to complete command"}, + {"911C", APDUCODE_TYPE_NONE, "Command code not supported"}, + {"911E", APDUCODE_TYPE_NONE, "CRC or MAC does not match data"}, + {"9140", APDUCODE_TYPE_NONE, "Invalid key number specified"}, + {"917E", APDUCODE_TYPE_NONE, "Length of command string invalid"}, + {"919D", APDUCODE_TYPE_NONE, "Not allow the requested command"}, + {"919E", APDUCODE_TYPE_NONE, "Value of the parameter invalid"}, + {"91A0", APDUCODE_TYPE_NONE, "Requested AID not present on PICC"}, + {"91A1", APDUCODE_TYPE_NONE, "Unrecoverable error within application"}, + {"91AE", APDUCODE_TYPE_NONE, "Authentication status does not allow the requested command"}, + {"91AF", APDUCODE_TYPE_NONE, "Additional data frame is expected to be sent"}, + {"91BE", APDUCODE_TYPE_NONE, "Out of boundary"}, + {"91C1", APDUCODE_TYPE_NONE, "Unrecoverable error within PICC"}, + {"91CA", APDUCODE_TYPE_NONE, "Previous Command was not fully completed"}, + {"91CD", APDUCODE_TYPE_NONE, "PICC was disabled by an unrecoverable error"}, + {"91CE", APDUCODE_TYPE_NONE, "Number of Applications limited to 28"}, + {"91DE", APDUCODE_TYPE_NONE, "File or application already exists"}, + {"91EE", APDUCODE_TYPE_NONE, "Could not complete NV-write operation due to loss of power"}, + {"91F0", APDUCODE_TYPE_NONE, "Specified file number does not exist"}, + {"91F1", APDUCODE_TYPE_NONE, "Unrecoverable error within file"}, + {"920x", APDUCODE_TYPE_INFO, "Writing to EEPROM successful after 'x' attempts."}, + {"9210", APDUCODE_TYPE_ERROR, "Insufficient memory. No more storage available."}, + {"9240", APDUCODE_TYPE_ERROR, "Writing to EEPROM not successful."}, + {"9301", APDUCODE_TYPE_NONE, "Integrity error"}, + {"9302", APDUCODE_TYPE_NONE, "Candidate S2 invalid"}, + {"9303", APDUCODE_TYPE_ERROR, "Application is permanently locked"}, + {"9400", APDUCODE_TYPE_ERROR, "No EF selected."}, + {"9401", APDUCODE_TYPE_NONE, "Candidate currency code does not match purse currency"}, + {"9402", APDUCODE_TYPE_NONE, "Candidate amount too high"}, + {"9402", APDUCODE_TYPE_ERROR, "Address range exceeded."}, + {"9403", APDUCODE_TYPE_NONE, "Candidate amount too low"}, + {"9404", APDUCODE_TYPE_ERROR, "FID not found, record not found or comparison pattern not found."}, + {"9405", APDUCODE_TYPE_NONE, "Problems in the data field"}, + {"9406", APDUCODE_TYPE_ERROR, "Required MAC unavailable"}, + {"9407", APDUCODE_TYPE_NONE, "Bad currency : purse engine has no slot with R3bc currency"}, + {"9408", APDUCODE_TYPE_NONE, "R3bc currency not supported in purse engine"}, + {"9408", APDUCODE_TYPE_ERROR, "Selected file type does not match command."}, + {"9580", APDUCODE_TYPE_NONE, "Bad sequence"}, + {"9681", APDUCODE_TYPE_NONE, "Slave not found"}, + {"9700", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 1 or 2"}, + {"9702", APDUCODE_TYPE_NONE, "Main keys are blocked"}, + {"9704", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 3 or more PIN tries left"}, + {"9784", APDUCODE_TYPE_NONE, "Base key"}, + {"9785", APDUCODE_TYPE_NONE, "Limit exceeded - C-MAC key"}, + {"9786", APDUCODE_TYPE_NONE, "SM error - Limit exceeded - R-MAC key"}, + {"9787", APDUCODE_TYPE_NONE, "Limit exceeded - sequence counter"}, + {"9788", APDUCODE_TYPE_NONE, "Limit exceeded - R-MAC length"}, + {"9789", APDUCODE_TYPE_NONE, "Service not available"}, + {"9802", APDUCODE_TYPE_ERROR, "No PIN defined."}, + {"9804", APDUCODE_TYPE_ERROR, "Access conditions not satisfied, authentication failed."}, + {"9835", APDUCODE_TYPE_ERROR, "ASK RANDOM or GIVE RANDOM not executed."}, + {"9840", APDUCODE_TYPE_ERROR, "PIN verification not successful."}, + {"9850", APDUCODE_TYPE_ERROR, "INCREASE or DECREASE could not be executed because a limit has been reached."}, + {"9862", APDUCODE_TYPE_ERROR, "Authentication Error, application specific (incorrect MAC)"}, + {"9900", APDUCODE_TYPE_NONE, "1 PIN try left"}, + {"9904", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 1 PIN try left"}, + {"9985", APDUCODE_TYPE_NONE, "Wrong status - Cardholder lock"}, + {"9986", APDUCODE_TYPE_ERROR, "Missing privilege"}, + {"9987", APDUCODE_TYPE_NONE, "PIN is not installed"}, + {"9988", APDUCODE_TYPE_NONE, "Wrong status - R-MAC state"}, + {"9A00", APDUCODE_TYPE_NONE, "2 PIN try left"}, + {"9A04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 2 PIN try left"}, + {"9A71", APDUCODE_TYPE_NONE, "Wrong parameter value - Double agent AID"}, + {"9A72", APDUCODE_TYPE_NONE, "Wrong parameter value - Double agent Type"}, + {"9D05", APDUCODE_TYPE_ERROR, "Incorrect certificate type"}, + {"9D07", APDUCODE_TYPE_ERROR, "Incorrect session data size"}, + {"9D08", APDUCODE_TYPE_ERROR, "Incorrect DIR file record size"}, + {"9D09", APDUCODE_TYPE_ERROR, "Incorrect FCI record size"}, + {"9D0A", APDUCODE_TYPE_ERROR, "Incorrect code size"}, + {"9D10", APDUCODE_TYPE_ERROR, "Insufficient memory to load application"}, + {"9D11", APDUCODE_TYPE_ERROR, "Invalid AID"}, + {"9D12", APDUCODE_TYPE_ERROR, "Duplicate AID"}, + {"9D13", APDUCODE_TYPE_ERROR, "Application previously loaded"}, + {"9D14", APDUCODE_TYPE_ERROR, "Application history list full"}, + {"9D15", APDUCODE_TYPE_ERROR, "Application not open"}, + {"9D17", APDUCODE_TYPE_ERROR, "Invalid offset"}, + {"9D18", APDUCODE_TYPE_ERROR, "Application already loaded"}, + {"9D19", APDUCODE_TYPE_ERROR, "Invalid certificate"}, + {"9D1A", APDUCODE_TYPE_ERROR, "Invalid signature"}, + {"9D1B", APDUCODE_TYPE_ERROR, "Invalid KTU"}, + {"9D1D", APDUCODE_TYPE_ERROR, "MSM controls not set"}, + {"9D1E", APDUCODE_TYPE_ERROR, "Application signature does not exist"}, + {"9D1F", APDUCODE_TYPE_ERROR, "KTU does not exist"}, + {"9D20", APDUCODE_TYPE_ERROR, "Application not loaded"}, + {"9D21", APDUCODE_TYPE_ERROR, "Invalid Open command data length"}, + {"9D30", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (invalid start address)"}, + {"9D31", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (invalid length)"}, + {"9D32", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (illegal memory check area)"}, + {"9D40", APDUCODE_TYPE_ERROR, "Invalid MSM Controls ciphertext"}, + {"9D41", APDUCODE_TYPE_ERROR, "MSM controls already set"}, + {"9D42", APDUCODE_TYPE_ERROR, "Set MSM Controls data length less than 2 bytes"}, + {"9D43", APDUCODE_TYPE_ERROR, "Invalid MSM Controls data length"}, + {"9D44", APDUCODE_TYPE_ERROR, "Excess MSM Controls ciphertext"}, + {"9D45", APDUCODE_TYPE_ERROR, "Verification of MSM Controls data failed"}, + {"9D50", APDUCODE_TYPE_ERROR, "Invalid MCD Issuer production ID"}, + {"9D51", APDUCODE_TYPE_ERROR, "Invalid MCD Issuer ID"}, + {"9D52", APDUCODE_TYPE_ERROR, "Invalid set MSM controls data date"}, + {"9D53", APDUCODE_TYPE_ERROR, "Invalid MCD number"}, + {"9D54", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D55", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D56", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D57", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D60", APDUCODE_TYPE_ERROR, "MAC verification failed"}, + {"9D61", APDUCODE_TYPE_ERROR, "Maximum number of unblocks reached"}, + {"9D62", APDUCODE_TYPE_ERROR, "Card was not blocked"}, + {"9D63", APDUCODE_TYPE_ERROR, "Crypto functions not available"}, + {"9D64", APDUCODE_TYPE_ERROR, "No application loaded"}, + {"9E00", APDUCODE_TYPE_NONE, "PIN not installed"}, + {"9E04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN not installed"}, + {"9F00", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 3"}, + {"9F04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN blocked and Unblock Try Counter is 3"}, + {"9FXX", APDUCODE_TYPE_NONE, "Command successfully executed; 'xx' bytes of data are available and can be requested using GET RESPONSE."}, + {"9xXX", APDUCODE_TYPE_NONE, "Application related status, (ISO 7816-3)"} +}; +const size_t APDUCodeTableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); + +int CodeCmp(const char *code1, const char *code2) { + int xsymb = 0; + int cmp = 0; + for (int i = 0; i < 4; i++) { + if (code1[i] == code2[i]) + cmp++; + if (code1[i] == 'X' || code2[i] == 'X') + xsymb++; + } + if (cmp == 4) + return 0; + + if (cmp + xsymb == 4) + return xsymb; + + return -1; +} + +const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { + char buf[4] = {0}; + int res; + int mineq = 100; + int mineqindx = 0; + + sprintf(&buf[0], "%02X ", sw1); + sprintf(&buf[2], "%02X ", sw2); + + for (int i = 0; i < APDUCodeTableLen; i++) { + res = CodeCmp(APDUCodeTable[i].ID, buf); + + // equal + if (res == 0) { + return &APDUCodeTable[i]; + } + + // with some 'X' + if (res > 0 && mineq > res) { + mineq = res; + mineqindx = i; + } + } + + // if we have not equal, but with some 'X' + if (mineqindx < 100) { + return &APDUCodeTable[mineqindx]; + } + + return NULL; +} + +const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) { + const APDUCode *cd = GetAPDUCode(sw1, sw2); + if (cd) + return cd->Description; + else + return APDUCodeTable[0].Description; //empty string +} diff --git a/client/emv/apduinfo.h b/client/emv/apduinfo.h new file mode 100644 index 000000000..a3fa2049b --- /dev/null +++ b/client/emv/apduinfo.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// APDU status bytes information +//----------------------------------------------------------------------------- + +#ifndef APDUINFO_H__ +#define APDUINFO_H__ + +#include +#include +#include +#include + +#define APDUCODE_TYPE_NONE 0 +#define APDUCODE_TYPE_INFO 1 +#define APDUCODE_TYPE_WARNING 2 +#define APDUCODE_TYPE_ERROR 3 +#define APDUCODE_TYPE_SECURITY 4 + +typedef struct { + const char *ID; + const uint8_t Type; + const char *Description; +} APDUCode; + +extern const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2); +extern const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2); + +#endif diff --git a/client/emv/dump.c b/client/emv/dump.c new file mode 100644 index 000000000..9915ad737 --- /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 000000000..5976da445 --- /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 000000000..1aae847af --- /dev/null +++ b/client/emv/emv_tags.c @@ -0,0 +1,457 @@ +/* + * 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, +}; + +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" }, + { 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_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; + }; + + return true; +} diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h new file mode 100644 index 000000000..de6d9d1e7 --- /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 000000000..9264b1106 --- /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_multi(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 000000000..523b92ac5 --- /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 000000000..d78f049e0 --- /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 000000000..3fd3f3477 --- /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