Reorder client src directories
This commit is contained in:
519
client/src/emv/apduinfo.c
Normal file
519
client/src/emv/apduinfo.c
Normal file
@@ -0,0 +1,519 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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"
|
||||
|
||||
#include <string.h> // memmove
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ui.h" // Print...
|
||||
#include "util.h"
|
||||
#include "commonutil.h" // ARRAYLEN
|
||||
|
||||
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 formatted 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 successfully 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 successfully 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 successfully 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 successfully 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 successfully verified, PIN not installed"},
|
||||
{"9F00", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 3"},
|
||||
{"9F04", APDUCODE_TYPE_NONE, "PIN not successfully 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)"}
|
||||
};
|
||||
|
||||
static 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 *GetAPDUCode(uint8_t sw1, uint8_t sw2) {
|
||||
char buf[6] = {0};
|
||||
int mineq = ARRAYLEN(APDUCodeTable);
|
||||
int mineqindx = 0;
|
||||
|
||||
sprintf(buf, "%02X%02X", sw1, sw2);
|
||||
|
||||
for (int i = 0; i < ARRAYLEN(APDUCodeTable); i++) {
|
||||
int 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 < ARRAYLEN(APDUCodeTable)) {
|
||||
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
|
||||
}
|
||||
|
||||
int APDUDecode(uint8_t *data, int len, APDUStruct *apdu) {
|
||||
ExtAPDUHeader *hapdu = (ExtAPDUHeader *)data;
|
||||
|
||||
apdu->cla = hapdu->cla;
|
||||
apdu->ins = hapdu->ins;
|
||||
apdu->p1 = hapdu->p1;
|
||||
apdu->p2 = hapdu->p2;
|
||||
|
||||
apdu->lc = 0;
|
||||
apdu->data = NULL;
|
||||
apdu->le = 0;
|
||||
apdu->extended_apdu = false;
|
||||
apdu->case_type = 0x00;
|
||||
|
||||
uint8_t b0 = hapdu->lc[0];
|
||||
|
||||
// case 1
|
||||
if (len == 4) {
|
||||
apdu->case_type = 0x01;
|
||||
}
|
||||
|
||||
// case 2S (Le)
|
||||
if (len == 5) {
|
||||
apdu->case_type = 0x02;
|
||||
apdu->le = b0;
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x100;
|
||||
}
|
||||
|
||||
// case 3S (Lc + data)
|
||||
if (len == 5U + b0 && b0 != 0) {
|
||||
apdu->case_type = 0x03;
|
||||
apdu->lc = b0;
|
||||
}
|
||||
|
||||
// case 4S (Lc + data + Le)
|
||||
if (len == 5U + b0 + 1U && b0 != 0) {
|
||||
apdu->case_type = 0x04;
|
||||
apdu->lc = b0;
|
||||
apdu->le = data[len - 1];
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x100;
|
||||
}
|
||||
|
||||
// extended length apdu
|
||||
if (len >= 7 && b0 == 0) {
|
||||
uint16_t extlen = (hapdu->lc[1] << 8) + hapdu->lc[2];
|
||||
|
||||
// case 2E (Le) - extended
|
||||
if (len == 7) {
|
||||
apdu->case_type = 0x12;
|
||||
apdu->extended_apdu = true;
|
||||
apdu->le = extlen;
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x10000;
|
||||
}
|
||||
|
||||
// case 3E (Lc + data) - extended
|
||||
if (len == 7U + extlen) {
|
||||
apdu->case_type = 0x13;
|
||||
apdu->extended_apdu = true;
|
||||
apdu->lc = extlen;
|
||||
}
|
||||
|
||||
// case 4E (Lc + data + Le) - extended 2-byte Le
|
||||
if (len == 7U + extlen + 2U) {
|
||||
apdu->case_type = 0x14;
|
||||
apdu->extended_apdu = true;
|
||||
apdu->lc = extlen;
|
||||
apdu->le = (data[len - 2] << 8) + data[len - 1];
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x10000;
|
||||
}
|
||||
|
||||
// case 4E (Lc + data + Le) - extended 3-byte Le
|
||||
if (len == 7U + extlen + 3U && data[len - 3] == 0) {
|
||||
apdu->case_type = 0x24;
|
||||
apdu->extended_apdu = true;
|
||||
apdu->lc = extlen;
|
||||
apdu->le = (data[len - 2] << 8) + data[len - 1];
|
||||
if (!apdu->le)
|
||||
apdu->le = 0x10000;
|
||||
}
|
||||
}
|
||||
|
||||
if (!apdu->case_type)
|
||||
return 1;
|
||||
|
||||
if (apdu->lc) {
|
||||
if (apdu->extended_apdu) {
|
||||
apdu->data = data + 7;
|
||||
} else {
|
||||
apdu->data = data + 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APDUEncode(APDUStruct *apdu, uint8_t *data, int *len) {
|
||||
if (len)
|
||||
*len = 0;
|
||||
|
||||
if (apdu->le > 0x10000 || apdu->lc > 0xffff)
|
||||
return 1;
|
||||
|
||||
size_t dptr = 0;
|
||||
data[dptr++] = apdu->cla;
|
||||
data[dptr++] = apdu->ins;
|
||||
data[dptr++] = apdu->p1;
|
||||
data[dptr++] = apdu->p2;
|
||||
|
||||
if (apdu->lc) {
|
||||
if (apdu->extended_apdu || apdu->lc > 0xff || apdu->le > 0x100) {
|
||||
data[dptr++] = 0x00;
|
||||
data[dptr++] = (apdu->lc >> 8) & 0xff;
|
||||
data[dptr++] = (apdu->lc) & 0xff;
|
||||
memmove(&data[dptr], apdu->data, apdu->lc);
|
||||
dptr += apdu->lc;
|
||||
apdu->extended_apdu = true;
|
||||
} else {
|
||||
data[dptr++] = apdu->lc;
|
||||
memmove(&data[dptr], apdu->data, apdu->lc);
|
||||
dptr += apdu->lc;
|
||||
}
|
||||
}
|
||||
|
||||
if (apdu->le) {
|
||||
if (apdu->extended_apdu) {
|
||||
if (apdu->le != 0x10000) {
|
||||
data[dptr++] = 0x00;
|
||||
data[dptr++] = (apdu->le >> 8) & 0xff;
|
||||
data[dptr++] = (apdu->le) & 0xff;
|
||||
} else {
|
||||
data[dptr++] = 0x00;
|
||||
data[dptr++] = 0x00;
|
||||
data[dptr++] = 0x00;
|
||||
}
|
||||
} else {
|
||||
if (apdu->le != 0x100)
|
||||
data[dptr++] = apdu->le;
|
||||
else
|
||||
data[dptr++] = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
if (len)
|
||||
*len = dptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APDUEncodeS(sAPDU *sapdu, bool extended, uint16_t le, uint8_t *data, int *len) {
|
||||
if (extended && le > 0x100)
|
||||
return 10;
|
||||
|
||||
APDUStruct apdu;
|
||||
|
||||
apdu.cla = sapdu->CLA;
|
||||
apdu.ins = sapdu->INS;
|
||||
apdu.p1 = sapdu->P1;
|
||||
apdu.p2 = sapdu->P2;
|
||||
|
||||
apdu.lc = sapdu->Lc;
|
||||
if (sapdu->Lc)
|
||||
apdu.data = sapdu->data;
|
||||
else
|
||||
apdu.data = NULL;
|
||||
apdu.le = le;
|
||||
|
||||
apdu.extended_apdu = extended;
|
||||
apdu.case_type = 0x00;
|
||||
|
||||
return APDUEncode(&apdu, data, len);
|
||||
}
|
||||
|
||||
void APDUPrint(APDUStruct apdu) {
|
||||
APDUPrintEx(apdu, 0);
|
||||
}
|
||||
|
||||
void APDUPrintEx(APDUStruct apdu, size_t maxdatalen) {
|
||||
PrintAndLogEx(INFO, "APDU: %scase=0x%02x cla=0x%02x ins=0x%02x p1=0x%02x p2=0x%02x Lc=0x%02x(%d) Le=0x%02x(%d)",
|
||||
apdu.extended_apdu ? "[e]" : "",
|
||||
apdu.case_type,
|
||||
apdu.cla,
|
||||
apdu.ins,
|
||||
apdu.p1,
|
||||
apdu.p2,
|
||||
apdu.lc,
|
||||
apdu.lc,
|
||||
apdu.le,
|
||||
apdu.le
|
||||
);
|
||||
if (maxdatalen > 0)
|
||||
PrintAndLogEx(INFO, "data: %s%s", sprint_hex(apdu.data, MIN(apdu.lc, maxdatalen)), apdu.lc > maxdatalen ? "..." : "");
|
||||
}
|
||||
66
client/src/emv/apduinfo.h
Normal file
66
client/src/emv/apduinfo.h
Normal file
@@ -0,0 +1,66 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 "common.h"
|
||||
|
||||
#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;
|
||||
|
||||
const APDUCode *GetAPDUCode(uint8_t sw1, uint8_t sw2);
|
||||
const char *GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2);
|
||||
|
||||
typedef struct {
|
||||
uint8_t CLA;
|
||||
uint8_t INS;
|
||||
uint8_t P1;
|
||||
uint8_t P2;
|
||||
uint8_t Lc;
|
||||
uint8_t *data;
|
||||
} PACKED sAPDU;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t lc[3];
|
||||
} PACKED ExtAPDUHeader;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint16_t lc;
|
||||
uint8_t *data;
|
||||
uint32_t le;
|
||||
bool extended_apdu;
|
||||
uint8_t case_type;
|
||||
} PACKED APDUStruct;
|
||||
|
||||
extern int APDUDecode(uint8_t *data, int len, APDUStruct *apdu);
|
||||
extern int APDUEncode(APDUStruct *apdu, uint8_t *data, int *len);
|
||||
extern int APDUEncodeS(sAPDU *sapdu, bool extended, uint16_t le, uint8_t *data, int *len);
|
||||
extern void APDUPrint(APDUStruct apdu);
|
||||
extern void APDUPrintEx(APDUStruct apdu, size_t maxdatalen);
|
||||
|
||||
#endif
|
||||
2013
client/src/emv/cmdemv.c
Normal file
2013
client/src/emv/cmdemv.c
Normal file
File diff suppressed because it is too large
Load Diff
18
client/src/emv/cmdemv.h
Normal file
18
client/src/emv/cmdemv.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2017 Merlok
|
||||
// modified 2017 iceman
|
||||
// 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 commands
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef CMDEMV_H__
|
||||
#define CMDEMV_H__
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int CmdEMV(const char *Cmd);
|
||||
|
||||
#endif
|
||||
162
client/src/emv/crypto.c
Normal file
162
client/src/emv/crypto.c
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "crypto.h"
|
||||
#include "crypto_backend.h"
|
||||
|
||||
static struct crypto_backend *crypto_backend;
|
||||
|
||||
static bool crypto_init(void) {
|
||||
if (crypto_backend)
|
||||
return true;
|
||||
|
||||
crypto_backend = crypto_polarssl_init();
|
||||
|
||||
if (!crypto_backend)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash) {
|
||||
struct crypto_hash *ch;
|
||||
|
||||
if (!crypto_init())
|
||||
return NULL;
|
||||
|
||||
ch = crypto_backend->hash_open(hash);
|
||||
if (ch)
|
||||
ch->algo = hash;
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
void crypto_hash_close(struct crypto_hash *ch) {
|
||||
ch->close(ch);
|
||||
}
|
||||
|
||||
void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len) {
|
||||
ch->write(ch, buf, len);
|
||||
}
|
||||
|
||||
unsigned char *crypto_hash_read(struct crypto_hash *ch) {
|
||||
return ch->read(ch);
|
||||
}
|
||||
|
||||
size_t crypto_hash_get_size(const struct crypto_hash *ch) {
|
||||
return ch->get_size(ch);
|
||||
}
|
||||
|
||||
struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...) {
|
||||
struct crypto_pk *cp;
|
||||
va_list vl;
|
||||
|
||||
if (!crypto_init())
|
||||
return NULL;
|
||||
|
||||
va_start(vl, pk);
|
||||
cp = crypto_backend->pk_open(pk, vl);
|
||||
va_end(vl);
|
||||
|
||||
if (cp)
|
||||
cp->algo = pk;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...) {
|
||||
struct crypto_pk *cp;
|
||||
va_list vl;
|
||||
|
||||
if (!crypto_init())
|
||||
return NULL;
|
||||
|
||||
if (!crypto_backend->pk_open_priv)
|
||||
return NULL;
|
||||
|
||||
va_start(vl, pk);
|
||||
cp = crypto_backend->pk_open_priv(pk, vl);
|
||||
va_end(vl);
|
||||
|
||||
if (cp)
|
||||
cp->algo = pk;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...) {
|
||||
struct crypto_pk *cp;
|
||||
va_list vl;
|
||||
|
||||
if (!crypto_init())
|
||||
return NULL;
|
||||
|
||||
if (!crypto_backend->pk_genkey)
|
||||
return NULL;
|
||||
|
||||
va_start(vl, pk);
|
||||
cp = crypto_backend->pk_genkey(pk, vl);
|
||||
va_end(vl);
|
||||
|
||||
if (cp)
|
||||
cp->algo = pk;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
void crypto_pk_close(struct crypto_pk *cp) {
|
||||
cp->close(cp);
|
||||
}
|
||||
|
||||
unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen) {
|
||||
return cp->encrypt(cp, buf, len, clen);
|
||||
}
|
||||
|
||||
unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen) {
|
||||
if (!cp->decrypt) {
|
||||
*clen = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cp->decrypt(cp, buf, len, clen);
|
||||
}
|
||||
|
||||
enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp) {
|
||||
if (!cp)
|
||||
return PK_INVALID;
|
||||
|
||||
return cp->algo;
|
||||
}
|
||||
|
||||
size_t crypto_pk_get_nbits(const struct crypto_pk *cp) {
|
||||
if (!cp->get_nbits)
|
||||
return 0;
|
||||
|
||||
return cp->get_nbits(cp);
|
||||
}
|
||||
|
||||
unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen) {
|
||||
*plen = 0;
|
||||
|
||||
if (!cp->get_parameter)
|
||||
return NULL;
|
||||
|
||||
return cp->get_parameter(cp, param, plen);
|
||||
}
|
||||
47
client/src/emv/crypto.h
Normal file
47
client/src/emv/crypto.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 CRYPTO_H
|
||||
#define CRYPTO_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
enum crypto_algo_hash {
|
||||
HASH_INVALID,
|
||||
HASH_SHA_1,
|
||||
};
|
||||
|
||||
struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash);
|
||||
void crypto_hash_close(struct crypto_hash *ch);
|
||||
void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len);
|
||||
unsigned char *crypto_hash_read(struct crypto_hash *ch);
|
||||
size_t crypto_hash_get_size(const struct crypto_hash *ch);
|
||||
|
||||
enum crypto_algo_pk {
|
||||
PK_INVALID,
|
||||
PK_RSA,
|
||||
};
|
||||
|
||||
struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...);
|
||||
struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...);
|
||||
struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...);
|
||||
void crypto_pk_close(struct crypto_pk *cp);
|
||||
unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
|
||||
unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
|
||||
enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp);
|
||||
size_t crypto_pk_get_nbits(const struct crypto_pk *cp);
|
||||
unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen);
|
||||
|
||||
#endif
|
||||
49
client/src/emv/crypto_backend.h
Normal file
49
client/src/emv/crypto_backend.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 CRYPTO_BACKEND_H
|
||||
#define CRYPTO_BACKEND_H
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#include <stdarg.h> // va_list
|
||||
|
||||
struct crypto_hash {
|
||||
enum crypto_algo_hash algo;
|
||||
void (*write)(struct crypto_hash *ch, const unsigned char *buf, size_t len);
|
||||
unsigned char *(*read)(struct crypto_hash *ch);
|
||||
void (*close)(struct crypto_hash *ch);
|
||||
size_t (*get_size)(const struct crypto_hash *ch);
|
||||
};
|
||||
|
||||
struct crypto_pk {
|
||||
enum crypto_algo_pk algo;
|
||||
unsigned char *(*encrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
|
||||
unsigned char *(*decrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen);
|
||||
unsigned char *(*get_parameter)(const struct crypto_pk *cp, unsigned param, size_t *plen);
|
||||
size_t (*get_nbits)(const struct crypto_pk *cp);
|
||||
void (*close)(struct crypto_pk *cp);
|
||||
};
|
||||
|
||||
struct crypto_backend {
|
||||
struct crypto_hash *(*hash_open)(enum crypto_algo_hash hash);
|
||||
struct crypto_pk *(*pk_open)(enum crypto_algo_pk pk, va_list vl);
|
||||
struct crypto_pk *(*pk_open_priv)(enum crypto_algo_pk pk, va_list vl);
|
||||
struct crypto_pk *(*pk_genkey)(enum crypto_algo_pk pk, va_list vl);
|
||||
};
|
||||
|
||||
struct crypto_backend *crypto_polarssl_init(void);
|
||||
|
||||
#endif
|
||||
350
client/src/emv/crypto_polarssl.c
Normal file
350
client/src/emv/crypto_polarssl.c
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* libopenemv - a library to work with EMV family of smart cards
|
||||
* Copyright (C) 2015 Dmitry Eremin-Solenikov
|
||||
* Copyright (C) 2017 Merlok
|
||||
*
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "crypto_backend.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mbedtls/rsa.h"
|
||||
#include "mbedtls/sha1.h"
|
||||
|
||||
struct crypto_hash_polarssl {
|
||||
struct crypto_hash ch;
|
||||
mbedtls_sha1_context ctx;
|
||||
};
|
||||
|
||||
static void crypto_hash_polarssl_close(struct crypto_hash *_ch) {
|
||||
struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch;
|
||||
|
||||
free(ch);
|
||||
}
|
||||
|
||||
static void crypto_hash_polarssl_write(struct crypto_hash *_ch, const unsigned char *buf, size_t len) {
|
||||
struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch;
|
||||
|
||||
mbedtls_sha1_update(&(ch->ctx), buf, len);
|
||||
}
|
||||
|
||||
static unsigned char *crypto_hash_polarssl_read(struct crypto_hash *_ch) {
|
||||
struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch;
|
||||
|
||||
static unsigned char sha1sum[20];
|
||||
mbedtls_sha1_finish(&(ch->ctx), sha1sum);
|
||||
return sha1sum;
|
||||
}
|
||||
|
||||
static size_t crypto_hash_polarssl_get_size(const struct crypto_hash *ch) {
|
||||
if (ch->algo == HASH_SHA_1)
|
||||
return 20;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct crypto_hash *crypto_hash_polarssl_open(enum crypto_algo_hash hash) {
|
||||
if (hash != HASH_SHA_1)
|
||||
return NULL;
|
||||
|
||||
struct crypto_hash_polarssl *ch = malloc(sizeof(*ch));
|
||||
|
||||
mbedtls_sha1_starts(&(ch->ctx));
|
||||
|
||||
ch->ch.write = crypto_hash_polarssl_write;
|
||||
ch->ch.read = crypto_hash_polarssl_read;
|
||||
ch->ch.close = crypto_hash_polarssl_close;
|
||||
ch->ch.get_size = crypto_hash_polarssl_get_size;
|
||||
|
||||
return &ch->ch;
|
||||
}
|
||||
|
||||
struct crypto_pk_polarssl {
|
||||
struct crypto_pk cp;
|
||||
mbedtls_rsa_context ctx;
|
||||
};
|
||||
|
||||
static struct crypto_pk *crypto_pk_polarssl_open_rsa(va_list vl) {
|
||||
struct crypto_pk_polarssl *cp = malloc(sizeof(*cp));
|
||||
memset(cp, 0x00, sizeof(*cp));
|
||||
|
||||
char *mod = va_arg(vl, char *); // N
|
||||
int modlen = va_arg(vl, size_t);
|
||||
char *exp = va_arg(vl, char *); // E
|
||||
int explen = va_arg(vl, size_t);
|
||||
|
||||
mbedtls_rsa_init(&cp->ctx, MBEDTLS_RSA_PKCS_V15, 0);
|
||||
|
||||
cp->ctx.len = modlen; // size(N) in bytes
|
||||
mbedtls_mpi_read_binary(&cp->ctx.N, (const unsigned char *)mod, modlen);
|
||||
mbedtls_mpi_read_binary(&cp->ctx.E, (const unsigned char *)exp, explen);
|
||||
|
||||
int res = mbedtls_rsa_check_pubkey(&cp->ctx);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "PolarSSL public key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen);
|
||||
free(cp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &cp->cp;
|
||||
}
|
||||
|
||||
static struct crypto_pk *crypto_pk_polarssl_open_priv_rsa(va_list vl) {
|
||||
struct crypto_pk_polarssl *cp = malloc(sizeof(*cp));
|
||||
memset(cp, 0x00, sizeof(*cp));
|
||||
char *mod = va_arg(vl, char *);
|
||||
int modlen = va_arg(vl, size_t);
|
||||
char *exp = va_arg(vl, char *);
|
||||
int explen = va_arg(vl, size_t);
|
||||
char *d = va_arg(vl, char *);
|
||||
int dlen = va_arg(vl, size_t);
|
||||
char *p = va_arg(vl, char *);
|
||||
int plen = va_arg(vl, size_t);
|
||||
char *q = va_arg(vl, char *);
|
||||
int qlen = va_arg(vl, size_t);
|
||||
char *dp = va_arg(vl, char *);
|
||||
int dplen = va_arg(vl, size_t);
|
||||
char *dq = va_arg(vl, char *);
|
||||
int dqlen = va_arg(vl, size_t);
|
||||
// calc QP via Q and P
|
||||
// char *inv = va_arg(vl, char *);
|
||||
// int invlen = va_arg(vl, size_t);
|
||||
|
||||
mbedtls_rsa_init(&cp->ctx, MBEDTLS_RSA_PKCS_V15, 0);
|
||||
|
||||
cp->ctx.len = modlen; // size(N) in bytes
|
||||
mbedtls_mpi_read_binary(&cp->ctx.N, (const unsigned char *)mod, modlen);
|
||||
mbedtls_mpi_read_binary(&cp->ctx.E, (const unsigned char *)exp, explen);
|
||||
|
||||
mbedtls_mpi_read_binary(&cp->ctx.D, (const unsigned char *)d, dlen);
|
||||
mbedtls_mpi_read_binary(&cp->ctx.P, (const unsigned char *)p, plen);
|
||||
mbedtls_mpi_read_binary(&cp->ctx.Q, (const unsigned char *)q, qlen);
|
||||
mbedtls_mpi_read_binary(&cp->ctx.DP, (const unsigned char *)dp, dplen);
|
||||
mbedtls_mpi_read_binary(&cp->ctx.DQ, (const unsigned char *)dq, dqlen);
|
||||
|
||||
int res = mbedtls_mpi_inv_mod(&cp->ctx.QP, &cp->ctx.Q, &cp->ctx.P);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "PolarSSL private key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen);
|
||||
free(cp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = mbedtls_rsa_check_privkey(&cp->ctx);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "PolarSSL private key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen);
|
||||
free(cp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &cp->cp;
|
||||
}
|
||||
|
||||
static int myrand(void *rng_state, unsigned char *output, size_t len) {
|
||||
size_t i;
|
||||
rng_state = NULL;
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
output[i] = rand();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct crypto_pk *crypto_pk_polarssl_genkey_rsa(va_list vl) {
|
||||
struct crypto_pk_polarssl *cp = malloc(sizeof(*cp));
|
||||
memset(cp, 0x00, sizeof(*cp));
|
||||
|
||||
int transient = va_arg(vl, int);
|
||||
unsigned int nbits = va_arg(vl, unsigned int);
|
||||
unsigned int exp = va_arg(vl, unsigned int);
|
||||
|
||||
if (transient) {
|
||||
}
|
||||
|
||||
int res = mbedtls_rsa_gen_key(&cp->ctx, &myrand, NULL, nbits, exp);
|
||||
if (res) {
|
||||
fprintf(stderr, "PolarSSL private key generation error res=%x exp=%u nbits=%u.\n", res * -1, exp, nbits);
|
||||
free(cp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &cp->cp;
|
||||
}
|
||||
|
||||
static void crypto_pk_polarssl_close(struct crypto_pk *_cp) {
|
||||
struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
|
||||
|
||||
mbedtls_rsa_free(&cp->ctx);
|
||||
free(cp);
|
||||
}
|
||||
|
||||
static unsigned char *crypto_pk_polarssl_encrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen) {
|
||||
struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
|
||||
int res;
|
||||
unsigned char *result;
|
||||
|
||||
*clen = 0;
|
||||
size_t keylen = mbedtls_mpi_size(&cp->ctx.N);
|
||||
|
||||
result = malloc(keylen);
|
||||
if (!result) {
|
||||
printf("RSA encrypt failed. Can't allocate result memory.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = mbedtls_rsa_public(&cp->ctx, buf, result);
|
||||
if (res) {
|
||||
printf("RSA encrypt failed. Error: %x data len: %zu key len: %zu\n", res * -1, len, keylen);
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*clen = keylen;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned char *crypto_pk_polarssl_decrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen) {
|
||||
struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
|
||||
int res;
|
||||
unsigned char *result;
|
||||
|
||||
*clen = 0;
|
||||
size_t keylen = mbedtls_mpi_size(&cp->ctx.N);
|
||||
|
||||
result = malloc(keylen);
|
||||
if (!result) {
|
||||
printf("RSA encrypt failed. Can't allocate result memory.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = mbedtls_rsa_private(&cp->ctx, NULL, NULL, buf, result); // CHECK???
|
||||
if (res) {
|
||||
printf("RSA decrypt failed. Error: %x data len: %zu key len: %zu\n", res * -1, len, keylen);
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*clen = keylen;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t crypto_pk_polarssl_get_nbits(const struct crypto_pk *_cp) {
|
||||
struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
|
||||
|
||||
return cp->ctx.len * 8;
|
||||
}
|
||||
|
||||
static unsigned char *crypto_pk_polarssl_get_parameter(const struct crypto_pk *_cp, unsigned param, size_t *plen) {
|
||||
struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp;
|
||||
unsigned char *result = NULL;
|
||||
int res;
|
||||
switch (param) {
|
||||
// mod
|
||||
case 0:
|
||||
*plen = mbedtls_mpi_size(&cp->ctx.N);
|
||||
result = malloc(*plen);
|
||||
memset(result, 0x00, *plen);
|
||||
res = mbedtls_mpi_write_binary(&cp->ctx.N, result, *plen);
|
||||
if (res < 0) {
|
||||
printf("Error write_binary.");
|
||||
free(result);
|
||||
result = 0;
|
||||
}
|
||||
break;
|
||||
// exp
|
||||
case 1:
|
||||
*plen = mbedtls_mpi_size(&cp->ctx.E);
|
||||
result = malloc(*plen);
|
||||
memset(result, 0x00, *plen);
|
||||
res = mbedtls_mpi_write_binary(&cp->ctx.E, result, *plen);
|
||||
if (res < 0) {
|
||||
printf("Error write_binary.");
|
||||
free(result);
|
||||
result = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Error get parameter. Param = %u", param);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct crypto_pk *crypto_pk_polarssl_open(enum crypto_algo_pk pk, va_list vl) {
|
||||
struct crypto_pk *cp;
|
||||
|
||||
if (pk == PK_RSA)
|
||||
cp = crypto_pk_polarssl_open_rsa(vl);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
cp->close = crypto_pk_polarssl_close;
|
||||
cp->encrypt = crypto_pk_polarssl_encrypt;
|
||||
cp->get_parameter = crypto_pk_polarssl_get_parameter;
|
||||
cp->get_nbits = crypto_pk_polarssl_get_nbits;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
static struct crypto_pk *crypto_pk_polarssl_open_priv(enum crypto_algo_pk pk, va_list vl) {
|
||||
struct crypto_pk *cp;
|
||||
|
||||
if (pk == PK_RSA)
|
||||
cp = crypto_pk_polarssl_open_priv_rsa(vl);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
cp->close = crypto_pk_polarssl_close;
|
||||
cp->encrypt = crypto_pk_polarssl_encrypt;
|
||||
cp->decrypt = crypto_pk_polarssl_decrypt;
|
||||
cp->get_parameter = crypto_pk_polarssl_get_parameter;
|
||||
cp->get_nbits = crypto_pk_polarssl_get_nbits;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
static struct crypto_pk *crypto_pk_polarssl_genkey(enum crypto_algo_pk pk, va_list vl) {
|
||||
struct crypto_pk *cp;
|
||||
|
||||
if (pk == PK_RSA)
|
||||
cp = crypto_pk_polarssl_genkey_rsa(vl);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
cp->close = crypto_pk_polarssl_close;
|
||||
cp->encrypt = crypto_pk_polarssl_encrypt;
|
||||
cp->decrypt = crypto_pk_polarssl_decrypt;
|
||||
cp->get_parameter = crypto_pk_polarssl_get_parameter;
|
||||
cp->get_nbits = crypto_pk_polarssl_get_nbits;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
static struct crypto_backend crypto_polarssl_backend = {
|
||||
.hash_open = crypto_hash_polarssl_open,
|
||||
.pk_open = crypto_pk_polarssl_open,
|
||||
.pk_open_priv = crypto_pk_polarssl_open_priv,
|
||||
.pk_genkey = crypto_pk_polarssl_genkey,
|
||||
};
|
||||
|
||||
struct crypto_backend *crypto_polarssl_init(void) {
|
||||
return &crypto_polarssl_backend;
|
||||
}
|
||||
132
client/src/emv/dol.c
Normal file
132
client/src/emv/dol.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "emv/dol.h"
|
||||
#include "emv/tlv.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static size_t dol_calculate_len(const struct tlv *tlv, size_t data_len) {
|
||||
if (!tlv)
|
||||
return 0;
|
||||
|
||||
const unsigned char *buf = tlv->value;
|
||||
size_t left = tlv->len;
|
||||
size_t count = 0;
|
||||
|
||||
while (left) {
|
||||
struct tlv cur_tlv;
|
||||
if (!tlv_parse_tl(&buf, &left, &cur_tlv))
|
||||
return 0;
|
||||
|
||||
count += cur_tlv.len;
|
||||
|
||||
/* Last tag can be of variable length */
|
||||
if (cur_tlv.len == 0 && left == 0)
|
||||
count = data_len;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag) {
|
||||
size_t res_len;
|
||||
if (!tlv || !(res_len = dol_calculate_len(tlv, 0))) {
|
||||
struct tlv *res_tlv = malloc(sizeof(*res_tlv));
|
||||
|
||||
res_tlv->tag = tag;
|
||||
res_tlv->len = 0;
|
||||
res_tlv->value = NULL;
|
||||
|
||||
return res_tlv;
|
||||
}
|
||||
|
||||
struct tlv *res_tlv = malloc(sizeof(*res_tlv) + res_len);
|
||||
if (!res_tlv)
|
||||
return NULL;
|
||||
|
||||
const unsigned char *buf = tlv->value;
|
||||
size_t left = tlv->len;
|
||||
unsigned char *res = (unsigned char *)(res_tlv + 1);
|
||||
size_t pos = 0;
|
||||
|
||||
while (left) {
|
||||
struct tlv cur_tlv;
|
||||
if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) {
|
||||
free(res_tlv);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct tlv *tag_tlv = tlvdb_get(tlvdb, cur_tlv.tag, NULL);
|
||||
if (!tag_tlv) {
|
||||
memset(res + pos, 0, cur_tlv.len);
|
||||
} else if (tag_tlv->len > cur_tlv.len) {
|
||||
memcpy(res + pos, tag_tlv->value, cur_tlv.len);
|
||||
} else {
|
||||
// FIXME: cn data should be padded with 0xFF !!!
|
||||
memcpy(res + pos, tag_tlv->value, tag_tlv->len);
|
||||
memset(res + pos + tag_tlv->len, 0, cur_tlv.len - tag_tlv->len);
|
||||
}
|
||||
pos += cur_tlv.len;
|
||||
}
|
||||
|
||||
res_tlv->tag = tag;
|
||||
res_tlv->len = res_len;
|
||||
res_tlv->value = res;
|
||||
|
||||
return res_tlv;
|
||||
}
|
||||
|
||||
struct tlvdb *dol_parse(const struct tlv *tlv, const unsigned char *data, size_t data_len) {
|
||||
if (!tlv)
|
||||
return NULL;
|
||||
|
||||
const unsigned char *buf = tlv->value;
|
||||
size_t left = tlv->len;
|
||||
size_t res_len = dol_calculate_len(tlv, data_len);
|
||||
size_t pos = 0;
|
||||
struct tlvdb *db = NULL;
|
||||
|
||||
if (res_len != data_len)
|
||||
return NULL;
|
||||
|
||||
while (left) {
|
||||
struct tlv cur_tlv;
|
||||
if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) {
|
||||
tlvdb_free(db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Last tag can be of variable length */
|
||||
if (cur_tlv.len == 0 && left == 0)
|
||||
cur_tlv.len = res_len - pos;
|
||||
|
||||
struct tlvdb *tag_db = tlvdb_fixed(cur_tlv.tag, cur_tlv.len, data + pos);
|
||||
if (!db)
|
||||
db = tag_db;
|
||||
else
|
||||
tlvdb_add(db, tag_db);
|
||||
|
||||
pos += cur_tlv.len;
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
24
client/src/emv/dol.h
Normal file
24
client/src/emv/dol.h
Normal file
@@ -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 DOL_H
|
||||
#define DOL_H
|
||||
|
||||
#include "emv/tlv.h"
|
||||
|
||||
struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag);
|
||||
struct tlvdb *dol_parse(const struct tlv *tlv, const unsigned char *data, size_t data_len);
|
||||
|
||||
#endif
|
||||
57
client/src/emv/dump.c
Normal file
57
client/src/emv/dump.c
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "dump.h"
|
||||
|
||||
#ifndef PRINT_INDENT
|
||||
# define PRINT_INDENT(level) {for (int myi = 0; myi < (level); myi++) fprintf(f, " ");}
|
||||
#endif
|
||||
|
||||
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 level) {
|
||||
int i, j;
|
||||
|
||||
if (!f)
|
||||
f = stdout;
|
||||
|
||||
for (i = 0; i < len; i += 16) {
|
||||
PRINT_INDENT(level);
|
||||
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");
|
||||
}
|
||||
}
|
||||
27
client/src/emv/dump.h
Normal file
27
client/src/emv/dump.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
#include <stdio.h> // FILE
|
||||
|
||||
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, int level);
|
||||
void dump_buffer_tab(const unsigned char *ptr, size_t len, FILE *f, int tabs);
|
||||
|
||||
#endif
|
||||
524
client/src/emv/emv_pk.c
Normal file
524
client/src/emv/emv_pk.c
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
/* For asprintf */
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "emv_pk.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ui.h"
|
||||
#include "crypto.h"
|
||||
#include "proxmark3.h"
|
||||
#include "fileutils.h"
|
||||
#include "pm3_cmd.h"
|
||||
|
||||
#define BCD(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
|
||||
-1)
|
||||
|
||||
#define HEX(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
|
||||
((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
|
||||
((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \
|
||||
-1)
|
||||
|
||||
#define TOHEX(v) ((v) < 10 ? (v) + '0' : (v) - 10 + 'a')
|
||||
|
||||
static ssize_t emv_pk_read_bin(char *buf, size_t buflen, unsigned char *bin, size_t size, size_t *read) {
|
||||
|
||||
if (buf == NULL)
|
||||
return 0;
|
||||
|
||||
size_t left = size;
|
||||
char *p = buf;
|
||||
while ((*p == ' ') && (p < (buf + buflen - 1)))
|
||||
p++;
|
||||
|
||||
while (left > 0) {
|
||||
int c1, c2;
|
||||
c1 = HEX(*p);
|
||||
if (c1 == -1)
|
||||
return -(p - buf);
|
||||
if (p == (buf + buflen - 1))
|
||||
return -(p - buf);
|
||||
p++;
|
||||
c2 = HEX(*p);
|
||||
if (c2 == -1)
|
||||
return -(p - buf);
|
||||
if (p == (buf + buflen - 1))
|
||||
return -(p - buf);
|
||||
p++;
|
||||
*bin = (c1 * 16 + c2);
|
||||
bin ++;
|
||||
left --;
|
||||
if ((*p == ':') && (p < (buf + buflen - 1)))
|
||||
p++;
|
||||
else if (read) {
|
||||
*read = (size - left);
|
||||
break;
|
||||
} else if (left == 0)
|
||||
break;
|
||||
else
|
||||
return -(p - buf);
|
||||
}
|
||||
|
||||
while ((*p == ' ') && (p < (buf + buflen - 1)))
|
||||
p++;
|
||||
|
||||
p--;
|
||||
|
||||
return (p - buf);
|
||||
}
|
||||
|
||||
static ssize_t emv_pk_read_ymv(char *buf, size_t buflen, unsigned *ymv) {
|
||||
|
||||
if (buf == NULL)
|
||||
return 0;
|
||||
|
||||
int i;
|
||||
unsigned char temp[3];
|
||||
char *p = buf;
|
||||
|
||||
*ymv = 0;
|
||||
|
||||
while ((*p == ' ') && (p < (buf + buflen - 1)))
|
||||
p++;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
int c1, c2;
|
||||
c1 = BCD(*p);
|
||||
if (c1 == -1)
|
||||
return -(p - buf);
|
||||
if (p == (buf + buflen - 1))
|
||||
return -(p - buf);
|
||||
p++;
|
||||
c2 = BCD(*p);
|
||||
if (c2 == -1)
|
||||
return -(p - buf);
|
||||
if (p == (buf + buflen - 1))
|
||||
return -(p - buf);
|
||||
p++;
|
||||
temp[i] = (c1 * 16 + c2);
|
||||
}
|
||||
|
||||
while ((*p == ' ') && (p < (buf + buflen - 1)))
|
||||
p++;
|
||||
|
||||
p--;
|
||||
|
||||
if (temp[1] > 0x12 || temp[2] > 0x31)
|
||||
return -(p - buf);
|
||||
|
||||
*ymv = (temp[0] * 0x10000 + temp[1] * 0x100 + temp[2]);
|
||||
|
||||
return (p - buf);
|
||||
}
|
||||
|
||||
static ssize_t emv_pk_read_string(char *buf, size_t buflen, char *str, size_t size) {
|
||||
|
||||
if (buf == NULL)
|
||||
return 0;
|
||||
|
||||
char *p = buf;
|
||||
while ((*p == ' ') && (p < (buf + buflen - 1)))
|
||||
p++;
|
||||
|
||||
while (size > 1) {
|
||||
if (*p == ' ')
|
||||
break;
|
||||
else if (*p < 0x20 || *p >= 0x7f)
|
||||
return -(p - buf);
|
||||
*str = *p;
|
||||
if (p == (buf + buflen - 1))
|
||||
return -(p - buf);
|
||||
p++;
|
||||
str ++;
|
||||
size --;
|
||||
}
|
||||
|
||||
*str = 0;
|
||||
|
||||
while ((*p == ' ') && (p < (buf + buflen - 1)))
|
||||
p++;
|
||||
|
||||
p--;
|
||||
|
||||
return (p - buf);
|
||||
}
|
||||
|
||||
|
||||
struct emv_pk *emv_pk_parse_pk(char *buf, size_t buflen) {
|
||||
struct emv_pk *r = calloc(1, sizeof(*r));
|
||||
ssize_t l;
|
||||
char temp[10];
|
||||
|
||||
l = emv_pk_read_bin(buf, buflen, r->rid, 5, NULL);
|
||||
if (l <= 0)
|
||||
goto out;
|
||||
buf += l;
|
||||
|
||||
l = emv_pk_read_bin(buf, buflen, &r->index, 1, NULL);
|
||||
if (l <= 0)
|
||||
goto out;
|
||||
buf += l;
|
||||
|
||||
l = emv_pk_read_ymv(buf, buflen, &r->expire);
|
||||
if (l <= 0)
|
||||
goto out;
|
||||
buf += l;
|
||||
|
||||
l = emv_pk_read_string(buf, buflen, temp, sizeof(temp));
|
||||
if (l <= 0)
|
||||
goto out;
|
||||
buf += l;
|
||||
|
||||
if (!strcmp(temp, "rsa"))
|
||||
r->pk_algo = PK_RSA;
|
||||
else
|
||||
goto out;
|
||||
|
||||
l = emv_pk_read_bin(buf, buflen, r->exp, sizeof(r->exp), &r->elen);
|
||||
if (l <= 0)
|
||||
goto out;
|
||||
buf += l;
|
||||
|
||||
r->modulus = malloc(2048 / 8);
|
||||
l = emv_pk_read_bin(buf, buflen, r->modulus, 2048 / 8, &r->mlen);
|
||||
if (l <= 0)
|
||||
goto out2;
|
||||
buf += l;
|
||||
|
||||
l = emv_pk_read_string(buf, buflen, temp, sizeof(temp));
|
||||
if (l <= 0)
|
||||
goto out2;
|
||||
buf += l;
|
||||
|
||||
if (!strcmp(temp, "sha1"))
|
||||
r->hash_algo = HASH_SHA_1;
|
||||
else
|
||||
goto out2;
|
||||
|
||||
l = emv_pk_read_bin(buf, buflen, r->hash, 20, NULL);
|
||||
if (l <= 0)
|
||||
goto out2;
|
||||
|
||||
return r;
|
||||
|
||||
out2:
|
||||
free(r->modulus);
|
||||
out:
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t emv_pk_write_bin(char *out, size_t outlen, const unsigned char *buf, size_t len) {
|
||||
int i;
|
||||
size_t pos = 0;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
if (outlen < len * 3)
|
||||
return 0;
|
||||
|
||||
out[pos++] = TOHEX(buf[0] >> 4);
|
||||
out[pos++] = TOHEX(buf[0] & 0xf);
|
||||
for (i = 1; i < len; i++) {
|
||||
out[pos++] = ':';
|
||||
out[pos++] = TOHEX(buf[i] >> 4);
|
||||
out[pos++] = TOHEX(buf[i] & 0xf);
|
||||
}
|
||||
out[pos++] = ' ';
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static size_t emv_pk_write_str(char *out, size_t outlen, const char *str) {
|
||||
size_t len = strlen(str);
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
if (outlen < len)
|
||||
return 0;
|
||||
|
||||
memcpy(out, str, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
char *emv_pk_dump_pk(const struct emv_pk *pk) {
|
||||
size_t outsize = 1024; /* should be enough */
|
||||
char *out = malloc(outsize); /* should be enough */
|
||||
size_t outpos = 0;
|
||||
size_t rc;
|
||||
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->rid, 5);
|
||||
if (rc == 0)
|
||||
goto err;
|
||||
outpos += rc;
|
||||
|
||||
rc = emv_pk_write_bin(out + outpos, outsize - outpos, &pk->index, 1);
|
||||
if (rc == 0)
|
||||
goto err;
|
||||
outpos += rc;
|
||||
|
||||
if (outpos + 7 > outsize)
|
||||
goto err;
|
||||
out[outpos++] = TOHEX((pk->expire >> 20) & 0xf);
|
||||
out[outpos++] = TOHEX((pk->expire >> 16) & 0xf);
|
||||
out[outpos++] = TOHEX((pk->expire >> 12) & 0xf);
|
||||
out[outpos++] = TOHEX((pk->expire >> 8) & 0xf);
|
||||
out[outpos++] = TOHEX((pk->expire >> 4) & 0xf);
|
||||
out[outpos++] = TOHEX((pk->expire >> 0) & 0xf);
|
||||
out[outpos++] = ' ';
|
||||
|
||||
if (pk->pk_algo == PK_RSA) {
|
||||
rc = emv_pk_write_str(out + outpos, outsize - outpos, "rsa");
|
||||
if (rc == 0)
|
||||
goto err;
|
||||
outpos += rc;
|
||||
out[outpos++] = ' ';
|
||||
} else {
|
||||
if (outpos + 4 > outsize)
|
||||
goto err;
|
||||
out[outpos++] = '?';
|
||||
out[outpos++] = '?';
|
||||
out[outpos++] = TOHEX(pk->pk_algo >> 4);
|
||||
out[outpos++] = TOHEX(pk->pk_algo & 0xf);
|
||||
}
|
||||
|
||||
rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->exp, pk->elen);
|
||||
if (rc == 0)
|
||||
goto err;
|
||||
outpos += rc;
|
||||
|
||||
rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->modulus, pk->mlen);
|
||||
if (rc == 0)
|
||||
goto err;
|
||||
outpos += rc;
|
||||
|
||||
if (pk->hash_algo == HASH_SHA_1) {
|
||||
rc = emv_pk_write_str(out + outpos, outsize - outpos, "sha1");
|
||||
if (rc == 0)
|
||||
goto err;
|
||||
outpos += rc;
|
||||
out[outpos++] = ' ';
|
||||
} else {
|
||||
if (outpos + 4 > outsize)
|
||||
goto err;
|
||||
out[outpos++] = '?';
|
||||
out[outpos++] = '?';
|
||||
out[outpos++] = TOHEX(pk->pk_algo >> 4);
|
||||
out[outpos++] = TOHEX(pk->pk_algo & 0xf);
|
||||
}
|
||||
|
||||
|
||||
rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->hash, 20);
|
||||
if (rc == 0)
|
||||
goto err;
|
||||
outpos += rc;
|
||||
|
||||
out[outpos - 1] = '\0';
|
||||
|
||||
return out;
|
||||
|
||||
err:
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool emv_pk_verify(const struct emv_pk *pk) {
|
||||
struct crypto_hash *ch = crypto_hash_open(pk->hash_algo);
|
||||
if (!ch)
|
||||
return false;
|
||||
|
||||
crypto_hash_write(ch, pk->rid, sizeof(pk->rid));
|
||||
crypto_hash_write(ch, &pk->index, 1);
|
||||
crypto_hash_write(ch, pk->modulus, pk->mlen);
|
||||
crypto_hash_write(ch, pk->exp, pk->elen);
|
||||
|
||||
unsigned char *h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t hsize = crypto_hash_get_size(ch);
|
||||
bool r = hsize && !memcmp(h, pk->hash, hsize) ? true : false;
|
||||
|
||||
crypto_hash_close(ch);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
struct emv_pk *emv_pk_new(size_t modlen, size_t explen) {
|
||||
struct emv_pk *pk;
|
||||
|
||||
/* Not supported ATM */
|
||||
if (explen > 3)
|
||||
return NULL;
|
||||
|
||||
pk = calloc(1, sizeof(*pk));
|
||||
if (!pk)
|
||||
return NULL;
|
||||
|
||||
pk->mlen = modlen;
|
||||
pk->elen = explen;
|
||||
|
||||
pk->modulus = calloc(modlen, 1);
|
||||
if (!pk->modulus) {
|
||||
free(pk);
|
||||
pk = NULL;
|
||||
}
|
||||
|
||||
return pk;
|
||||
}
|
||||
|
||||
void emv_pk_free(struct emv_pk *pk) {
|
||||
if (!pk)
|
||||
return;
|
||||
|
||||
free(pk->modulus);
|
||||
free(pk);
|
||||
}
|
||||
|
||||
static struct emv_pk *emv_pk_get_ca_pk_from_file(const char *fname,
|
||||
const unsigned char *rid,
|
||||
unsigned char idx) {
|
||||
if (!fname)
|
||||
return NULL;
|
||||
|
||||
FILE *f = fopen(fname, "r");
|
||||
if (!f) {
|
||||
PrintAndLogEx(ERR, "Error: can't open file %s.", fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (!feof(f)) {
|
||||
char buf[2048];
|
||||
if (fgets(buf, sizeof(buf), f) == NULL)
|
||||
break;
|
||||
|
||||
struct emv_pk *pk = emv_pk_parse_pk(buf, sizeof(buf));
|
||||
if (!pk)
|
||||
continue;
|
||||
|
||||
if (memcmp(pk->rid, rid, 5) || pk->index != idx) {
|
||||
emv_pk_free(pk);
|
||||
continue;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return pk;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx) {
|
||||
if (!dirname)
|
||||
dirname = ".";//openemv_config_get_str("capk.dir", NULL);
|
||||
|
||||
char *filename;
|
||||
int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx_%02hhx.0",
|
||||
dirname,
|
||||
rid[0],
|
||||
rid[1],
|
||||
rid[2],
|
||||
rid[3],
|
||||
rid[4],
|
||||
idx);
|
||||
|
||||
if (ret <= 0)
|
||||
return NULL;
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid) {
|
||||
if (!dirname)
|
||||
dirname = "."; //openemv_config_get_str("capk.dir", NULL);
|
||||
|
||||
char *filename;
|
||||
int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx.pks",
|
||||
dirname,
|
||||
rid[0],
|
||||
rid[1],
|
||||
rid[2],
|
||||
rid[3],
|
||||
rid[4]);
|
||||
|
||||
if (ret <= 0)
|
||||
return NULL;
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx) {
|
||||
struct emv_pk *pk = NULL;
|
||||
|
||||
/* if (!pk) {
|
||||
char *fname = emv_pk_get_ca_pk_file(NULL, rid, idx);
|
||||
if (fname) {
|
||||
pk = emv_pk_get_ca_pk_from_file(fname, rid, idx);
|
||||
free(fname);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pk) {
|
||||
char *fname = emv_pk_get_ca_pk_rid_file(NULL, rid);
|
||||
if (fname) {
|
||||
pk = emv_pk_get_ca_pk_from_file(fname, rid, idx);
|
||||
free(fname);
|
||||
}
|
||||
}
|
||||
*/
|
||||
char *path;
|
||||
if (searchFile(&path, RESOURCES_SUBDIR, "capk", ".txt", false) != PM3_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
pk = emv_pk_get_ca_pk_from_file(path, rid, idx);
|
||||
free(path);
|
||||
|
||||
if (!pk)
|
||||
return NULL;
|
||||
|
||||
printf("Verifying CA PK for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zu bits...",
|
||||
pk->rid[0],
|
||||
pk->rid[1],
|
||||
pk->rid[2],
|
||||
pk->rid[3],
|
||||
pk->rid[4],
|
||||
pk->index,
|
||||
pk->mlen * 8);
|
||||
|
||||
if (emv_pk_verify(pk)) {
|
||||
printf("OK\n");
|
||||
return pk;
|
||||
}
|
||||
|
||||
printf("Failed!\n");
|
||||
emv_pk_free(pk);
|
||||
return NULL;
|
||||
}
|
||||
47
client/src/emv/emv_pk.h
Normal file
47
client/src/emv/emv_pk.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 EMV_PK_H
|
||||
#define EMV_PK_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct emv_pk {
|
||||
unsigned char rid[5];
|
||||
unsigned char index;
|
||||
unsigned char serial[3];
|
||||
unsigned char pan[10];
|
||||
unsigned char hash_algo;
|
||||
unsigned char pk_algo;
|
||||
unsigned char hash[20];
|
||||
unsigned char exp[3];
|
||||
size_t elen;
|
||||
size_t mlen;
|
||||
unsigned char *modulus;
|
||||
unsigned int expire;
|
||||
};
|
||||
|
||||
#define EXPIRE(yy, mm, dd) 0x ## yy ## mm ## dd
|
||||
|
||||
struct emv_pk *emv_pk_parse_pk(char *buf, size_t buflen);
|
||||
struct emv_pk *emv_pk_new(size_t modlen, size_t explen);
|
||||
void emv_pk_free(struct emv_pk *pk);
|
||||
char *emv_pk_dump_pk(const struct emv_pk *pk);
|
||||
bool emv_pk_verify(const struct emv_pk *pk);
|
||||
|
||||
char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx);
|
||||
char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid);
|
||||
struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx);
|
||||
#endif
|
||||
575
client/src/emv/emv_pki.c
Normal file
575
client/src/emv/emv_pki.c
Normal file
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "emv_pki.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "crypto.h"
|
||||
#include "dump.h"
|
||||
#include "util.h"
|
||||
|
||||
static bool strictExecution = true;
|
||||
void PKISetStrictExecution(bool se) {
|
||||
strictExecution = se;
|
||||
}
|
||||
|
||||
static const unsigned char empty_tlv_value[] = {};
|
||||
static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value};
|
||||
|
||||
static size_t emv_pki_hash_psn[256] = { 0, 0, 11, 2, 17, 2, };
|
||||
|
||||
static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk,
|
||||
uint8_t msgtype,
|
||||
size_t *len,
|
||||
const struct tlv *cert_tlv,
|
||||
int tlv_count,
|
||||
... /* A list of tlv pointers */
|
||||
) {
|
||||
struct crypto_pk *kcp;
|
||||
unsigned char *data;
|
||||
size_t data_len;
|
||||
va_list vl;
|
||||
|
||||
if (!enc_pk)
|
||||
return NULL;
|
||||
|
||||
if (!cert_tlv) {
|
||||
printf("ERROR: Can't find certificate\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cert_tlv->len != enc_pk->mlen) {
|
||||
printf("ERROR: Certificate length (%zu) not equal key length (%zu)\n", cert_tlv->len, enc_pk->mlen);
|
||||
return NULL;
|
||||
}
|
||||
kcp = crypto_pk_open(enc_pk->pk_algo,
|
||||
enc_pk->modulus, enc_pk->mlen,
|
||||
enc_pk->exp, enc_pk->elen);
|
||||
if (!kcp)
|
||||
return NULL;
|
||||
|
||||
data = crypto_pk_encrypt(kcp, cert_tlv->value, cert_tlv->len, &data_len);
|
||||
crypto_pk_close(kcp);
|
||||
|
||||
/* if (true){
|
||||
printf("Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}*/
|
||||
|
||||
if (data[data_len - 1] != 0xbc || data[0] != 0x6a || data[1] != msgtype) {
|
||||
printf("ERROR: Certificate format\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t hash_pos = emv_pki_hash_psn[msgtype];
|
||||
if (hash_pos == 0 || hash_pos > data_len) {
|
||||
printf("ERROR: Cant get hash position in the certificate\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct crypto_hash *ch;
|
||||
ch = crypto_hash_open(data[hash_pos]);
|
||||
if (!ch) {
|
||||
printf("ERROR: Cant do hash\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t hash_len = crypto_hash_get_size(ch);
|
||||
crypto_hash_write(ch, data + 1, data_len - 2 - hash_len);
|
||||
|
||||
va_start(vl, tlv_count);
|
||||
for (int i = 0; i < tlv_count; i++) {
|
||||
const struct tlv *add_tlv = va_arg(vl, const struct tlv *);
|
||||
if (!add_tlv)
|
||||
continue;
|
||||
|
||||
crypto_hash_write(ch, add_tlv->value, add_tlv->len);
|
||||
}
|
||||
va_end(vl);
|
||||
|
||||
uint8_t hash[hash_len];
|
||||
memset(hash, 0, hash_len);
|
||||
memcpy(hash, crypto_hash_read(ch), hash_len);
|
||||
if (memcmp(data + data_len - 1 - hash_len, hash, hash_len)) {
|
||||
printf("ERROR: Calculated wrong hash\n");
|
||||
printf("decoded: %s\n", sprint_hex(data + data_len - 1 - hash_len, hash_len));
|
||||
printf("calculated: %s\n", sprint_hex(hash, hash_len));
|
||||
|
||||
if (strictExecution) {
|
||||
crypto_hash_close(ch);
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
|
||||
*len = data_len - hash_len - 1;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static unsigned emv_cn_length(const struct tlv *tlv) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tlv->len; i++) {
|
||||
unsigned char c = tlv->value[i];
|
||||
|
||||
if (c >> 4 == 0xf)
|
||||
return 2 * i;
|
||||
|
||||
if ((c & 0xf) == 0xf)
|
||||
return 2 * i + 1;
|
||||
}
|
||||
|
||||
return 2 * tlv->len;
|
||||
}
|
||||
|
||||
static unsigned char emv_cn_get(const struct tlv *tlv, unsigned pos) {
|
||||
if (pos > tlv->len * 2)
|
||||
return 0xf;
|
||||
|
||||
unsigned char c = tlv->value[pos / 2];
|
||||
|
||||
if (pos % 2)
|
||||
return c & 0xf;
|
||||
else
|
||||
return c >> 4;
|
||||
}
|
||||
|
||||
static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk,
|
||||
unsigned char msgtype,
|
||||
const struct tlv *pan_tlv,
|
||||
const struct tlv *cert_tlv,
|
||||
const struct tlv *exp_tlv,
|
||||
const struct tlv *rem_tlv,
|
||||
const struct tlv *add_tlv,
|
||||
const struct tlv *sdatl_tlv,
|
||||
bool showData
|
||||
) {
|
||||
size_t pan_length;
|
||||
unsigned char *data;
|
||||
size_t data_len;
|
||||
size_t pk_len;
|
||||
|
||||
if (!cert_tlv || !exp_tlv || !pan_tlv)
|
||||
return NULL;
|
||||
|
||||
if (!rem_tlv)
|
||||
rem_tlv = &empty_tlv;
|
||||
|
||||
if (msgtype == 2)
|
||||
pan_length = 4;
|
||||
else if (msgtype == 4)
|
||||
pan_length = 10;
|
||||
else {
|
||||
printf("ERROR: Message type must be 2 or 4\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = emv_pki_decode_message(enc_pk, msgtype, &data_len,
|
||||
cert_tlv,
|
||||
5,
|
||||
rem_tlv,
|
||||
exp_tlv,
|
||||
add_tlv,
|
||||
sdatl_tlv,
|
||||
NULL);
|
||||
if (!data || data_len < 11 + pan_length) {
|
||||
printf("ERROR: Can't decode message\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (showData) {
|
||||
printf("Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
/* Perform the rest of checks here */
|
||||
|
||||
struct tlv pan2_tlv = {
|
||||
.tag = 0x5a,
|
||||
.len = pan_length,
|
||||
.value = &data[2],
|
||||
};
|
||||
unsigned pan_len = emv_cn_length(pan_tlv);
|
||||
unsigned pan2_len = emv_cn_length(&pan2_tlv);
|
||||
|
||||
if (((msgtype == 2) && (pan2_len < 4 || pan2_len > pan_len)) ||
|
||||
((msgtype == 4) && (pan2_len != pan_len))) {
|
||||
printf("ERROR: Invalid PAN lengths\n");
|
||||
free(data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < pan2_len; i++)
|
||||
if (emv_cn_get(pan_tlv, i) != emv_cn_get(&pan2_tlv, i)) {
|
||||
printf("ERROR: PAN data mismatch\n");
|
||||
printf("tlv pan=%s\n", sprint_hex(pan_tlv->value, pan_tlv->len));
|
||||
printf("cert pan=%s\n", sprint_hex(pan2_tlv.value, pan2_tlv.len));
|
||||
free(data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pk_len = data[9 + pan_length];
|
||||
if (pk_len > data_len - 11 - pan_length + rem_tlv->len) {
|
||||
printf("ERROR: Invalid pk length\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (exp_tlv->len != data[10 + pan_length]) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct emv_pk *pk = emv_pk_new(pk_len, exp_tlv->len);
|
||||
|
||||
memcpy(pk->rid, enc_pk->rid, 5);
|
||||
pk->index = enc_pk->index;
|
||||
|
||||
pk->hash_algo = data[7 + pan_length];
|
||||
pk->pk_algo = data[8 + pan_length];
|
||||
pk->expire = (data[3 + pan_length] << 16) | (data[2 + pan_length] << 8) | 0x31;
|
||||
memcpy(pk->serial, data + 4 + pan_length, 3);
|
||||
memcpy(pk->pan, data + 2, pan_length);
|
||||
memset(pk->pan + pan_length, 0xff, 10 - pan_length);
|
||||
|
||||
memcpy(pk->modulus, data + 11 + pan_length,
|
||||
pk_len < data_len - (11 + pan_length) ?
|
||||
pk_len :
|
||||
data_len - (11 + pan_length));
|
||||
memcpy(pk->modulus + data_len - (11 + pan_length), rem_tlv->value, rem_tlv->len);
|
||||
memcpy(pk->exp, exp_tlv->value, exp_tlv->len);
|
||||
|
||||
free(data);
|
||||
|
||||
return pk;
|
||||
}
|
||||
|
||||
static struct emv_pk *emv_pki_decode_key(const struct emv_pk *enc_pk,
|
||||
unsigned char msgtype,
|
||||
const struct tlv *pan_tlv,
|
||||
const struct tlv *cert_tlv,
|
||||
const struct tlv *exp_tlv,
|
||||
const struct tlv *rem_tlv,
|
||||
const struct tlv *add_tlv,
|
||||
const struct tlv *sdatl_tlv
|
||||
) {
|
||||
return emv_pki_decode_key_ex(enc_pk, msgtype, pan_tlv, cert_tlv, exp_tlv, rem_tlv, add_tlv, sdatl_tlv, false);
|
||||
}
|
||||
|
||||
struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db) {
|
||||
return emv_pki_decode_key(pk, 2,
|
||||
tlvdb_get(db, 0x5a, NULL),
|
||||
tlvdb_get(db, 0x90, NULL),
|
||||
tlvdb_get(db, 0x9f32, NULL),
|
||||
tlvdb_get(db, 0x92, NULL),
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv) {
|
||||
size_t sdatl_len;
|
||||
unsigned char *sdatl = emv_pki_sdatl_fill(db, &sdatl_len);
|
||||
struct tlv sda_tdata = {
|
||||
.tag = 0x00, // dummy tag
|
||||
.len = sdatl_len,
|
||||
.value = sdatl
|
||||
};
|
||||
|
||||
struct emv_pk *res = emv_pki_decode_key(pk, 4,
|
||||
tlvdb_get(db, 0x5a, NULL),
|
||||
tlvdb_get(db, 0x9f46, NULL),
|
||||
tlvdb_get(db, 0x9f47, NULL),
|
||||
tlvdb_get(db, 0x9f48, NULL),
|
||||
sda_tlv,
|
||||
&sda_tdata);
|
||||
|
||||
free(sdatl); // malloc here: emv_pki_sdatl_fill
|
||||
return res;
|
||||
}
|
||||
|
||||
struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db) {
|
||||
return emv_pki_decode_key(pk, 4,
|
||||
tlvdb_get(db, 0x5a, NULL),
|
||||
tlvdb_get(db, 0x9f2d, NULL),
|
||||
tlvdb_get(db, 0x9f2e, NULL),
|
||||
tlvdb_get(db, 0x9f2f, NULL),
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
unsigned char *emv_pki_sdatl_fill(const struct tlvdb *db, size_t *sdatl_len) {
|
||||
uint8_t buf[2048] = {0};
|
||||
size_t len = 0;
|
||||
|
||||
*sdatl_len = 0;
|
||||
|
||||
const struct tlv *sda_tl = tlvdb_get(db, 0x9f4a, NULL);
|
||||
if (!sda_tl || sda_tl->len == 0)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < sda_tl->len; i++) {
|
||||
uint32_t tag = sda_tl->value[i]; // here may be multibyte, but now not
|
||||
const struct tlv *elm = tlvdb_get(db, tag, NULL);
|
||||
if (elm) {
|
||||
memcpy(&buf[len], elm->value, elm->len);
|
||||
len += elm->len;
|
||||
}
|
||||
}
|
||||
|
||||
if (len) {
|
||||
*sdatl_len = len;
|
||||
unsigned char *value = malloc(len);
|
||||
memcpy(value, buf, len);
|
||||
return value;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData) {
|
||||
size_t data_len;
|
||||
|
||||
// Static Data Authentication Tag List
|
||||
size_t sdatl_len;
|
||||
unsigned char *sdatl = emv_pki_sdatl_fill(db, &sdatl_len);
|
||||
struct tlv sda_tdata = {
|
||||
.tag = 0x00, // dummy tag
|
||||
.len = sdatl_len,
|
||||
.value = sdatl
|
||||
};
|
||||
|
||||
unsigned char *data = emv_pki_decode_message(enc_pk, 3, &data_len,
|
||||
tlvdb_get(db, 0x93, NULL),
|
||||
3,
|
||||
sda_tlv,
|
||||
&sda_tdata,
|
||||
NULL);
|
||||
|
||||
free(sdatl); // malloc here: emv_pki_sdatl_fill
|
||||
|
||||
if (!data || data_len < 5)
|
||||
return NULL;
|
||||
|
||||
if (showData) {
|
||||
printf("Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
struct tlvdb *dac_db = tlvdb_fixed(0x9f45, 2, data + 3);
|
||||
|
||||
free(data);
|
||||
|
||||
return dac_db;
|
||||
}
|
||||
struct tlvdb *emv_pki_recover_dac(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv) {
|
||||
return emv_pki_recover_dac_ex(enc_pk, db, sda_tlv, false);
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_recover_idn(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv) {
|
||||
return emv_pki_recover_idn_ex(enc_pk, db, dyn_tlv, false);
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv, bool showData) {
|
||||
size_t data_len;
|
||||
unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
|
||||
tlvdb_get(db, 0x9f4b, NULL),
|
||||
2,
|
||||
dyn_tlv,
|
||||
NULL);
|
||||
|
||||
if (!data || data_len < 3)
|
||||
return NULL;
|
||||
|
||||
if (data[3] < 2 || data[3] > data_len - 3) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (showData) {
|
||||
printf("Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
size_t idn_len = data[4];
|
||||
if (idn_len > data[3] - 1) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 9f4c ICC Dynamic Number
|
||||
struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5);
|
||||
|
||||
free(data);
|
||||
|
||||
return idn_db;
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, bool showData) {
|
||||
size_t data_len;
|
||||
unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
|
||||
tlvdb_get(db, 0x9f4b, NULL),
|
||||
5,
|
||||
tlvdb_get(db, 0x9f37, NULL),
|
||||
tlvdb_get(db, 0x9f02, NULL),
|
||||
tlvdb_get(db, 0x5f2a, NULL),
|
||||
tlvdb_get(db, 0x9f69, NULL),
|
||||
NULL);
|
||||
|
||||
if (!data || data_len < 3)
|
||||
return NULL;
|
||||
|
||||
if (data[3] < 2 || data[3] > data_len - 3) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (showData) {
|
||||
printf("Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
size_t idn_len = data[4];
|
||||
if (idn_len > data[3] - 1) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 9f36 Application Transaction Counter (ATC)
|
||||
struct tlvdb *atc_db = tlvdb_fixed(0x9f36, idn_len, data + 5);
|
||||
|
||||
free(data);
|
||||
|
||||
return atc_db;
|
||||
}
|
||||
|
||||
static bool tlv_hash(void *data, const struct tlv *tlv, int level, bool is_leaf) {
|
||||
struct crypto_hash *ch = data;
|
||||
size_t tag_len;
|
||||
unsigned char *tag;
|
||||
|
||||
if (tlv_is_constructed(tlv))
|
||||
return true;
|
||||
|
||||
if (tlv->tag == 0x9f4b)
|
||||
return true;
|
||||
|
||||
tag = tlv_encode(tlv, &tag_len);
|
||||
crypto_hash_write(ch, tag, tag_len);
|
||||
free(tag);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db,
|
||||
const struct tlvdb *this_db,
|
||||
const struct tlv *pdol_data_tlv,
|
||||
const struct tlv *crm1_tlv,
|
||||
const struct tlv *crm2_tlv) {
|
||||
return emv_pki_perform_cda_ex(enc_pk, db, this_db, pdol_data_tlv, crm1_tlv, crm2_tlv, false);
|
||||
}
|
||||
struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct tlvdb *db,
|
||||
const struct tlvdb *this_db, // AC TLV result
|
||||
const struct tlv *pdol_data_tlv, // PDOL
|
||||
const struct tlv *crm1_tlv, // CDOL1
|
||||
const struct tlv *crm2_tlv, // CDOL2
|
||||
bool showData) {
|
||||
const struct tlv *un_tlv = tlvdb_get(db, 0x9f37, NULL);
|
||||
const struct tlv *cid_tlv = tlvdb_get(this_db, 0x9f27, NULL);
|
||||
|
||||
if (!un_tlv || !cid_tlv)
|
||||
return NULL;
|
||||
|
||||
size_t data_len = 0;
|
||||
unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len,
|
||||
tlvdb_get(this_db, 0x9f4b, NULL),
|
||||
2,
|
||||
un_tlv,
|
||||
NULL);
|
||||
if (!data || data_len < 3) {
|
||||
printf("ERROR: can't decode message. len %zu\n", data_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (showData) {
|
||||
printf("Recovered data:\n");
|
||||
dump_buffer(data, data_len, stdout, 0);
|
||||
}
|
||||
|
||||
if (data[3] < 30 || data[3] > data_len - 4) {
|
||||
printf("ERROR: Invalid data length\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!cid_tlv || cid_tlv->len != 1 || cid_tlv->value[0] != data[5 + data[4]]) {
|
||||
printf("ERROR: CID mismatch\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct crypto_hash *ch;
|
||||
ch = crypto_hash_open(enc_pk->hash_algo);
|
||||
if (!ch) {
|
||||
printf("ERROR: can't create hash\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pdol_data_tlv)
|
||||
crypto_hash_write(ch, pdol_data_tlv->value, pdol_data_tlv->len);
|
||||
if (crm1_tlv)
|
||||
crypto_hash_write(ch, crm1_tlv->value, crm1_tlv->len);
|
||||
if (crm2_tlv)
|
||||
crypto_hash_write(ch, crm2_tlv->value, crm2_tlv->len);
|
||||
|
||||
tlvdb_visit(this_db, tlv_hash, ch, 0);
|
||||
|
||||
if (memcmp(data + 5 + data[4] + 1 + 8, crypto_hash_read(ch), 20)) {
|
||||
printf("ERROR: calculated hash error\n");
|
||||
crypto_hash_close(ch);
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
crypto_hash_close(ch);
|
||||
|
||||
size_t idn_len = data[4];
|
||||
if (idn_len > data[3] - 1) {
|
||||
printf("ERROR: Invalid IDN length\n");
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5);
|
||||
free(data);
|
||||
|
||||
return idn_db;
|
||||
}
|
||||
46
client/src/emv/emv_pki.h
Normal file
46
client/src/emv/emv_pki.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 EMV_PKI_H
|
||||
#define EMV_PKI_H
|
||||
|
||||
#include "emv_pk.h"
|
||||
#include "tlv.h"
|
||||
|
||||
void PKISetStrictExecution(bool se);
|
||||
|
||||
unsigned char *emv_pki_sdatl_fill(const struct tlvdb *db, size_t *sdatl_len);
|
||||
struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db);
|
||||
struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv);
|
||||
struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db);
|
||||
|
||||
struct tlvdb *emv_pki_recover_dac(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv);
|
||||
struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData);
|
||||
struct tlvdb *emv_pki_recover_idn(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv);
|
||||
struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv, bool showData);
|
||||
struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, bool showData);
|
||||
struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db,
|
||||
const struct tlvdb *this_db,
|
||||
const struct tlv *pdol_data_tlv,
|
||||
const struct tlv *crm1_tlv,
|
||||
const struct tlv *crm2_tlv);
|
||||
struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct tlvdb *db,
|
||||
const struct tlvdb *this_db,
|
||||
const struct tlv *pdol_data_tlv,
|
||||
const struct tlv *crm1_tlv,
|
||||
const struct tlv *crm2_tlv,
|
||||
bool showData);
|
||||
|
||||
#endif
|
||||
277
client/src/emv/emv_pki_priv.c
Normal file
277
client/src/emv/emv_pki_priv.c
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "emv_pki_priv.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp,
|
||||
const unsigned char *rid, unsigned char index,
|
||||
unsigned int expire, enum crypto_algo_hash hash_algo) {
|
||||
size_t modlen, explen;
|
||||
unsigned char *mod, *exp;
|
||||
|
||||
if (!rid)
|
||||
return NULL;
|
||||
|
||||
mod = crypto_pk_get_parameter(cp, 0, &modlen);
|
||||
exp = crypto_pk_get_parameter(cp, 1, &explen);
|
||||
|
||||
if (!mod || !modlen || !exp || !explen) {
|
||||
free(mod);
|
||||
free(exp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct emv_pk *pk = emv_pk_new(modlen, explen);
|
||||
memcpy(pk->rid, rid, 5);
|
||||
pk->index = index;
|
||||
pk->expire = expire;
|
||||
pk->pk_algo = crypto_pk_get_algo(cp);
|
||||
pk->hash_algo = hash_algo;
|
||||
memcpy(pk->modulus, mod, modlen);
|
||||
memcpy(pk->exp, exp, explen);
|
||||
|
||||
free(mod);
|
||||
free(exp);
|
||||
|
||||
struct crypto_hash *ch = crypto_hash_open(pk->hash_algo);
|
||||
if (!ch) {
|
||||
emv_pk_free(pk);
|
||||
return false;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, pk->rid, sizeof(pk->rid));
|
||||
crypto_hash_write(ch, &pk->index, 1);
|
||||
crypto_hash_write(ch, pk->modulus, pk->mlen);
|
||||
crypto_hash_write(ch, pk->exp, pk->elen);
|
||||
|
||||
unsigned char *h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
emv_pk_free(pk);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(pk->hash, h, crypto_hash_get_size(ch));
|
||||
crypto_hash_close(ch);
|
||||
|
||||
return pk;
|
||||
}
|
||||
|
||||
static struct tlvdb *emv_pki_sign_message(const struct crypto_pk *cp,
|
||||
tlv_tag_t cert_tag, tlv_tag_t rem_tag,
|
||||
const unsigned char *msg, size_t msg_len,
|
||||
... /* A list of tlv pointers, end with NULL */
|
||||
) {
|
||||
size_t tmp_len = (crypto_pk_get_nbits(cp) + 7) / 8;
|
||||
unsigned char *tmp = malloc(tmp_len);
|
||||
if (!tmp)
|
||||
return NULL;
|
||||
|
||||
// XXX
|
||||
struct crypto_hash *ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(tmp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmp[0] = 0x6a;
|
||||
tmp[tmp_len - 1] = 0xbc;
|
||||
|
||||
const unsigned char *rem;
|
||||
size_t rem_len;
|
||||
size_t hash_len = crypto_hash_get_size(ch);
|
||||
size_t part_len = tmp_len - 2 - hash_len;
|
||||
if (part_len < msg_len) {
|
||||
memcpy(tmp + 1, msg, part_len);
|
||||
rem = msg + part_len;
|
||||
rem_len = msg_len - part_len;
|
||||
} else {
|
||||
memcpy(tmp + 1, msg, msg_len);
|
||||
memset(tmp + 1 + msg_len, 0xbb, part_len - msg_len);
|
||||
rem = NULL;
|
||||
rem_len = 0;
|
||||
}
|
||||
crypto_hash_write(ch, tmp + 1, part_len);
|
||||
crypto_hash_write(ch, rem, rem_len);
|
||||
|
||||
va_list vl;
|
||||
va_start(vl, msg_len);
|
||||
while (true) {
|
||||
const struct tlv *add_tlv = va_arg(vl, const struct tlv *);
|
||||
if (!add_tlv)
|
||||
break;
|
||||
|
||||
crypto_hash_write(ch, add_tlv->value, add_tlv->len);
|
||||
}
|
||||
va_end(vl);
|
||||
|
||||
unsigned char *h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
free(tmp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(tmp + 1 + part_len, h, hash_len);
|
||||
crypto_hash_close(ch);
|
||||
|
||||
size_t cert_len;
|
||||
unsigned char *cert = crypto_pk_decrypt(cp, tmp, tmp_len, &cert_len);
|
||||
free(tmp);
|
||||
|
||||
if (!cert)
|
||||
return NULL;
|
||||
|
||||
struct tlvdb *db = tlvdb_fixed(cert_tag, cert_len, cert);
|
||||
free(cert);
|
||||
if (!db)
|
||||
return NULL;
|
||||
|
||||
if (rem) {
|
||||
struct tlvdb *rdb = tlvdb_fixed(rem_tag, rem_len, rem);
|
||||
if (!rdb) {
|
||||
tlvdb_free(db);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
tlvdb_add(db, rdb);
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
static struct tlvdb *emv_pki_sign_key(const struct crypto_pk *cp,
|
||||
struct emv_pk *ipk,
|
||||
unsigned char msgtype,
|
||||
size_t pan_len,
|
||||
tlv_tag_t cert_tag,
|
||||
tlv_tag_t exp_tag,
|
||||
tlv_tag_t rem_tag,
|
||||
const struct tlv *add_tlv
|
||||
) {
|
||||
unsigned pos = 0;
|
||||
unsigned char *msg = malloc(1 + pan_len + 2 + 3 + 1 + 1 + 1 + 1 + ipk->mlen);
|
||||
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg[pos++] = msgtype;
|
||||
memcpy(msg + pos, ipk->pan, pan_len);
|
||||
pos += pan_len;
|
||||
msg[pos++] = (ipk->expire >> 8) & 0xff;
|
||||
msg[pos++] = (ipk->expire >> 16) & 0xff;
|
||||
memcpy(msg + pos, ipk->serial, 3);
|
||||
pos += 3;
|
||||
msg[pos++] = ipk->hash_algo;
|
||||
msg[pos++] = ipk->pk_algo;
|
||||
msg[pos++] = ipk->mlen;
|
||||
msg[pos++] = ipk->elen;
|
||||
memcpy(msg + pos, ipk->modulus, ipk->mlen);
|
||||
pos += ipk->mlen;
|
||||
|
||||
struct tlvdb *exp_db = tlvdb_fixed(exp_tag, ipk->elen, ipk->exp);
|
||||
if (!exp_db) {
|
||||
free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct tlvdb *db = emv_pki_sign_message(cp,
|
||||
cert_tag, rem_tag,
|
||||
msg, pos,
|
||||
tlvdb_get(exp_db, exp_tag, NULL),
|
||||
add_tlv,
|
||||
NULL);
|
||||
free(msg);
|
||||
if (!db) {
|
||||
free(exp_db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tlvdb_add(db, exp_db);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk) {
|
||||
return emv_pki_sign_key(cp, issuer_pk, 2, 4, 0x90, 0x9f32, 0x92, NULL);
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv) {
|
||||
return emv_pki_sign_key(cp, icc_pk, 4, 10, 0x9f46, 0x9f47, 0x9f48, sda_tlv);
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk) {
|
||||
return emv_pki_sign_key(cp, icc_pe_pk, 4, 10, 0x9f2d, 0x9f2e, 0x9f2f, NULL);
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv) {
|
||||
unsigned pos = 0;
|
||||
unsigned char *msg = malloc(1 + 1 + dac_tlv->len);
|
||||
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg[pos++] = 3;
|
||||
msg[pos++] = HASH_SHA_1;
|
||||
memcpy(msg + pos, dac_tlv->value, dac_tlv->len);
|
||||
pos += dac_tlv->len;
|
||||
|
||||
struct tlvdb *db = emv_pki_sign_message(cp,
|
||||
0x93, 0,
|
||||
msg, pos,
|
||||
sda_tlv,
|
||||
NULL);
|
||||
|
||||
free(msg);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv) {
|
||||
unsigned pos = 0;
|
||||
unsigned char *msg = malloc(1 + 1 + 1 + 1 + idn_tlv->len);
|
||||
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg[pos++] = 5;
|
||||
msg[pos++] = HASH_SHA_1;
|
||||
msg[pos++] = idn_tlv->len + 1;
|
||||
msg[pos++] = idn_tlv->len;
|
||||
memcpy(msg + pos, idn_tlv->value, idn_tlv->len);
|
||||
pos += idn_tlv->len;
|
||||
|
||||
struct tlvdb *db = emv_pki_sign_message(cp,
|
||||
0x9f4b, 0,
|
||||
msg, pos,
|
||||
dyn_tlv,
|
||||
NULL);
|
||||
|
||||
free(msg);
|
||||
|
||||
return db;
|
||||
}
|
||||
35
client/src/emv/emv_pki_priv.h
Normal file
35
client/src/emv/emv_pki_priv.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 EMV_PKI_PRIV_H
|
||||
#define EMV_PKI_PRIV_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "crypto.h"
|
||||
#include "emv_pk.h"
|
||||
#include "tlv.h"
|
||||
|
||||
struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp,
|
||||
const unsigned char *rid, unsigned char index,
|
||||
unsigned int expire, enum crypto_algo_hash hash_algo);
|
||||
struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk);
|
||||
struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv);
|
||||
struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk);
|
||||
|
||||
struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv);
|
||||
struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv);
|
||||
|
||||
#endif
|
||||
179
client/src/emv/emv_roca.c
Normal file
179
client/src/emv/emv_roca.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/* roca.c - ROCA (CVE-2017-15361) fingerprint checker.
|
||||
* Written by Rob Stradling (based on https://github.com/crocs-muni/roca/blob/master/roca/detect.py)
|
||||
* Copyright (C) 2017-2018 Sectigo Limited
|
||||
* modified 2018 iceman (dropped openssl bignum, now use mbedtls lib)
|
||||
* modified 2018 merlok
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//-----------------------------------------------------------------------------
|
||||
// EMV roca commands
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "emv_roca.h"
|
||||
|
||||
#include "ui.h" // Print...
|
||||
#include "mbedtls/bignum.h"
|
||||
|
||||
static uint8_t g_primes[ROCA_PRINTS_LENGTH] = {
|
||||
11, 13, 17, 19, 37, 53, 61, 71, 73, 79, 97, 103, 107, 109, 127, 151, 157
|
||||
};
|
||||
|
||||
mbedtls_mpi g_prints[ROCA_PRINTS_LENGTH];
|
||||
|
||||
static void rocacheck_init(void) {
|
||||
|
||||
for (int i = 0; i < ROCA_PRINTS_LENGTH; i++)
|
||||
mbedtls_mpi_init(&g_prints[i]);
|
||||
|
||||
mbedtls_mpi_read_string(&g_prints[0], 10, "1026");
|
||||
mbedtls_mpi_read_string(&g_prints[1], 10, "5658");
|
||||
mbedtls_mpi_read_string(&g_prints[2], 10, "107286");
|
||||
mbedtls_mpi_read_string(&g_prints[3], 10, "199410");
|
||||
mbedtls_mpi_read_string(&g_prints[4], 10, "67109890");
|
||||
mbedtls_mpi_read_string(&g_prints[5], 10, "5310023542746834");
|
||||
mbedtls_mpi_read_string(&g_prints[6], 10, "1455791217086302986");
|
||||
mbedtls_mpi_read_string(&g_prints[7], 10, "20052041432995567486");
|
||||
mbedtls_mpi_read_string(&g_prints[8], 10, "6041388139249378920330");
|
||||
mbedtls_mpi_read_string(&g_prints[9], 10, "207530445072488465666");
|
||||
mbedtls_mpi_read_string(&g_prints[10], 10, "79228162521181866724264247298");
|
||||
mbedtls_mpi_read_string(&g_prints[11], 10, "1760368345969468176824550810518");
|
||||
mbedtls_mpi_read_string(&g_prints[12], 10, "50079290986288516948354744811034");
|
||||
mbedtls_mpi_read_string(&g_prints[13], 10, "473022961816146413042658758988474");
|
||||
mbedtls_mpi_read_string(&g_prints[14], 10, "144390480366845522447407333004847678774");
|
||||
mbedtls_mpi_read_string(&g_prints[15], 10, "1800793591454480341970779146165214289059119882");
|
||||
mbedtls_mpi_read_string(&g_prints[16], 10, "126304807362733370595828809000324029340048915994");
|
||||
}
|
||||
|
||||
static void rocacheck_cleanup(void) {
|
||||
for (int i = 0; i < ROCA_PRINTS_LENGTH; i++)
|
||||
mbedtls_mpi_free(&g_prints[i]);
|
||||
}
|
||||
|
||||
static int bitand_is_zero(mbedtls_mpi *a, mbedtls_mpi *b) {
|
||||
|
||||
for (int i = 0; i < mbedtls_mpi_bitlen(a); i++) {
|
||||
|
||||
if (mbedtls_mpi_get_bit(a, i) && mbedtls_mpi_get_bit(b, i))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static mbedtls_mpi_uint mpi_get_uint(const mbedtls_mpi *X) {
|
||||
|
||||
if (X->n == 1 && X->s > 0) {
|
||||
return X->p[0];
|
||||
}
|
||||
printf("ZERRRRO!!!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
static void print_mpi(const char *msg, int radix, const mbedtls_mpi *X) {
|
||||
|
||||
char Xchar[400] = {0};
|
||||
size_t len = 0;
|
||||
|
||||
mbedtls_mpi_write_string(X, radix, Xchar, sizeof(Xchar), &len);
|
||||
printf("%s[%zu] %s\n", msg, len, Xchar);
|
||||
}
|
||||
*/
|
||||
bool emv_rocacheck(const unsigned char *buf, size_t buflen, bool verbose) {
|
||||
|
||||
mbedtls_mpi t_modulus;
|
||||
mbedtls_mpi_init(&t_modulus);
|
||||
|
||||
bool ret = false;
|
||||
|
||||
rocacheck_init();
|
||||
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&t_modulus, buf, buflen));
|
||||
|
||||
for (int i = 0; i < ROCA_PRINTS_LENGTH; i++) {
|
||||
|
||||
mbedtls_mpi t_temp;
|
||||
mbedtls_mpi t_prime;
|
||||
mbedtls_mpi g_one;
|
||||
|
||||
mbedtls_mpi_init(&t_temp);
|
||||
mbedtls_mpi_init(&t_prime);
|
||||
mbedtls_mpi_init(&g_one);
|
||||
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&g_one, 10, "1"));
|
||||
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&t_prime, &t_prime, g_primes[i]));
|
||||
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&t_temp, &t_modulus, &t_prime));
|
||||
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&g_one, mpi_get_uint(&t_temp)));
|
||||
|
||||
if (bitand_is_zero(&g_one, &g_prints[i])) {
|
||||
if (verbose)
|
||||
PrintAndLogEx(FAILED, "No fingerprint found.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
mbedtls_mpi_free(&g_one);
|
||||
mbedtls_mpi_free(&t_temp);
|
||||
mbedtls_mpi_free(&t_prime);
|
||||
}
|
||||
|
||||
ret = true;
|
||||
if (verbose)
|
||||
PrintAndLogEx(SUCCESS, "Fingerprint found!\n");
|
||||
|
||||
cleanup:
|
||||
mbedtls_mpi_free(&t_modulus);
|
||||
|
||||
rocacheck_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int roca_self_test(void) {
|
||||
int ret = 0;
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "ROCA check vulnerability tests");
|
||||
|
||||
// positive
|
||||
uint8_t keyp[] = "\x94\x4e\x13\x20\x8a\x28\x0c\x37\xef\xc3\x1c\x31\x14\x48\x5e\x59"\
|
||||
"\x01\x92\xad\xbb\x8e\x11\xc8\x7c\xad\x60\xcd\xef\x00\x37\xce\x99"\
|
||||
"\x27\x83\x30\xd3\xf4\x71\xa2\x53\x8f\xa6\x67\x80\x2e\xd2\xa3\xc4"\
|
||||
"\x4a\x8b\x7d\xea\x82\x6e\x88\x8d\x0a\xa3\x41\xfd\x66\x4f\x7f\xa7";
|
||||
|
||||
|
||||
if (emv_rocacheck(keyp, 64, false)) {
|
||||
PrintAndLogEx(SUCCESS, "Weak modulus [ %s]", _GREEN_("PASS"));
|
||||
} else {
|
||||
ret++;
|
||||
PrintAndLogEx(FAILED, "Weak modulus [ %s]", _RED_("Fail"));
|
||||
}
|
||||
|
||||
// negative
|
||||
uint8_t keyn[] = "\x84\x4e\x13\x20\x8a\x28\x0c\x37\xef\xc3\x1c\x31\x14\x48\x5e\x59"\
|
||||
"\x01\x92\xad\xbb\x8e\x11\xc8\x7c\xad\x60\xcd\xef\x00\x37\xce\x99"\
|
||||
"\x27\x83\x30\xd3\xf4\x71\xa2\x53\x8f\xa6\x67\x80\x2e\xd2\xa3\xc4"\
|
||||
"\x4a\x8b\x7d\xea\x82\x6e\x88\x8d\x0a\xa3\x41\xfd\x66\x4f\x7f\xa7";
|
||||
|
||||
if (emv_rocacheck(keyn, 64, false)) {
|
||||
ret++;
|
||||
PrintAndLogEx(FAILED, "Strong modulus [ %s]", _RED_("Fail"));
|
||||
} else {
|
||||
PrintAndLogEx(SUCCESS, "Strong modulus [ %s]", _GREEN_("PASS"));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
35
client/src/emv/emv_roca.h
Normal file
35
client/src/emv/emv_roca.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* roca.c - ROCA (CVE-2017-15361) fingerprint checker.
|
||||
* Written by Rob Stradling (based on https://github.com/crocs-muni/roca/blob/master/roca/detect.py)
|
||||
* Copyright (C) 2017-2018 Sectigo Limited
|
||||
* modified 2018 iceman (dropped openssl bignum, now use mbedtls lib)
|
||||
* modified 2018 merlok
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//-----------------------------------------------------------------------------
|
||||
// EMV roca commands
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef EMV_ROCA_H__
|
||||
#define EMV_ROCA_H__
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define ROCA_PRINTS_LENGTH 17
|
||||
|
||||
bool emv_rocacheck(const unsigned char *buf, size_t buflen, bool verbose);
|
||||
int roca_self_test(void);
|
||||
|
||||
#endif
|
||||
|
||||
833
client/src/emv/emv_tags.c
Normal file
833
client/src/emv/emv_tags.c
Normal file
@@ -0,0 +1,833 @@
|
||||
/*
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "emv_tags.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "commonutil.h"
|
||||
|
||||
#ifndef PRINT_INDENT
|
||||
# define PRINT_INDENT(level) {for (int myi = 0; myi < (level); myi++) fprintf(f, " ");}
|
||||
#endif
|
||||
|
||||
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_CVR,
|
||||
EMV_TAG_CID,
|
||||
};
|
||||
|
||||
struct emv_tag {
|
||||
tlv_tag_t tag;
|
||||
const 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 (Combined Dynamic Data Authentication / Application Cryptogram Generation)" },
|
||||
{ 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" },
|
||||
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_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,
|
||||
};
|
||||
|
||||
static const struct emv_tag_bit EMV_CVR[] = {
|
||||
// mask 0F 0F F0 0F
|
||||
{ EMV_BIT(1, 4), "CDA Performed" },
|
||||
{ EMV_BIT(1, 3), "Offline DDA Performed" },
|
||||
{ EMV_BIT(1, 2), "Issuer Authentication Not Performed" },
|
||||
{ EMV_BIT(1, 1), "Issuer Authentication performed and Failed" },
|
||||
{ EMV_BIT(2, 4), "Offline PIN Verification Performed" },
|
||||
{ EMV_BIT(2, 3), "Offline PIN Verification Performed and PIN Not Successfully Verified" },
|
||||
{ EMV_BIT(2, 2), "PIN Try Limit Exceeded" },
|
||||
{ EMV_BIT(2, 1), "Last Online Transaction Not Completed" },
|
||||
{ EMV_BIT(3, 8), "Lower Offline Transaction Count Limit Exceeded" },
|
||||
{ EMV_BIT(3, 7), "Upper Offline Transaction Count Limit Exceeded" },
|
||||
{ EMV_BIT(3, 6), "Lower Cumulative Offline Amount Limit Exceeded" },
|
||||
{ EMV_BIT(3, 5), "Upper Cumulative Offline Amount Limit Exceeded" },
|
||||
{ EMV_BIT(4, 4), "Issuer script processing failed on last transaction" },
|
||||
{ EMV_BIT(4, 3), "Offline data authentication failed on previous transaction and transaction declined offline" },
|
||||
{ EMV_BIT(4, 2), "Go Online on Next Transaction Was Set" },
|
||||
{ EMV_BIT(4, 1), "Unable to go Online" },
|
||||
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[] = {
|
||||
// internal
|
||||
{ 0x00, "Unknown ???", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x01, "", EMV_TAG_STRING, NULL }, // string for headers
|
||||
{ 0x02, "Raw data", EMV_TAG_GENERIC, NULL }, // data
|
||||
{ 0x06, "Object Identifier (OID)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x20, "Cardholder Verification Results (CVR)", EMV_TAG_CVR, NULL }, // not standard!
|
||||
{ 0x21, "Input list for Offline Data Authentication", EMV_TAG_GENERIC, NULL }, // not standard! data for "Offline Data Authentication" come from "read records" command. (EMV book3 10.3)
|
||||
|
||||
// EMV
|
||||
{ 0x41, "Country code and national data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x42, "Issuer Identification Number (IIN)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x4f, "Application Dedicated File (ADF) Name", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x50, "Application Label", EMV_TAG_STRING, NULL },
|
||||
{ 0x51, "File reference data element", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x52, "Command APDU", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x53, "Discretionary data (or template)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x56, "Track 1 Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x57, "Track 2 Equivalent Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x5a, "Application Primary Account Number (PAN)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x5f20, "Cardholder Name", EMV_TAG_STRING, NULL },
|
||||
{ 0x5f24, "Application Expiration Date", EMV_TAG_YYMMDD, NULL },
|
||||
{ 0x5f25, "Application Effective Date", EMV_TAG_YYMMDD, NULL },
|
||||
{ 0x5f28, "Issuer Country Code", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x5f2a, "Transaction Currency Code", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x5f2d, "Language Preference", EMV_TAG_STRING, NULL },
|
||||
{ 0x5f30, "Service Code", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x5f34, "Application Primary Account Number (PAN) Sequence Number", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x5f36, "Transaction Currency Exponent", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x5f50, "Issuer URL", EMV_TAG_STRING, NULL },
|
||||
{ 0x5f53, "International Bank Account Number (IBAN)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x5f54, "Bank Identifier Code (BIC)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x5f55, "Issuer Country Code (alpha2 format)", EMV_TAG_STRING, NULL },
|
||||
{ 0x5f56, "Issuer Country Code (alpha3 format)", EMV_TAG_STRING, NULL },
|
||||
|
||||
{ 0x61, "Application Template", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x6f, "File Control Information (FCI) Template", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x70, "READ RECORD Response Message Template", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x71, "Issues Script Template 1", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x72, "Issues Script Template 2", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x73, "Directory Discretionary Template", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x77, "Response Message Template Format 2", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x80, "Response Message Template Format 1", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x81, "Amount, Authorised (Binary)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x82, "Application Interchange Profile", EMV_TAG_BITMASK, &EMV_AIP },
|
||||
{ 0x83, "Command Template", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x84, "Dedicated File (DF) Name", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x86, "Issuer Script Command", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x87, "Application Priority Indicator", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x88, "Short File Identifier (SFI)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x89, "Authorisation Code", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x8a, "Authorisation Response Code", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x8c, "Card Risk Management Data Object List 1 (CDOL1)", EMV_TAG_DOL, NULL },
|
||||
{ 0x8d, "Card Risk Management Data Object List 2 (CDOL2)", EMV_TAG_DOL, NULL },
|
||||
{ 0x8e, "Cardholder Verification Method (CVM) List", EMV_TAG_CVM_LIST, NULL },
|
||||
{ 0x8f, "Certification Authority Public Key Index", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x90, "Issuer Public Key Certificate", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x91, "Issuer Authentication Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x92, "Issuer Public Key Remainder", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x93, "Signed Static Application Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x94, "Application File Locator (AFL)", EMV_TAG_AFL, NULL },
|
||||
{ 0x95, "Terminal Verification Results", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x97, "Transaction Certificate Data Object List (TDOL)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x98, "Transaction Certificate (TC) Hash Value", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x99, "Transaction Personal Identification Number (PIN) Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9a, "Transaction Date", EMV_TAG_YYMMDD, NULL },
|
||||
{ 0x9b, "Transaction Status Information", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9c, "Transaction Type", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9d, "Directory Definition File (DDF) Name", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0x9f01, "Acquirer Identifier", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f03, "Amount, Other (Numeric)", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f04, "Amount, Other (Binary)", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f05, "Application Discretionary Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC },
|
||||
{ 0x9f08, "Application Version Number", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f09, "Application Version Number - terminal", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f0a, "Application Selection Registered Proprietary Data", EMV_TAG_GENERIC, NULL }, // https://blog.ul-ts.com/posts/electronic-card-identifier-one-more-step-for-mif-compliance/
|
||||
{ 0x9f0b, "Cardholder Name Extended", EMV_TAG_STRING, NULL },
|
||||
{ 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", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f11, "Issuer Code Table Index", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f12, "Application Preferred Name", EMV_TAG_STRING, NULL },
|
||||
{ 0x9f13, "Last Online Application Transaction Counter (ATC) Register", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f14, "Lower Consecutive Offline Limit", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f15, "Merchant Category Code", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f16, "Merchant Identifier", EMV_TAG_STRING, NULL },
|
||||
{ 0x9f17, "Personal Identification Number (PIN) Try Counter", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f18, "Issuer Script Identifier", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f1a, "Terminal Country Code", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f1b, "Terminal Floor Limit", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f1c, "Terminal Identification", EMV_TAG_STRING, NULL },
|
||||
{ 0x9f1d, "Terminal Risk Management Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f1e, "Interface Device (IFD) Serial Number", EMV_TAG_STRING, NULL },
|
||||
{ 0x9f1f, "Track 1 Discretionary Data", EMV_TAG_STRING, NULL },
|
||||
{ 0x9f20, "Track 2 Discretionary Data", EMV_TAG_STRING, NULL },
|
||||
{ 0x9f21, "Transaction Time", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f22, "Certification Authority Public Key Index - Terminal", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f23, "Upper Consecutive Offline Limit", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f26, "Application Cryptogram", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f27, "Cryptogram Information Data", EMV_TAG_CID, NULL },
|
||||
{ 0x9f2a, "Kernel Identifier", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f2d, "ICC PIN Encipherment Public Key Certificate", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f2e, "ICC PIN Encipherment Public Key Exponent", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f2f, "ICC PIN Encipherment Public Key Remainder", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f32, "Issuer Public Key Exponent", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f33, "Terminal Capabilities", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f34, "Cardholder Verification Method (CVM) Results", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f35, "Terminal Type", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f36, "Application Transaction Counter (ATC)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f37, "Unpredictable Number", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f38, "Processing Options Data Object List (PDOL)", EMV_TAG_DOL, NULL },
|
||||
{ 0x9f39, "Point-of-Service (POS) Entry Mode", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f3a, "Amount, Reference Currency", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f3b, "Application Reference Currency", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f3c, "Transaction Reference Currency Code", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f3d, "Transaction Reference Currency Exponent", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f40, "Additional Terminal Capabilities", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f41, "Transaction Sequence Counter", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f42, "Application Currency Code", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f43, "Application Reference Currency Exponent", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f44, "Application Currency Exponent", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f45, "Data Authentication Code", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f46, "ICC Public Key Certificate", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f47, "ICC Public Key Exponent", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f48, "ICC Public Key Remainder", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f49, "Dynamic Data Authentication Data Object List (DDOL)", EMV_TAG_DOL, NULL },
|
||||
{ 0x9f4a, "Static Data Authentication Tag List", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f4b, "Signed Dynamic Application Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f4c, "ICC Dynamic Number", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f4d, "Log Entry", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f4e, "Merchant Name and Location", EMV_TAG_STRING, NULL },
|
||||
{ 0x9f4f, "Log Format", EMV_TAG_DOL, NULL },
|
||||
|
||||
{ 0x9f50, "Offline Accumulator Balance", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f51, "Application Currency Code", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f51, "DRDOL", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0x9f52, "Application Default Action (ADA)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f52, "Terminal Compatibility Indicator", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0x9f53, "Transaction Category Code", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f54, "DS ODS Card", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0x9f55, "Mobile Support Indicator", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f55, "Issuer Authentication Flags", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0x9f56, "Issuer Authentication Indicator", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f57, "Issuer Country Code", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f58, "Consecutive Transaction Counter Limit (CTCL)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f59, "Consecutive Transaction Counter Upper Limit (CTCUL)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f5A, "Application Program Identifier", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f5b, "Issuer Script Results", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f5c, "Cumulative Total Transaction Amount Upper Limit (CTTAUL)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f5d, "Application Capabilities Information", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f5e, "Data Storage Identifier", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f5f, "DS Slot Availability", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0x9f60, "CVC3 (Track1)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f61, "CVC3 (Track2)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f62, "PCVC3 (Track1)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f63, "PUNATC (Track1)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f64, "NATC (Track1)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f65, "PCVC3 (Track2)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f66, "PUNATC (Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ },
|
||||
{ 0x9f67, "NATC (Track2) / MSD Offset", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f68, "Cardholder verification method list (PayPass)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f69, "Card Authentication Related Data (UDOL)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC, NULL },
|
||||
{ 0x9f6b, "Track 2 Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ },
|
||||
{ 0x9f6d, "Mag-stripe Application Version Number (Reader)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f6e, "Form Factor Indicator", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f6f, "DS Slot Management Control", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0x9f70, "Protected Data Envelope 1", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f71, "Protected Data Envelope 2", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f72, "Protected Data Envelope 3", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f73, "Protected Data Envelope 4", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f74, "Protected Data Envelope 5", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f75, "Unprotected Data Envelope 1", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f76, "Unprotected Data Envelope 2", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f77, "Unprotected Data Envelope 3", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f78, "Unprotected Data Envelope 4", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f79, "Unprotected Data Envelope 5", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f7c, "Merchant Custom Data / Customer Exclusive Data (CED)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f7d, "DS Summary 1", EMV_TAG_GENERIC, NULL },
|
||||
{ 0x9f7f, "DS Unpredictable Number", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0xa5, "File Control Information (FCI) Proprietary Template", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf20, "Issuer Proprietary Bitmap (IPB)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf4b, "POS Cardholder Interaction Information", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf60, "VISA Log Entry", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf61, "DS Digest H", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf62, "DS ODS Info", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf63, "DS ODS Term", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0xdf8104, "Balance Read Before Gen AC", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8105, "Balance Read After Gen AC", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8106, "Data Needed", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8107, "CDOL1 Related Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8108, "DS AC Type", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8109, "DS Input (Term)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf810a, "DS ODS Info For Reader", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf810b, "DS Summary Status", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf810c, "Kernel ID", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf810d, "DSVN Term", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf810e, "Post-Gen AC Put Data Status", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf810f, "Pre-Gen AC Put Data Status", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8110, "Proceed To First Write Flag", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8111, "PDOL Related Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8112, "Tags To Read", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8113, "DRDOL Related Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8114, "Reference Control Parameter", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8115, "Error Indication", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8116, "User Interface Request Data", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8117, "Card Data Input Capability", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8118, "CVM Capability - CVM Required", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8119, "CVM Capability - No CVM Required", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf811a, "Default UDOL", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf811b, "Kernel Configuration", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf811c, "Max Lifetime of Torn Transaction Log Record", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf811d, "Max Number of Torn Transaction Log Records", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf811e, "Mag-stripe CVM Capability - CVM Required", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf811f, "Security Capability", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8120, "Terminal Action Code - Default", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8121, "Terminal Action Code - Denial", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8122, "Terminal Action Code - Online", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8123, "Reader Contactless Floor Limit", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8124, "Reader Contactless Transaction Limit (No On-device CVM)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8125, "Reader Contactless Transaction Limit (On-device CVM)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8126, "Reader CVM Required Limit", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8127, "TIME_OUT_VALUE", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8128, "IDS Status", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf8129, "Outcome Parameter Set", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf812a, "DD Card (Track1)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf812b, "DD Card (Track2)", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf812c, "Mag-stripe CVM Capability - No CVM Required", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xdf812d, "Message Hold Time", EMV_TAG_GENERIC, NULL },
|
||||
|
||||
{ 0xff8101, "Torn Record", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xff8102, "Tags To Write Before Gen AC", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xff8103, "Tags To Write After Gen AC", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xff8104, "Data To Send", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xff8105, "Data Record", EMV_TAG_GENERIC, NULL },
|
||||
{ 0xff8106, "Discretionary Data", EMV_TAG_GENERIC, NULL },
|
||||
};
|
||||
|
||||
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, ARRAYLEN(emv_tags),
|
||||
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, 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) {
|
||||
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, int level) {
|
||||
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)) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "Invalid Tag-Len\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
doltag = emv_get_tag(&doltlv);
|
||||
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tTag %4x 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, int level) {
|
||||
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, 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, int level) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tDate: 20%02lu.%lu.%lu\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);
|
||||
}
|
||||
|
||||
// https://github.com/binaryfoo/emv-bertlv/blob/master/src/main/resources/fields/visa-cvr.txt
|
||||
static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) {
|
||||
if (!tlv || tlv->len < 1) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tINVALID!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tlv->len != tlv->value[0] + 1) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tINVALID length!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tlv->len >= 2) {
|
||||
// AC1
|
||||
PRINT_INDENT(level);
|
||||
if ((tlv->value[1] & 0xC0) == 0x00) fprintf(f, "\tAC1: AAC (Transaction declined)\n");
|
||||
if ((tlv->value[1] & 0xC0) == 0x40) fprintf(f, "\tAC1: TC (Transaction approved)\n");
|
||||
if ((tlv->value[1] & 0xC0) == 0x80) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n");
|
||||
if ((tlv->value[1] & 0xC0) == 0xC0) fprintf(f, "\tAC1: RFU\n");
|
||||
// AC2
|
||||
PRINT_INDENT(level);
|
||||
if ((tlv->value[1] & 0x30) == 0x00) fprintf(f, "\tAC2: AAC (Transaction declined)\n");
|
||||
if ((tlv->value[1] & 0x30) == 0x10) fprintf(f, "\tAC2: TC (Transaction approved)\n");
|
||||
if ((tlv->value[1] & 0x30) == 0x20) fprintf(f, "\tAC2: not requested (ARQC)\n");
|
||||
if ((tlv->value[1] & 0x30) == 0x30) fprintf(f, "\tAC2: RFU\n");
|
||||
}
|
||||
if (tlv->len >= 3 && (tlv->value[2] >> 4)) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tPIN try: %x\n", tlv->value[2] >> 4);
|
||||
}
|
||||
if (tlv->len >= 4 && (tlv->value[3] & 0x0F)) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tIssuer discretionary bits: %x\n", tlv->value[3] & 0x0F);
|
||||
}
|
||||
if (tlv->len >= 5 && (tlv->value[4] >> 4)) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tSuccessfully processed issuer script commands: %x\n", tlv->value[4] >> 4);
|
||||
}
|
||||
|
||||
// mask 0F 0F F0 0F
|
||||
uint8_t data[20] = {0};
|
||||
memcpy(data, &tlv->value[1], tlv->len - 1);
|
||||
data[0] &= 0x0F;
|
||||
data[1] &= 0x0F;
|
||||
data[2] &= 0xF0;
|
||||
data[3] &= 0x0F;
|
||||
const struct tlv bit_tlv = {
|
||||
.tag = tlv->tag,
|
||||
.len = tlv->len - 1,
|
||||
.value = data,
|
||||
};
|
||||
const struct emv_tag bit_tag = {
|
||||
.tag = tag->tag,
|
||||
.name = tag->name,
|
||||
.type = EMV_TAG_BITMASK,
|
||||
.data = EMV_CVR,
|
||||
};
|
||||
|
||||
if (data[0] || data[1] || data[2] || data[3])
|
||||
emv_tag_dump_bitmask(&bit_tlv, &bit_tag, f, level);
|
||||
}
|
||||
|
||||
// EMV Book 3
|
||||
static void emv_tag_dump_cid(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) {
|
||||
if (!tlv || tlv->len < 1) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tINVALID!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
PRINT_INDENT(level);
|
||||
if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_AAC) fprintf(f, "\tAC1: AAC (Transaction declined)\n");
|
||||
if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_TC) fprintf(f, "\tAC1: TC (Transaction approved)\n");
|
||||
if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_ARQC) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n");
|
||||
if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_AC_MASK) fprintf(f, "\tAC1: RFU\n");
|
||||
|
||||
if (tlv->value[0] & EMVCID_ADVICE) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tAdvice required!\n");
|
||||
}
|
||||
|
||||
if (tlv->value[0] & EMVCID_REASON_MASK) {
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tReason/advice/referral code: ");
|
||||
switch ((tlv->value[0] & EMVCID_REASON_MASK)) {
|
||||
case 0:
|
||||
fprintf(f, "No information given\n");
|
||||
break;
|
||||
case 1:
|
||||
fprintf(f, "Service not allowed\n");
|
||||
break;
|
||||
case 2:
|
||||
fprintf(f, "PIN Try Limit exceeded\n");
|
||||
break;
|
||||
case 3:
|
||||
fprintf(f, "Issuer authentication failed\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(f, "\tRFU: %2x\n", (tlv->value[0] & EMVCID_REASON_MASK));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
X = emv_get_binary(tlv->value);
|
||||
Y = emv_get_binary(tlv->value + 4);
|
||||
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tX: %u\n", X);
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "\tY: %u\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;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct emv_tag *tag = emv_get_tag(tlv);
|
||||
|
||||
PRINT_INDENT(level);
|
||||
fprintf(f, "--%2x[%02zx] '%s':", tlv->tag, tlv->len, tag->name);
|
||||
|
||||
switch (tag->type) {
|
||||
case EMV_TAG_GENERIC:
|
||||
fprintf(f, "\n");
|
||||
break;
|
||||
case EMV_TAG_BITMASK:
|
||||
fprintf(f, "\n");
|
||||
emv_tag_dump_bitmask(tlv, tag, f, level);
|
||||
break;
|
||||
case EMV_TAG_DOL:
|
||||
fprintf(f, "\n");
|
||||
emv_tag_dump_dol(tlv, tag, f, level);
|
||||
break;
|
||||
case EMV_TAG_CVM_LIST:
|
||||
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, level);
|
||||
break;
|
||||
case EMV_TAG_NUMERIC:
|
||||
emv_tag_dump_numeric(tlv, tag, f, level);
|
||||
break;
|
||||
case EMV_TAG_YYMMDD:
|
||||
emv_tag_dump_yymmdd(tlv, tag, f, level);
|
||||
break;
|
||||
case EMV_TAG_CVR:
|
||||
fprintf(f, "\n");
|
||||
emv_tag_dump_cvr(tlv, tag, f, level);
|
||||
break;
|
||||
case EMV_TAG_CID:
|
||||
fprintf(f, "\n");
|
||||
emv_tag_dump_cid(tlv, tag, f, level);
|
||||
break;
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *emv_get_tag_name(const struct tlv *tlv) {
|
||||
static const char *defstr = "";
|
||||
|
||||
if (!tlv)
|
||||
return defstr;
|
||||
|
||||
const struct emv_tag *tag = emv_get_tag(tlv);
|
||||
if (tag)
|
||||
return tag->name;
|
||||
|
||||
return defstr;
|
||||
}
|
||||
40
client/src/emv/emv_tags.h
Normal file
40
client/src/emv/emv_tags.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 <stdio.h> // FILE
|
||||
|
||||
// AC
|
||||
# define EMVAC_AC_MASK 0xC0
|
||||
# define EMVAC_AAC 0x00
|
||||
# define EMVAC_TC 0x40
|
||||
# define EMVAC_ARQC 0x80
|
||||
# define EMVAC_CDAREQ 0x10
|
||||
# define EMVAC_AC2_MASK 0x30
|
||||
# define EMVAC_AAC2 0x00
|
||||
# define EMVAC_TC2 0x10
|
||||
# define EMVAC_ARQC2 0x20
|
||||
|
||||
// CID
|
||||
# define EMVCID_ADVICE 0x08
|
||||
# define EMVCID_REASON_MASK 0x07
|
||||
|
||||
bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level);
|
||||
const char *emv_get_tag_name(const struct tlv *tlv);
|
||||
|
||||
#endif
|
||||
1099
client/src/emv/emvcore.c
Normal file
1099
client/src/emv/emvcore.c
Normal file
File diff suppressed because it is too large
Load Diff
94
client/src/emv/emvcore.h
Normal file
94
client/src/emv/emvcore.h
Normal file
@@ -0,0 +1,94 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 functionality
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef EMVCORE_H__
|
||||
#define EMVCORE_H__
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#include "apduinfo.h"
|
||||
#include "emv_pki.h"
|
||||
|
||||
#define APDU_RES_LEN 260
|
||||
#define APDU_AID_LEN 50
|
||||
|
||||
typedef enum {
|
||||
ECC_CONTACTLESS,
|
||||
ECC_CONTACT
|
||||
} EMVCommandChannel;
|
||||
|
||||
enum TransactionType {
|
||||
TT_MSD,
|
||||
TT_VSDC, // contact only. not standard for contactless
|
||||
TT_QVSDCMCHIP,
|
||||
TT_CDA,
|
||||
};
|
||||
extern const char *TransactionTypeStr[];
|
||||
|
||||
enum CardPSVendor {
|
||||
CV_NA,
|
||||
CV_VISA,
|
||||
CV_MASTERCARD,
|
||||
CV_AMERICANEXPRESS,
|
||||
CV_JCB,
|
||||
CV_CB,
|
||||
CV_SWITCH,
|
||||
CV_DINERS,
|
||||
CV_OTHER,
|
||||
};
|
||||
enum CardPSVendor GetCardPSVendor(uint8_t *AID, size_t AIDlen);
|
||||
|
||||
bool TLVPrintFromBuffer(uint8_t *data, int datalen);
|
||||
void TLVPrintFromTLV(struct tlvdb *tlv);
|
||||
void TLVPrintFromTLVLev(struct tlvdb *tlv, int level);
|
||||
void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv);
|
||||
|
||||
struct tlvdb *GetPANFromTrack2(const struct tlv *track2);
|
||||
struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2);
|
||||
|
||||
void SetAPDULogging(bool logging);
|
||||
bool GetAPDULogging(void);
|
||||
|
||||
// exchange
|
||||
int EMVExchange(EMVCommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
|
||||
// search application
|
||||
int EMVSearchPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, bool decodeTLV, struct tlvdb *tlv);
|
||||
int EMVSearch(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||
int EMVSelectPSE(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
int EMVSelect(EMVCommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// select application
|
||||
int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen);
|
||||
// Get Processing Options
|
||||
int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// AC
|
||||
int EMVGenerateChallenge(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
int EMVAC(EMVCommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// DDA
|
||||
int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// Mastercard
|
||||
int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
|
||||
// Auth
|
||||
int trSDA(struct tlvdb *tlv);
|
||||
int trDDA(EMVCommandChannel channel, bool decodeTLV, struct tlvdb *tlv);
|
||||
int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv);
|
||||
|
||||
int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root);
|
||||
|
||||
struct emv_pk *get_ca_pk(struct tlvdb *db);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
401
client/src/emv/emvjson.c
Normal file
401
client/src/emv/emvjson.c
Normal file
@@ -0,0 +1,401 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018 Merlok
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// EMV json logic
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "emvjson.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "commonutil.h" // ARRAYLEN
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
#include "proxmark3.h"
|
||||
#include "emv_tags.h"
|
||||
#include "fileutils.h"
|
||||
#include "pm3_cmd.h"
|
||||
|
||||
static const ApplicationDataElm ApplicationData[] = {
|
||||
{0x82, "AIP"},
|
||||
{0x94, "AFL"},
|
||||
|
||||
{0x5A, "PAN"},
|
||||
{0x5F34, "PANSeqNo"},
|
||||
{0x5F24, "ExpirationDate"},
|
||||
{0x5F25, "EffectiveDate"},
|
||||
{0x5F28, "IssuerCountryCode"},
|
||||
|
||||
{0x50, "ApplicationLabel"},
|
||||
{0x9F08, "VersionNumber"},
|
||||
{0x9F42, "CurrencyCode"},
|
||||
{0x5F2D, "LanguagePreference"},
|
||||
{0x87, "PriorityIndicator"},
|
||||
{0x9F36, "ATC"}, //Application Transaction Counter
|
||||
|
||||
{0x5F20, "CardholderName"},
|
||||
|
||||
{0x9F38, "PDOL"},
|
||||
{0x8C, "CDOL1"},
|
||||
{0x8D, "CDOL2"},
|
||||
|
||||
{0x9F07, "AUC"}, // Application Usage Control
|
||||
{0x9F6C, "CTQ"},
|
||||
{0x8E, "CVMList"},
|
||||
{0x9F0D, "IACDefault"},
|
||||
{0x9F0E, "IACDeny"},
|
||||
{0x9F0F, "IACOnline"},
|
||||
|
||||
{0x8F, "CertificationAuthorityPublicKeyIndex"},
|
||||
{0x9F32, "IssuerPublicKeyExponent"},
|
||||
{0x92, "IssuerPublicKeyRemainder"},
|
||||
{0x90, "IssuerPublicKeyCertificate"},
|
||||
{0x9F47, "ICCPublicKeyExponent"},
|
||||
{0x9F46, "ICCPublicKeyCertificate"},
|
||||
|
||||
{0x00, "end..."}
|
||||
};
|
||||
|
||||
const char *GetApplicationDataName(tlv_tag_t tag) {
|
||||
for (int i = 0; i < ARRAYLEN(ApplicationData); i++)
|
||||
if (ApplicationData[i].Tag == tag)
|
||||
return ApplicationData[i].Name;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int JsonSaveJsonObject(json_t *root, const char *path, json_t *value) {
|
||||
json_error_t error;
|
||||
|
||||
if (strlen(path) < 1)
|
||||
return 1;
|
||||
|
||||
if (path[0] == '$') {
|
||||
if (json_path_set(root, path, value, 0, &error)) {
|
||||
PrintAndLogEx(ERR, "ERROR: can't set json path: %s", error.text);
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return json_object_set_new(root, path, value);
|
||||
}
|
||||
}
|
||||
|
||||
int JsonSaveInt(json_t *root, const char *path, int value) {
|
||||
return JsonSaveJsonObject(root, path, json_integer(value));
|
||||
}
|
||||
|
||||
int JsonSaveStr(json_t *root, const char *path, const char *value) {
|
||||
return JsonSaveJsonObject(root, path, json_string(value));
|
||||
};
|
||||
|
||||
int JsonSaveBoolean(json_t *root, const char *path, bool value) {
|
||||
return JsonSaveJsonObject(root, path, json_boolean(value));
|
||||
}
|
||||
|
||||
int JsonSaveBufAsHexCompact(json_t *elm, const char *path, uint8_t *data, size_t datalen) {
|
||||
char *msg = sprint_hex_inrow(data, datalen);
|
||||
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
|
||||
msg[strlen(msg) - 1] = '\0';
|
||||
|
||||
return JsonSaveStr(elm, path, msg);
|
||||
}
|
||||
|
||||
int JsonSaveBufAsHex(json_t *elm, const char *path, uint8_t *data, size_t datalen) {
|
||||
char *msg = sprint_hex(data, datalen);
|
||||
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
|
||||
msg[strlen(msg) - 1] = '\0';
|
||||
|
||||
return JsonSaveStr(elm, path, msg);
|
||||
}
|
||||
|
||||
int JsonSaveHex(json_t *elm, const char *path, uint64_t data, int datalen) {
|
||||
uint8_t bdata[8] = {0};
|
||||
int len = 0;
|
||||
if (!datalen) {
|
||||
for (uint64_t u = 0xffffffffffffffff; u; u = u << 8) {
|
||||
if (!(data & u)) {
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
if (!len)
|
||||
len = 1;
|
||||
} else {
|
||||
len = datalen;
|
||||
}
|
||||
num_to_bytes(data, len, bdata);
|
||||
|
||||
return JsonSaveBufAsHex(elm, path, bdata, len);
|
||||
}
|
||||
|
||||
int JsonSaveTLVValue(json_t *root, const char *path, struct tlvdb *tlvdbelm) {
|
||||
const struct tlv *tlvelm = tlvdb_get_tlv(tlvdbelm);
|
||||
if (tlvelm)
|
||||
return JsonSaveBufAsHex(root, path, (uint8_t *)tlvelm->value, tlvelm->len);
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int JsonSaveTLVElm(json_t *elm, const char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink) {
|
||||
json_error_t error;
|
||||
|
||||
if (strlen(path) < 1 || !tlvelm)
|
||||
return 1;
|
||||
|
||||
if (path[0] == '$') {
|
||||
|
||||
json_t *obj = json_path_get(elm, path);
|
||||
if (!obj) {
|
||||
obj = json_object();
|
||||
|
||||
if (json_is_array(elm)) {
|
||||
if (json_array_append_new(elm, obj)) {
|
||||
PrintAndLogEx(ERR, "ERROR: can't append array: %s", path);
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
if (json_path_set(elm, path, obj, 0, &error)) {
|
||||
PrintAndLogEx(ERR, "ERROR: can't set json path: %s", error.text);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (saveAppDataLink) {
|
||||
const char *AppDataName = GetApplicationDataName(tlvelm->tag);
|
||||
if (AppDataName)
|
||||
JsonSaveStr(obj, "appdata", AppDataName);
|
||||
} else {
|
||||
const char *name = emv_get_tag_name(tlvelm);
|
||||
if (saveName && name && strlen(name) > 0 && strncmp(name, "Unknown", 7))
|
||||
JsonSaveStr(obj, "name", emv_get_tag_name(tlvelm));
|
||||
JsonSaveHex(obj, "tag", tlvelm->tag, 0);
|
||||
if (saveValue) {
|
||||
JsonSaveHex(obj, "length", tlvelm->len, 0);
|
||||
JsonSaveBufAsHex(obj, "value", (uint8_t *)tlvelm->value, tlvelm->len);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JsonSaveTLVTreeElm(json_t *elm, const char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink) {
|
||||
return JsonSaveTLVElm(elm, path, (struct tlv *)tlvdb_get_tlv(tlvdbelm), saveName, saveValue, saveAppDataLink);
|
||||
}
|
||||
|
||||
int JsonSaveTLVTree(json_t *root, json_t *elm, const char *path, struct tlvdb *tlvdbelm) {
|
||||
struct tlvdb *tlvp = tlvdbelm;
|
||||
while (tlvp) {
|
||||
const struct tlv *tlvpelm = tlvdb_get_tlv(tlvp);
|
||||
const char *AppDataName = NULL;
|
||||
if (tlvpelm)
|
||||
AppDataName = GetApplicationDataName(tlvpelm->tag);
|
||||
|
||||
if (AppDataName) {
|
||||
char appdatalink[200] = {0};
|
||||
sprintf(appdatalink, "$.ApplicationData.%s", AppDataName);
|
||||
JsonSaveBufAsHex(root, appdatalink, (uint8_t *)tlvpelm->value, tlvpelm->len);
|
||||
}
|
||||
|
||||
json_t *pelm = json_path_get(elm, path);
|
||||
if (pelm && json_is_array(pelm)) {
|
||||
json_t *appendelm = json_object();
|
||||
json_array_append_new(pelm, appendelm);
|
||||
JsonSaveTLVTreeElm(appendelm, "$", tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName);
|
||||
pelm = appendelm;
|
||||
} else {
|
||||
JsonSaveTLVTreeElm(elm, path, tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName);
|
||||
pelm = json_path_get(elm, path);
|
||||
}
|
||||
|
||||
if (tlvdb_elm_get_children(tlvp)) {
|
||||
// get path element
|
||||
if (!pelm)
|
||||
return 1;
|
||||
|
||||
// check childs element and add it if not found
|
||||
json_t *chjson = json_path_get(pelm, "$.Childs");
|
||||
if (!chjson) {
|
||||
json_object_set_new(pelm, "Childs", json_array());
|
||||
|
||||
chjson = json_path_get(pelm, "$.Childs");
|
||||
}
|
||||
|
||||
// check
|
||||
if (!json_is_array(chjson)) {
|
||||
PrintAndLogEx(ERR, "E->Internal logic error. `$.Childs` is not an array.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Recursion
|
||||
JsonSaveTLVTree(root, chjson, "$", tlvdb_elm_get_children(tlvp));
|
||||
}
|
||||
|
||||
tlvp = tlvdb_elm_get_next(tlvp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t *buffer, size_t maxbufferlen, size_t *bufferlen) {
|
||||
int buflen = 0;
|
||||
|
||||
switch (param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) {
|
||||
case 1:
|
||||
PrintAndLogEx(ERR, "%s Invalid HEX value.", errormsg);
|
||||
return false;
|
||||
case 2:
|
||||
PrintAndLogEx(ERR, "%s Hex value too large.", errormsg);
|
||||
return false;
|
||||
case 3:
|
||||
PrintAndLogEx(ERR, "%s Hex value must have even number of digits.", errormsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buflen > maxbufferlen) {
|
||||
PrintAndLogEx(ERR, "%s HEX length (%zu) more than %zu", errormsg, (bufferlen) ? *bufferlen : -1, maxbufferlen);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bufferlen)
|
||||
*bufferlen = buflen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int JsonLoadStr(json_t *root, const char *path, char *value) {
|
||||
if (!value)
|
||||
return 1;
|
||||
|
||||
json_t *jelm = json_path_get((const json_t *)root, path);
|
||||
if (!jelm || !json_is_string(jelm))
|
||||
return 2;
|
||||
|
||||
const char *strval = json_string_value(jelm);
|
||||
if (!strval)
|
||||
return 1;
|
||||
|
||||
memcpy(value, strval, strlen(strval));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JsonLoadBufAsHex(json_t *elm, const char *path, uint8_t *data, size_t maxbufferlen, size_t *datalen) {
|
||||
if (datalen)
|
||||
*datalen = 0;
|
||||
|
||||
json_t *jelm = json_path_get((const json_t *)elm, path);
|
||||
if (!jelm || !json_is_string(jelm))
|
||||
return 1;
|
||||
|
||||
if (!HexToBuffer("ERROR load", json_string_value(jelm), data, maxbufferlen, datalen))
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
bool ParamLoadFromJson(struct tlvdb *tlv) {
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
|
||||
if (!tlv) {
|
||||
PrintAndLogEx(ERR, "ERROR load params: tlv tree is NULL.");
|
||||
return false;
|
||||
}
|
||||
|
||||
char *path;
|
||||
if (searchFile(&path, RESOURCES_SUBDIR, "emv_defparams", ".json", false) != PM3_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
root = json_load_file(path, 0, &error);
|
||||
free(path);
|
||||
if (!root) {
|
||||
PrintAndLogEx(ERR, "Load params: json error on line " _YELLOW_("%d") ": %s", error.line, error.text);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!json_is_array(root)) {
|
||||
PrintAndLogEx(ERR, "Load params: Invalid json format. root must be array.");
|
||||
return false;
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Load params: json(%zu) " _GREEN_("OK"), json_array_size(root));
|
||||
|
||||
for (int i = 0; i < json_array_size(root); i++) {
|
||||
json_t *data, *jtag, *jlength, *jvalue;
|
||||
|
||||
data = json_array_get(root, i);
|
||||
if (!json_is_object(data)) {
|
||||
PrintAndLogEx(ERR, "Load params: data [%d] is not an object", i + 1);
|
||||
json_decref(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
jtag = json_object_get(data, "tag");
|
||||
if (!json_is_string(jtag)) {
|
||||
PrintAndLogEx(ERR, "Load params: data [%d] tag is not a string", i + 1);
|
||||
json_decref(root);
|
||||
return false;
|
||||
}
|
||||
const char *tlvTag = json_string_value(jtag);
|
||||
|
||||
jvalue = json_object_get(data, "value");
|
||||
if (!json_is_string(jvalue)) {
|
||||
PrintAndLogEx(ERR, "Load params: data [%d] value is not a string", i + 1);
|
||||
json_decref(root);
|
||||
return false;
|
||||
}
|
||||
const char *tlvValue = json_string_value(jvalue);
|
||||
|
||||
jlength = json_object_get(data, "length");
|
||||
if (!json_is_number(jlength)) {
|
||||
PrintAndLogEx(ERR, "Load params: data [%d] length is not a number", i + 1);
|
||||
json_decref(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
int tlvLength = json_integer_value(jlength);
|
||||
if (tlvLength > 250) {
|
||||
PrintAndLogEx(ERR, "Load params: data [%d] length more than 250", i + 1);
|
||||
json_decref(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "TLV param: %s[%d]=%s", tlvTag, tlvLength, tlvValue);
|
||||
uint8_t buf[251] = {0};
|
||||
size_t buflen = 0;
|
||||
|
||||
if (!HexToBuffer("TLV Error type:", tlvTag, buf, 4, &buflen)) {
|
||||
json_decref(root);
|
||||
return false;
|
||||
}
|
||||
tlv_tag_t tag = 0;
|
||||
for (int j = 0; j < buflen; j++) {
|
||||
tag = (tag << 8) | buf[j];
|
||||
}
|
||||
|
||||
if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) {
|
||||
json_decref(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buflen != tlvLength) {
|
||||
PrintAndLogEx(ERR, "Load params: data [%d] length of HEX must(%zu) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength);
|
||||
json_decref(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf);
|
||||
}
|
||||
|
||||
json_decref(root);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
44
client/src/emv/emvjson.h
Normal file
44
client/src/emv/emvjson.h
Normal file
@@ -0,0 +1,44 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018 Merlok
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// EMV json logic
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef EMVJSON_H__
|
||||
#define EMVJSON_H__
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <jansson.h>
|
||||
#include "tlv.h"
|
||||
|
||||
typedef struct {
|
||||
tlv_tag_t Tag;
|
||||
const char *Name;
|
||||
} ApplicationDataElm;
|
||||
|
||||
const char *GetApplicationDataName(tlv_tag_t tag);
|
||||
|
||||
int JsonSaveJsonObject(json_t *root, const char *path, json_t *value);
|
||||
int JsonSaveStr(json_t *root, const char *path, const char *value);
|
||||
int JsonSaveBoolean(json_t *root, const char *path, bool value);
|
||||
int JsonSaveInt(json_t *root, const char *path, int value);
|
||||
int JsonSaveBufAsHexCompact(json_t *elm, const char *path, uint8_t *data, size_t datalen);
|
||||
int JsonSaveBufAsHex(json_t *elm, const char *path, uint8_t *data, size_t datalen);
|
||||
int JsonSaveHex(json_t *elm, const char *path, uint64_t data, int datalen);
|
||||
|
||||
int JsonSaveTLVValue(json_t *root, const char *path, struct tlvdb *tlvdbelm);
|
||||
int JsonSaveTLVElm(json_t *elm, const char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink);
|
||||
int JsonSaveTLVTreeElm(json_t *elm, const char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink);
|
||||
|
||||
int JsonSaveTLVTree(json_t *root, json_t *elm, const char *path, struct tlvdb *tlvdbelm);
|
||||
|
||||
int JsonLoadStr(json_t *root, const char *path, char *value);
|
||||
int JsonLoadBufAsHex(json_t *elm, const char *path, uint8_t *data, size_t maxbufferlen, size_t *datalen);
|
||||
|
||||
bool ParamLoadFromJson(struct tlvdb *tlv);
|
||||
|
||||
#endif
|
||||
441
client/src/emv/test/cda_test.c
Normal file
441
client/src/emv/test/cda_test.c
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* emv-tools - a set of tools 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "../emv_pk.h"
|
||||
#include "../crypto.h"
|
||||
#include "../dump.h"
|
||||
#include "../tlv.h"
|
||||
#include "../emv_pki.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "cda_test.h"
|
||||
|
||||
struct emv_pk c_mchip_05 = {
|
||||
.rid = { 0xa0, 0x00, 0x00, 0x00, 0x04, },
|
||||
.index = 5,
|
||||
.hash_algo = HASH_SHA_1,
|
||||
.pk_algo = PK_RSA,
|
||||
.hash = {
|
||||
0xeb, 0xfa, 0x0d, 0x5d,
|
||||
0x06, 0xd8, 0xce, 0x70,
|
||||
0x2d, 0xa3, 0xea, 0xe8,
|
||||
0x90, 0x70, 0x1d, 0x45,
|
||||
0xe2, 0x74, 0xc8, 0x45,
|
||||
},
|
||||
.exp = { 0x03, },
|
||||
.elen = 1,
|
||||
.mlen = 1408 / 8,
|
||||
.modulus = (unsigned char[]) {
|
||||
0xb8, 0x04, 0x8a, 0xbc, 0x30, 0xc9, 0x0d, 0x97, 0x63, 0x36, 0x54, 0x3e, 0x3f, 0xd7, 0x09, 0x1c,
|
||||
0x8f, 0xe4, 0x80, 0x0d, 0xf8, 0x20, 0xed, 0x55, 0xe7, 0xe9, 0x48, 0x13, 0xed, 0x00, 0x55, 0x5b,
|
||||
0x57, 0x3f, 0xec, 0xa3, 0xd8, 0x4a, 0xf6, 0x13, 0x1a, 0x65, 0x1d, 0x66, 0xcf, 0xf4, 0x28, 0x4f,
|
||||
0xb1, 0x3b, 0x63, 0x5e, 0xdd, 0x0e, 0xe4, 0x01, 0x76, 0xd8, 0xbf, 0x04, 0xb7, 0xfd, 0x1c, 0x7b,
|
||||
0xac, 0xf9, 0xac, 0x73, 0x27, 0xdf, 0xaa, 0x8a, 0xa7, 0x2d, 0x10, 0xdb, 0x3b, 0x8e, 0x70, 0xb2,
|
||||
0xdd, 0xd8, 0x11, 0xcb, 0x41, 0x96, 0x52, 0x5e, 0xa3, 0x86, 0xac, 0xc3, 0x3c, 0x0d, 0x9d, 0x45,
|
||||
0x75, 0x91, 0x64, 0x69, 0xc4, 0xe4, 0xf5, 0x3e, 0x8e, 0x1c, 0x91, 0x2c, 0xc6, 0x18, 0xcb, 0x22,
|
||||
0xdd, 0xe7, 0xc3, 0x56, 0x8e, 0x90, 0x02, 0x2e, 0x6b, 0xba, 0x77, 0x02, 0x02, 0xe4, 0x52, 0x2a,
|
||||
0x2d, 0xd6, 0x23, 0xd1, 0x80, 0xe2, 0x15, 0xbd, 0x1d, 0x15, 0x07, 0xfe, 0x3d, 0xc9, 0x0c, 0xa3,
|
||||
0x10, 0xd2, 0x7b, 0x3e, 0xfc, 0xcd, 0x8f, 0x83, 0xde, 0x30, 0x52, 0xca, 0xd1, 0xe4, 0x89, 0x38,
|
||||
0xc6, 0x8d, 0x09, 0x5a, 0xac, 0x91, 0xb5, 0xf3, 0x7e, 0x28, 0xbb, 0x49, 0xec, 0x7e, 0xd5, 0x97,
|
||||
},
|
||||
};
|
||||
|
||||
const unsigned char c_issuer_cert[] = {
|
||||
0x17, 0x14, 0x28, 0x4f, 0x76, 0x3b, 0x85, 0x86, 0xee, 0x6d, 0x31, 0x99, 0x51, 0xf7, 0xe6, 0x3f,
|
||||
0xa2, 0x50, 0x76, 0xe5, 0x0d, 0xc9, 0xd3, 0x20, 0x0b, 0xa9, 0x98, 0xd3, 0xa0, 0x52, 0xad, 0xba,
|
||||
0x9a, 0xb6, 0x9a, 0xc6, 0xad, 0x6a, 0xdd, 0x3c, 0xe0, 0x9f, 0x02, 0x78, 0xf4, 0x07, 0x4e, 0xc4,
|
||||
0xee, 0x9b, 0x1d, 0x22, 0x68, 0xa3, 0xe9, 0x53, 0x57, 0x5e, 0x45, 0x4e, 0x50, 0xcd, 0x86, 0x0b,
|
||||
0xf4, 0x24, 0xc5, 0x1c, 0x59, 0x77, 0x12, 0xd2, 0xaa, 0x05, 0x70, 0x89, 0xdd, 0x86, 0x73, 0xe5,
|
||||
0x1b, 0x1e, 0x1d, 0x71, 0x88, 0x03, 0x48, 0x92, 0x07, 0x7a, 0xc1, 0x8a, 0x6a, 0xe2, 0x34, 0x88,
|
||||
0xbe, 0xa9, 0xdf, 0x3b, 0x1a, 0x83, 0xf2, 0xc0, 0x80, 0x0c, 0xd7, 0xc5, 0xcd, 0xf2, 0xfd, 0xe0,
|
||||
0x49, 0x6f, 0x7b, 0xc3, 0x9f, 0xb4, 0xbf, 0x36, 0x32, 0x99, 0xbf, 0xa6, 0x37, 0xb2, 0xec, 0x33,
|
||||
0xc5, 0x07, 0xe3, 0x68, 0x21, 0xee, 0xc2, 0x07, 0x5f, 0x0e, 0x42, 0x0d, 0x38, 0xa1, 0xc9, 0xf3,
|
||||
0x12, 0x72, 0x61, 0xba, 0x31, 0x6c, 0x98, 0x76, 0x74, 0xfa, 0xdb, 0x20, 0xea, 0x7f, 0xeb, 0x75,
|
||||
0xee, 0x45, 0x5d, 0x12, 0x14, 0x6e, 0xa6, 0xf0, 0x2e, 0x8b, 0x01, 0xec, 0x2f, 0xa7, 0xa1, 0x15,
|
||||
};
|
||||
|
||||
const unsigned char c_issuer_rem[] = {
|
||||
0x6e, 0x63, 0xb7, 0xbc, 0x70, 0xab, 0xdd, 0x09, 0x34, 0x1b, 0x34, 0xc0, 0x32, 0x86, 0xba, 0x9b,
|
||||
0xd8, 0x3b, 0xa7, 0x93, 0x6c, 0x5b, 0x77, 0x98, 0xfb, 0x22, 0xc5, 0xe5, 0x3f, 0xf2, 0x40, 0xa2,
|
||||
0x6d, 0xbd, 0x64, 0x15,
|
||||
};
|
||||
|
||||
const unsigned char c_issuer_exp[] = {
|
||||
0x03,
|
||||
};
|
||||
|
||||
const unsigned char c_icc_cert[] = {
|
||||
0xa4, 0x2f, 0xbe, 0xb1, 0x56, 0xb9, 0x8d, 0xcb, 0x05, 0x54, 0xda, 0x06, 0x2a, 0xdc, 0xa5, 0x30,
|
||||
0x9a, 0x91, 0xf0, 0x4f, 0xa2, 0xc7, 0xbd, 0x71, 0x02, 0xa8, 0xd7, 0x3f, 0x16, 0xa3, 0xcf, 0xad,
|
||||
0xe8, 0xaa, 0xdf, 0x4f, 0x3f, 0xe2, 0xa2, 0x12, 0x5c, 0xcd, 0xd7, 0x7c, 0x6b, 0x9f, 0x78, 0xb5,
|
||||
0xb4, 0x37, 0x1c, 0xe0, 0x80, 0x57, 0x25, 0xb0, 0xf9, 0xc0, 0x27, 0xaf, 0x14, 0x7d, 0x91, 0xe1,
|
||||
0xff, 0xdb, 0x20, 0x1e, 0x9c, 0x17, 0x0c, 0xe7, 0x77, 0x05, 0x3a, 0x17, 0x2a, 0xd5, 0x26, 0xdc,
|
||||
0xaf, 0xd3, 0x38, 0x95, 0xe1, 0xa9, 0x47, 0x30, 0x5c, 0x5b, 0x16, 0x7f, 0x2e, 0x7c, 0x6f, 0x99,
|
||||
0x15, 0x81, 0xa6, 0x52, 0xee, 0x47, 0x31, 0x54, 0x76, 0x0c, 0x2e, 0xd7, 0x74, 0x21, 0x4e, 0x50,
|
||||
0xdf, 0xec, 0xdd, 0x4c, 0xf2, 0x94, 0xc9, 0x74, 0xb8, 0x9e, 0xbc, 0xa2, 0x5b, 0x5a, 0xb3, 0xc0,
|
||||
0xbe, 0xb5, 0x0d, 0xfa, 0xf7, 0x82, 0xaf, 0xde, 0x14, 0x33, 0xd9, 0x0c, 0xa2, 0xa8, 0x9d, 0x65,
|
||||
0x1e, 0x75, 0xd6, 0x7e, 0xbc, 0x7c, 0x3e, 0x36, 0xf5, 0xa1, 0x65, 0xee, 0x61, 0x32, 0x61, 0x29,
|
||||
0x39, 0xc1, 0xec, 0xd3, 0x99, 0xe4, 0x60, 0x74, 0xb9, 0x96, 0xd9, 0x3a, 0x88, 0xe0, 0x1e, 0x0a,
|
||||
};
|
||||
|
||||
const unsigned char c_icc_exp[] = {
|
||||
0x03,
|
||||
};
|
||||
|
||||
const unsigned char c_sdad_cr[] = {
|
||||
0x1c, 0x00, 0x9f, 0xc4, 0x86, 0x79, 0x15, 0x7d, 0xbf, 0xf4, 0x5f, 0x65, 0xd3, 0x3f, 0xf7, 0x8d,
|
||||
0x4f, 0xcb, 0xf0, 0xcf, 0x5e, 0xa4, 0x20, 0x8d, 0x10, 0x7a, 0xe9, 0x5a, 0xa3, 0x8c, 0x54, 0x6d,
|
||||
0x0e, 0x5a, 0x18, 0xb8, 0x74, 0x03, 0xa1, 0x2b, 0xd4, 0x47, 0xa8, 0xbb, 0xfc, 0x1e, 0x49, 0xce,
|
||||
0x0b, 0x2e, 0x25, 0x13, 0x89, 0x20, 0x57, 0x03, 0xc9, 0xbb, 0x1a, 0x88, 0xcc, 0x79, 0xf1, 0xdd,
|
||||
0xc2, 0xf9, 0x84, 0x1e, 0xad, 0xf0, 0x7c, 0xe0, 0x7b, 0x62, 0x51, 0x1d, 0xdc, 0x93, 0xdf, 0x59,
|
||||
0xf2, 0x8f, 0x0e, 0x91, 0xf9, 0x23, 0x32, 0xd2, 0x9c, 0xde, 0xf2, 0xbc, 0xcb, 0x10, 0x08, 0x85,
|
||||
0x05, 0x00, 0xef, 0x3e, 0x47, 0x0a, 0x4c, 0xb1, 0x8c, 0xd9, 0x1a, 0xa5, 0xc1, 0xa1, 0x08, 0xf3,
|
||||
|
||||
};
|
||||
|
||||
const unsigned char c_ssd1[] = {
|
||||
0x5f, 0x25, 0x03, 0x14, 0x05, 0x01, 0x5f, 0x24, 0x03, 0x15, 0x06, 0x30, 0x5a, 0x08, 0x52, 0x85,
|
||||
0x88, 0x12, 0x54, 0x34, 0x56, 0x53, 0x5f, 0x34, 0x01, 0x01, 0x8e, 0x0c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x1f, 0x03, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05,
|
||||
0xbc, 0x50, 0xbc, 0x00, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05,
|
||||
0xbc, 0x70, 0xbc, 0x98, 0x00, 0x9f, 0x4a, 0x01, 0x82, 0x5f, 0x28, 0x02, 0x06, 0x43, 0x8c, 0x21,
|
||||
0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03,
|
||||
0x9c, 0x01, 0x9f, 0x37, 0x04, 0x9f, 0x35, 0x01, 0x9f, 0x45, 0x02, 0x9f, 0x4c, 0x08, 0x9f, 0x34,
|
||||
0x03, 0x8d, 0x0c, 0x91, 0x0a, 0x8a, 0x02, 0x95, 0x05, 0x9f, 0x37, 0x04, 0x9f, 0x4c, 0x08,
|
||||
0x39, 0x00,
|
||||
};
|
||||
static const struct tlv ssd1_tlv = {
|
||||
.len = sizeof(c_ssd1),
|
||||
.value = c_ssd1,
|
||||
};
|
||||
|
||||
const unsigned char c_pan[] = {
|
||||
0x52, 0x85, 0x88, 0x12, 0x54, 0x34, 0x56, 0x53,
|
||||
};
|
||||
|
||||
const unsigned char c_dd1[] = {
|
||||
0x12, 0x34, 0x57, 0x79,
|
||||
};
|
||||
|
||||
const unsigned char c_dd2[] = {
|
||||
0x9f, 0x27, 0x01, 0x40, 0x9f, 0x36, 0x02, 0x00, 0x10, 0x9f, 0x10, 0x12, 0x00, 0x10, 0x90, 0x40,
|
||||
0x01, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
};
|
||||
|
||||
const unsigned char c_crm1[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x06, 0x43, 0x14, 0x09, 0x25, 0x50, 0x12, 0x34, 0x57, 0x79, 0x23, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00,
|
||||
};
|
||||
static const struct tlv crm1_tlv = {
|
||||
.len = sizeof(c_crm1),
|
||||
.value = c_crm1,
|
||||
};
|
||||
|
||||
static int cda_test_raw(bool verbose) {
|
||||
const struct emv_pk *pk = &c_mchip_05;
|
||||
|
||||
struct crypto_pk *kcp = crypto_pk_open(PK_RSA,
|
||||
pk->modulus, pk->mlen,
|
||||
pk->exp, pk->elen);
|
||||
if (!kcp)
|
||||
return 1;
|
||||
|
||||
unsigned char *ipk_data;
|
||||
size_t ipk_data_len;
|
||||
ipk_data = crypto_pk_encrypt(kcp, c_issuer_cert, sizeof(c_issuer_cert), &ipk_data_len);
|
||||
crypto_pk_close(kcp);
|
||||
|
||||
if (!ipk_data)
|
||||
return 1;
|
||||
|
||||
if (verbose) {
|
||||
printf("issuer cert:\n");
|
||||
dump_buffer(ipk_data, ipk_data_len, stdout, 0);
|
||||
}
|
||||
|
||||
size_t ipk_pk_len = ipk_data[13];
|
||||
unsigned char *ipk_pk = malloc(ipk_pk_len);
|
||||
memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36);
|
||||
memcpy(ipk_pk + ipk_data_len - 36, c_issuer_rem, sizeof(c_issuer_rem));
|
||||
|
||||
struct crypto_hash *ch;
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, ipk_data + 1, 14);
|
||||
crypto_hash_write(ch, ipk_pk, ipk_pk_len);
|
||||
crypto_hash_write(ch, c_issuer_exp, sizeof(c_issuer_exp));
|
||||
|
||||
unsigned char *h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash:\n");
|
||||
dump_buffer(h, 20, stdout, 0);
|
||||
}
|
||||
|
||||
if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) {
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_data);
|
||||
|
||||
struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len,
|
||||
c_issuer_exp, (int) sizeof(c_issuer_exp));
|
||||
free(ipk_pk);
|
||||
if (!ikcp)
|
||||
return 1;
|
||||
|
||||
unsigned char *iccpk_data;
|
||||
size_t iccpk_data_len;
|
||||
iccpk_data = crypto_pk_encrypt(ikcp, c_icc_cert, sizeof(c_icc_cert), &iccpk_data_len);
|
||||
crypto_pk_close(ikcp);
|
||||
|
||||
if (!iccpk_data)
|
||||
return 1;
|
||||
|
||||
if (verbose) {
|
||||
printf("icc cert:\n");
|
||||
dump_buffer(iccpk_data, iccpk_data_len, stdout, 0);
|
||||
}
|
||||
|
||||
size_t iccpk_pk_len = iccpk_data[19];
|
||||
unsigned char *iccpk_pk = malloc(iccpk_pk_len);
|
||||
memcpy(iccpk_pk, iccpk_data + 21, /*iccpk_data_len - 36*/iccpk_pk_len);
|
||||
/*memcpy(iccpk_pk + iccpk_data_len - 36, icc_rem, sizeof(icc_rem));*/
|
||||
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(iccpk_pk);
|
||||
free(iccpk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, iccpk_data + 1, iccpk_data_len - 22);
|
||||
crypto_hash_write(ch, c_icc_exp, sizeof(c_icc_exp));
|
||||
crypto_hash_write(ch, c_ssd1, sizeof(c_ssd1));
|
||||
|
||||
h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
free(iccpk_pk);
|
||||
free(iccpk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash1.1:\n");
|
||||
dump_buffer(h, 20, stdout, 0);
|
||||
}
|
||||
|
||||
if (memcmp(iccpk_data + iccpk_data_len - 21, h, 20)) {
|
||||
crypto_hash_close(ch);
|
||||
free(iccpk_pk);
|
||||
free(iccpk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
free(iccpk_data);
|
||||
|
||||
struct crypto_pk *icckcp = crypto_pk_open(PK_RSA, iccpk_pk, (int) iccpk_pk_len,
|
||||
c_issuer_exp, (int) sizeof(c_issuer_exp));
|
||||
free(iccpk_pk);
|
||||
if (!icckcp)
|
||||
return 1;
|
||||
|
||||
size_t sdad_len;
|
||||
unsigned char *sdad = crypto_pk_encrypt(icckcp, c_sdad_cr, sizeof(c_sdad_cr), &sdad_len);
|
||||
crypto_pk_close(icckcp);
|
||||
if (!sdad)
|
||||
return 1;
|
||||
|
||||
if (verbose) {
|
||||
printf("SDAD:\n");
|
||||
dump_buffer(sdad, sdad_len, stdout, 0);
|
||||
}
|
||||
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(sdad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, sdad + 1, sdad_len - 22);
|
||||
crypto_hash_write(ch, c_dd1, sizeof(c_dd1));
|
||||
|
||||
unsigned char *h2 = crypto_hash_read(ch);
|
||||
if (!h2) {
|
||||
crypto_hash_close(ch);
|
||||
free(sdad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash2:\n");
|
||||
dump_buffer(h2, 20, stdout, 0);
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(sdad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, c_crm1, sizeof(c_crm1));
|
||||
crypto_hash_write(ch, c_dd2, sizeof(c_dd2));
|
||||
|
||||
h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
free(sdad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash2.1:\n");
|
||||
dump_buffer(h, 20, stdout, 0);
|
||||
}
|
||||
|
||||
if (memcmp(sdad + 5 + 8 + 1 + 8, h, 20)) {
|
||||
crypto_hash_close(ch);
|
||||
free(sdad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
|
||||
free(sdad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cda_test_pk(bool verbose) {
|
||||
const struct emv_pk *pk = &c_mchip_05;
|
||||
struct tlvdb *db;
|
||||
|
||||
db = tlvdb_external(0x90, sizeof(c_issuer_cert), c_issuer_cert);
|
||||
tlvdb_add(db, tlvdb_external(0x9f32, sizeof(c_issuer_exp), c_issuer_exp));
|
||||
tlvdb_add(db, tlvdb_external(0x92, sizeof(c_issuer_rem), c_issuer_rem));
|
||||
tlvdb_add(db, tlvdb_external(0x5a, sizeof(c_pan), c_pan));
|
||||
|
||||
struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db);
|
||||
if (!ipk) {
|
||||
fprintf(stderr, "Could not recover Issuer certificate!\n");
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
tlvdb_add(db, tlvdb_external(0x9f46, sizeof(c_icc_cert), c_icc_cert));
|
||||
tlvdb_add(db, tlvdb_external(0x9f47, sizeof(c_icc_exp), c_icc_exp));
|
||||
/*tlvdb_add(db, tlvdb_external(0x9f48, sizeof(issuer_rem), issuer_rem));*/
|
||||
|
||||
struct emv_pk *iccpk = emv_pki_recover_icc_cert(ipk, db, &ssd1_tlv);
|
||||
if (!iccpk) {
|
||||
fprintf(stderr, "Could not recover ICC certificate!\n");
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
tlvdb_add(db, tlvdb_fixed(0x9f37, sizeof(c_dd1), c_dd1));
|
||||
|
||||
struct tlvdb *cda_db;
|
||||
cda_db = tlvdb_fixed(0x9f27, 1, (unsigned char[]) { 0x40 });
|
||||
tlvdb_add(cda_db, tlvdb_fixed(0x9f36, 2, (unsigned char[]) { 0x00, 0x10 }));
|
||||
tlvdb_add(cda_db, tlvdb_external(0x9f4b, sizeof(c_sdad_cr), c_sdad_cr));
|
||||
tlvdb_add(cda_db, tlvdb_fixed(0x9f10, 0x12,
|
||||
(unsigned char[]) { 0x00, 0x10, 0x90, 0x40, 0x01, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}));
|
||||
|
||||
struct tlvdb *idndb = emv_pki_perform_cda(iccpk, db, cda_db,
|
||||
NULL,
|
||||
&crm1_tlv,
|
||||
NULL);
|
||||
if (!idndb) {
|
||||
fprintf(stderr, "Could not recover IDN!\n");
|
||||
tlvdb_free(cda_db);
|
||||
emv_pk_free(iccpk);
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const struct tlv *idn = tlvdb_get(idndb, 0x9f4c, NULL);
|
||||
if (!idn) {
|
||||
fprintf(stderr, "IDN not found!\n");
|
||||
tlvdb_free(idndb);
|
||||
tlvdb_free(cda_db);
|
||||
emv_pk_free(iccpk);
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("IDN:\n");
|
||||
dump_buffer(idn->value, idn->len, stdout, 0);
|
||||
}
|
||||
|
||||
tlvdb_free(idndb);
|
||||
tlvdb_free(cda_db);
|
||||
emv_pk_free(iccpk);
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_cda_test(bool verbose) {
|
||||
int ret;
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
ret = cda_test_raw(verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "CDA raw test: failed\n");
|
||||
return ret;
|
||||
}
|
||||
fprintf(stdout, "CDA raw test: passed\n");
|
||||
|
||||
ret = cda_test_pk(verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "CDA test pk: failed\n");
|
||||
return ret;
|
||||
}
|
||||
fprintf(stdout, "CDA test pk: passed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
client/src/emv/test/cda_test.h
Normal file
21
client/src/emv/test/cda_test.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* emv-tools - a set of tools 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.
|
||||
*/
|
||||
|
||||
#ifndef __CDA_TEST_H
|
||||
#define __CDA_TEST_H
|
||||
#include <stdbool.h>
|
||||
|
||||
int exec_cda_test(bool verbose);
|
||||
#endif
|
||||
337
client/src/emv/test/crypto_test.c
Normal file
337
client/src/emv/test/crypto_test.c
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* emv-tools - a set of tools 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "commonutil.h" // ARRAYLEN
|
||||
|
||||
#include "../crypto.h"
|
||||
#include "../dump.h"
|
||||
#include "util_posix.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "crypto_test.h"
|
||||
|
||||
static int test_genkey(unsigned int keylength, unsigned char *msg, size_t msg_len, bool verbose) {
|
||||
int ret = 1;
|
||||
size_t tmp_len, tmp2_len;
|
||||
unsigned char *tmp, *tmp2;
|
||||
struct crypto_pk *pk;
|
||||
|
||||
printf("Testing key length %u ", keylength);
|
||||
uint64_t ms = msclock();
|
||||
|
||||
pk = crypto_pk_genkey(PK_RSA, 1, keylength, 3);
|
||||
if (!pk) {
|
||||
fprintf(stderr, "ERROR: key generation error.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp_len = crypto_pk_get_nbits(pk);
|
||||
if (tmp_len != keylength) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_get_nbits.\n");
|
||||
goto close;
|
||||
}
|
||||
|
||||
tmp = crypto_pk_decrypt(pk, msg, msg_len, &tmp_len);
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_decrypt.\n");
|
||||
goto close;
|
||||
}
|
||||
|
||||
tmp2 = crypto_pk_encrypt(pk, tmp, tmp_len, &tmp2_len);
|
||||
if (!tmp2) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_encrypt.\n");
|
||||
goto free_tmp;
|
||||
}
|
||||
|
||||
if (tmp2_len == msg_len && !memcmp(tmp2, msg, tmp2_len)) {
|
||||
ret = 0;
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: encrypt-decrypt sequence length or data error.\n");
|
||||
}
|
||||
|
||||
free(tmp2);
|
||||
printf("passed. (%"PRIu64" ms) \n", msclock() - ms);
|
||||
free_tmp:
|
||||
free(tmp);
|
||||
close:
|
||||
crypto_pk_close(pk);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned char message[4096 / 8] =
|
||||
"aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb"
|
||||
"ccccccccccccccccdddddddddddddddd"
|
||||
"eeeeeeeeeeeeeeeeffffffffffffffff"
|
||||
"gggggggggggggggghhhhhhhhhhhhhhhh"
|
||||
"iiiiiiiiiiiiiiiijjjjjjjjjjjjjjjj"
|
||||
"kkkkkkkkkkkkkkkkllllllllllllllll"
|
||||
"mmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnn"
|
||||
"oooooooooooooooopppppppppppppppp"
|
||||
"qqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrr"
|
||||
"sssssssssssssssstttttttttttttttt"
|
||||
"uuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvv"
|
||||
"wwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxx"
|
||||
"yyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzz"
|
||||
"aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb"
|
||||
"ccccccccccccccccdddddddddddddddd"
|
||||
"eeeeeeeeeeeeeeeeffffffffffffffff"
|
||||
;
|
||||
|
||||
static unsigned char pk_N[] = {
|
||||
0xdb, 0x12, 0xe4, 0xf1, 0x8d, 0x43, 0x74, 0xf0, 0xec, 0x38, 0xdc, 0xfb, 0xf9, 0x20, 0x75, 0x6d,
|
||||
0x05, 0xf4, 0x36, 0xc2, 0x21, 0xac, 0x34, 0x0d, 0x16, 0xc5, 0x23, 0xcb, 0xfc, 0x9a, 0x8a, 0xd1,
|
||||
0xe0, 0xbd, 0xda, 0xe5, 0x77, 0xd5, 0xaf, 0x65, 0x8d, 0x6b, 0x62, 0x5c, 0xcd, 0x89, 0x06, 0x8d,
|
||||
0x11, 0x19, 0x6b, 0x0e, 0x3e, 0xe2, 0x80, 0x45, 0xf6, 0x44, 0x55, 0x21, 0x9c, 0x86, 0x90, 0x00,
|
||||
0xa8, 0xaf, 0x8c, 0x94, 0xde, 0x3f, 0xe8, 0x56, 0x52, 0xfe, 0xee, 0xa5, 0x36, 0x72, 0x07, 0xf2,
|
||||
0xcf, 0x8e, 0xf0, 0xbd, 0xff, 0x36, 0xd5, 0xf2, 0xad, 0x74, 0x1d, 0x17, 0xd0, 0xb7, 0x93, 0xe2,
|
||||
0x2c, 0x8d, 0x3f, 0xb6, 0x7c, 0x65, 0x19, 0x9f, 0xa7, 0x80, 0x1f, 0x9f, 0xe5, 0x2f, 0x2d, 0x75,
|
||||
0xc9, 0xc2, 0xe9, 0x70, 0xfa, 0x1e, 0x5a, 0xc6, 0xa3, 0x82, 0xd1, 0x29, 0x5a, 0x60, 0xce, 0x1f,
|
||||
0x40, 0x2e, 0xfc, 0x2a, 0x5e, 0xde, 0xc9, 0x67, 0xfc, 0x45, 0x18, 0xce, 0xf2, 0x83, 0x94, 0x53,
|
||||
0xd6, 0x4f, 0x2e, 0xc5, 0x2d, 0xa1, 0xa5, 0x7a, 0x63, 0x26, 0x70, 0xcb, 0x76, 0xfc, 0xb5, 0x8d,
|
||||
0x0f, 0x88, 0x4c, 0x07, 0xba, 0xfa, 0x8b, 0xbc, 0xa0, 0xea, 0xea, 0x0a, 0xe6, 0xa5, 0x44, 0xa5,
|
||||
0x0d, 0x12, 0x66, 0x2b, 0xf7, 0xc4, 0x76, 0xa3, 0x82, 0xa6, 0x2b, 0xb2, 0x5a, 0x27, 0xcd, 0x11,
|
||||
0xd2, 0x9d, 0x42, 0x86, 0x8c, 0x82, 0xc8, 0xe1, 0xff, 0x7d, 0xf1, 0xd9, 0xd9, 0xa1, 0xf3, 0x3d,
|
||||
0xc3, 0x12, 0x4e, 0x47, 0xc8, 0xa2, 0xcd, 0x72, 0x5a, 0x18, 0xea, 0x89, 0x5c, 0x73, 0x28, 0x52,
|
||||
0xf8, 0xdb, 0x70, 0xdc, 0x92, 0xc9, 0xb7, 0x98, 0x10, 0x94, 0x79, 0xdc, 0x9e, 0x12, 0x6c, 0x14,
|
||||
0x78, 0xf9, 0x5a, 0xad, 0x00, 0x98, 0xc8, 0x17, 0x79, 0x8a, 0xed, 0xe7, 0xc3, 0xd3, 0xa7, 0x8b,
|
||||
};
|
||||
|
||||
static unsigned char pk_E[] = {
|
||||
0x01, 0x00, 0x01,
|
||||
};
|
||||
|
||||
static unsigned char pk_D[] = {
|
||||
0x01, 0x17, 0xd4, 0x0a, 0x9c, 0x80, 0xd4, 0xa9, 0x8b, 0x14, 0x31, 0x8e, 0x14, 0x4d, 0x24, 0x28,
|
||||
0xda, 0x19, 0xc0, 0xd8, 0x31, 0x20, 0xd1, 0xd5, 0xaa, 0xe2, 0x6a, 0xee, 0x4e, 0xa1, 0x5a, 0xc5,
|
||||
0xf7, 0x50, 0x1b, 0x32, 0x7f, 0xe9, 0x92, 0x09, 0x78, 0xae, 0x2b, 0x7c, 0x79, 0x0e, 0x10, 0xf9,
|
||||
0x4d, 0x37, 0x8a, 0x40, 0x34, 0xf2, 0x1e, 0x5f, 0xba, 0xfd, 0xd6, 0x4a, 0xe7, 0xa4, 0x08, 0x3d,
|
||||
0xe8, 0x99, 0x8f, 0xa3, 0x02, 0x84, 0xe1, 0x1c, 0xe5, 0x27, 0x1e, 0x7b, 0xb6, 0x8c, 0xd5, 0x1b,
|
||||
0x52, 0x0b, 0xcd, 0x89, 0xb5, 0x27, 0x49, 0xe3, 0xff, 0x17, 0x90, 0x39, 0x99, 0x32, 0x01, 0x4b,
|
||||
0xe4, 0x9b, 0x03, 0xd1, 0x5e, 0x47, 0x86, 0xdc, 0x34, 0x12, 0xc0, 0x95, 0xa4, 0xa8, 0x1a, 0x9a,
|
||||
0xf6, 0xd9, 0xc1, 0x1e, 0x6e, 0x31, 0x0e, 0x94, 0xe5, 0x25, 0xf6, 0xf3, 0x34, 0xdf, 0x3c, 0xc8,
|
||||
0x0a, 0xc5, 0x8c, 0x00, 0x5c, 0x59, 0x55, 0x06, 0xd1, 0x39, 0x84, 0x35, 0x96, 0x40, 0xe8, 0xb2,
|
||||
0xf7, 0x13, 0x83, 0x37, 0xe1, 0xe2, 0x79, 0x41, 0x90, 0x2a, 0xc3, 0x71, 0xc5, 0xcf, 0xf0, 0xaa,
|
||||
0x01, 0x2f, 0x48, 0x9c, 0x3f, 0x29, 0x7b, 0xb7, 0x5c, 0xef, 0x25, 0xde, 0x34, 0x23, 0x81, 0x7a,
|
||||
0x4c, 0x3a, 0x9b, 0xe4, 0xa7, 0x44, 0x73, 0xbf, 0xf7, 0x39, 0x43, 0xa4, 0x39, 0xa0, 0x1b, 0xf7,
|
||||
0x4f, 0x5f, 0x14, 0x49, 0x32, 0x0e, 0x66, 0xd0, 0x29, 0xb5, 0x80, 0xe0, 0xba, 0x3b, 0x88, 0x2b,
|
||||
0x14, 0xa4, 0x26, 0x00, 0x2f, 0x50, 0x20, 0x4e, 0xfa, 0xc2, 0x44, 0x72, 0x72, 0x6c, 0x2a, 0x77,
|
||||
0x85, 0x20, 0xe0, 0x1d, 0x95, 0x6a, 0x66, 0xe7, 0xb8, 0xca, 0x5b, 0xc9, 0xc3, 0xf3, 0x39, 0xef,
|
||||
0xd7, 0xd5, 0x45, 0xb6, 0x3e, 0x19, 0xea, 0x7c, 0x56, 0x20, 0x1b, 0x95, 0x86, 0x2e, 0xc7, 0x51,
|
||||
};
|
||||
|
||||
static unsigned char pk_P[] = {
|
||||
0xf5, 0x93, 0x0f, 0x76, 0x00, 0xab, 0x37, 0x01, 0xb9, 0x52, 0xb6, 0x82, 0xf9, 0xf5, 0xae, 0x29,
|
||||
0x8f, 0xd5, 0x08, 0xbc, 0xf7, 0x9f, 0x84, 0xb6, 0x4c, 0x94, 0xb5, 0xfc, 0xfe, 0xe1, 0xcd, 0x6a,
|
||||
0xf4, 0x9c, 0xa7, 0x33, 0xdb, 0xd8, 0xc8, 0xc1, 0xc0, 0x8d, 0x65, 0xed, 0x29, 0x99, 0x6c, 0x5c,
|
||||
0xbe, 0x08, 0xac, 0x04, 0xe4, 0x3a, 0x18, 0xe2, 0x0f, 0x70, 0x26, 0x70, 0x9b, 0x71, 0xfc, 0x9f,
|
||||
0x22, 0xea, 0x90, 0x3b, 0xc2, 0xa5, 0x16, 0x7a, 0xcd, 0x04, 0x3e, 0xa6, 0x37, 0x49, 0xa7, 0xee,
|
||||
0xaa, 0xe4, 0x9d, 0xaa, 0x9b, 0xb0, 0xe2, 0x6a, 0x9d, 0x1e, 0xcd, 0x83, 0x4e, 0xd8, 0x59, 0x6d,
|
||||
0x03, 0xd5, 0x4c, 0x5e, 0xc5, 0x22, 0x10, 0xb7, 0xcc, 0x0c, 0x90, 0x76, 0x05, 0x21, 0xe7, 0x77,
|
||||
0x5c, 0x88, 0x5f, 0xd0, 0x5f, 0x9e, 0x2e, 0x49, 0x56, 0xf4, 0x2b, 0xa9, 0x99, 0x57, 0x74, 0x19,
|
||||
};
|
||||
|
||||
static unsigned char pk_Q[] = {
|
||||
0xe4, 0x5f, 0xd2, 0x28, 0xbd, 0xf3, 0xdd, 0x70, 0x3d, 0xfd, 0x01, 0x23, 0xae, 0x93, 0x6a, 0x91,
|
||||
0xca, 0x68, 0xb1, 0xdb, 0x81, 0xab, 0x1e, 0x63, 0x76, 0x9b, 0x6d, 0xaa, 0x41, 0x87, 0x5a, 0x79,
|
||||
0xe7, 0xce, 0xd6, 0x84, 0x32, 0x53, 0xf5, 0xfc, 0xb7, 0x41, 0x7c, 0xcb, 0x88, 0x09, 0xcb, 0xe9,
|
||||
0x07, 0x16, 0x28, 0x55, 0x23, 0xe5, 0xf2, 0xf5, 0x23, 0xf5, 0xee, 0x2b, 0x9d, 0x91, 0x56, 0xc6,
|
||||
0x30, 0x91, 0x4d, 0x16, 0x11, 0x6c, 0x48, 0x45, 0xe8, 0x5d, 0x0e, 0x9e, 0x04, 0xc8, 0xb6, 0xdd,
|
||||
0xba, 0x0d, 0xdf, 0x53, 0x56, 0xfa, 0x0b, 0x0b, 0x99, 0x8d, 0xea, 0x5c, 0x45, 0x7d, 0xed, 0xad,
|
||||
0x1f, 0xc5, 0xc1, 0x7d, 0x63, 0x31, 0xf8, 0x32, 0xb5, 0x33, 0xb0, 0xef, 0xce, 0x2e, 0x74, 0x1e,
|
||||
0x77, 0x2a, 0x18, 0x35, 0x3d, 0x6e, 0x01, 0xba, 0xde, 0x21, 0x8e, 0x14, 0x12, 0xc3, 0x0d, 0x43,
|
||||
};
|
||||
|
||||
static unsigned char pk_dP[] = {
|
||||
0x5a, 0xc8, 0xf7, 0x1a, 0x44, 0xbd, 0x07, 0x24, 0xd8, 0x02, 0x3f, 0xfe, 0xc3, 0xb1, 0x93, 0xa5,
|
||||
0x41, 0xcb, 0x1b, 0xe3, 0xe0, 0x17, 0x54, 0xd4, 0xa0, 0x13, 0x0a, 0x04, 0x71, 0xa5, 0xc0, 0x6f,
|
||||
0x1d, 0xe7, 0x1b, 0xd9, 0x0c, 0x19, 0x64, 0x7e, 0x5c, 0x54, 0xe9, 0xad, 0x77, 0x87, 0x84, 0x8b,
|
||||
0xf4, 0xa4, 0xf8, 0x13, 0x06, 0xdc, 0x83, 0x7e, 0x6e, 0xfe, 0xa2, 0xf7, 0x56, 0x40, 0x19, 0x88,
|
||||
0x2b, 0x3c, 0x53, 0xfe, 0x03, 0xc3, 0x4c, 0x40, 0x31, 0xb2, 0xb4, 0x06, 0x76, 0xc2, 0x00, 0x17,
|
||||
0x37, 0x8e, 0x34, 0xcb, 0x71, 0xab, 0x3e, 0xc8, 0xf3, 0x35, 0x03, 0xfc, 0xdb, 0x15, 0x18, 0x5a,
|
||||
0x38, 0xe4, 0x8d, 0xcb, 0x2b, 0x4d, 0xa0, 0xa8, 0x92, 0x02, 0xc3, 0x15, 0x1e, 0x68, 0x9e, 0x4d,
|
||||
0x7e, 0x23, 0xdc, 0x68, 0x08, 0x31, 0x4e, 0x23, 0x46, 0xc6, 0x15, 0xae, 0x29, 0x46, 0x2f, 0x61,
|
||||
};
|
||||
|
||||
static unsigned char pk_dQ[] = {
|
||||
0x33, 0x61, 0x9f, 0xae, 0x0c, 0xf6, 0xc6, 0x16, 0x8f, 0xcb, 0xd1, 0xaa, 0xce, 0x87, 0x5a, 0x4d,
|
||||
0xcc, 0xe5, 0x7b, 0x46, 0xb0, 0xc8, 0xe8, 0x40, 0x66, 0x9a, 0x17, 0xb5, 0x5b, 0xa2, 0xf1, 0x67,
|
||||
0x46, 0x11, 0x52, 0x50, 0x51, 0xe6, 0x74, 0x0c, 0xd4, 0xca, 0x46, 0x22, 0xa0, 0xcb, 0xdb, 0x75,
|
||||
0xe5, 0x63, 0x45, 0xd5, 0xca, 0x0a, 0xdd, 0x7b, 0xec, 0x08, 0x53, 0xfa, 0xba, 0x2b, 0xce, 0x03,
|
||||
0x2f, 0x40, 0x31, 0xc0, 0xca, 0x50, 0xbb, 0x7e, 0x07, 0x06, 0x90, 0xd8, 0x5a, 0xa9, 0x32, 0x03,
|
||||
0x76, 0xed, 0xd2, 0x16, 0x35, 0x16, 0x72, 0xcf, 0xbc, 0x4f, 0xa2, 0xaf, 0xf9, 0xee, 0x98, 0x40,
|
||||
0x00, 0x4b, 0x04, 0xfa, 0x8a, 0x0b, 0xdf, 0x14, 0xc1, 0x92, 0x0c, 0xb8, 0x17, 0x82, 0x7a, 0x1b,
|
||||
0xb4, 0xa1, 0xe2, 0xea, 0x6f, 0x94, 0xc5, 0x8c, 0xde, 0x97, 0x5c, 0x19, 0x06, 0x13, 0x9e, 0x73,
|
||||
};
|
||||
|
||||
static unsigned char pk_I[] = {
|
||||
0x75, 0x40, 0xc6, 0x02, 0x7e, 0x4f, 0xad, 0xdb, 0x95, 0xac, 0x07, 0x8d, 0x80, 0xb6, 0x80, 0x02,
|
||||
0x06, 0xdd, 0xb8, 0x5d, 0x92, 0x65, 0x69, 0x26, 0x86, 0x61, 0x6c, 0x87, 0x4e, 0xe5, 0x03, 0x68,
|
||||
0xc6, 0x10, 0x15, 0x8c, 0x43, 0x3a, 0x45, 0x63, 0x48, 0xb7, 0x8a, 0x8c, 0xa2, 0x2b, 0x34, 0xb6,
|
||||
0x83, 0xfe, 0xa8, 0x10, 0xa5, 0x74, 0xa5, 0xa9, 0x52, 0x42, 0x1f, 0xa0, 0x80, 0x6a, 0xc5, 0x35,
|
||||
0xe8, 0xb8, 0xc2, 0xa0, 0x3f, 0x49, 0x18, 0xcf, 0x0e, 0x54, 0x3c, 0x70, 0x11, 0x11, 0xd3, 0x85,
|
||||
0x8c, 0xb2, 0xe5, 0x74, 0xdf, 0x98, 0xea, 0x6c, 0xc0, 0x5f, 0x7f, 0xff, 0x69, 0xbf, 0x08, 0x8d,
|
||||
0x1b, 0xc4, 0x90, 0xcc, 0xa4, 0xcd, 0xcc, 0x34, 0x58, 0xe5, 0x91, 0x53, 0x3a, 0xd5, 0x39, 0xf4,
|
||||
0xd4, 0x42, 0xc9, 0x17, 0xb2, 0x2c, 0x92, 0x12, 0x37, 0x1b, 0xd3, 0xc5, 0x79, 0xd2, 0x65, 0x61,
|
||||
};
|
||||
|
||||
static int test_pk(bool verbose) {
|
||||
int ret = 1;
|
||||
size_t tmp_len, tmp2_len;
|
||||
unsigned char *tmp, *tmp2;
|
||||
struct crypto_pk *pubk, *privk;
|
||||
unsigned char *msg = message;
|
||||
size_t msg_len = sizeof(pk_N);
|
||||
|
||||
printf("Testing public keys interfaces\n");
|
||||
|
||||
pubk = crypto_pk_open(PK_RSA,
|
||||
pk_N, sizeof(pk_N),
|
||||
pk_E, sizeof(pk_E));
|
||||
if (!pubk) {
|
||||
fprintf(stderr, "ERROR: open public key.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmp_len = crypto_pk_get_nbits(pubk);
|
||||
if (tmp_len != sizeof(pk_N) * 8) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_get_nbits mismatch.\n");
|
||||
goto close_pub;
|
||||
}
|
||||
|
||||
tmp = crypto_pk_get_parameter(pubk, 0, &tmp_len);
|
||||
if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %zu len %zu\n", tmp_len, sizeof(pk_N));
|
||||
free(tmp);
|
||||
goto close_pub;
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
tmp = crypto_pk_get_parameter(pubk, 1, &tmp_len);
|
||||
if (tmp_len != sizeof(pk_E) || memcmp(tmp, pk_E, tmp_len)) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_get_parameter(1) Exponent.\n");
|
||||
free(tmp);
|
||||
goto close_pub;
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
privk = crypto_pk_open_priv(PK_RSA,
|
||||
pk_N, sizeof(pk_N),
|
||||
pk_E, sizeof(pk_E),
|
||||
pk_D, sizeof(pk_D),
|
||||
pk_P, sizeof(pk_P),
|
||||
pk_Q, sizeof(pk_Q),
|
||||
pk_dP, sizeof(pk_dP),
|
||||
pk_dQ, sizeof(pk_dQ),
|
||||
pk_I, sizeof(pk_I));
|
||||
if (!privk) {
|
||||
fprintf(stderr, "ERROR: open private key.\n");
|
||||
goto close_pub;
|
||||
}
|
||||
|
||||
|
||||
tmp_len = crypto_pk_get_nbits(privk);
|
||||
if (tmp_len != sizeof(pk_N) * 8) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_get_nbits mismatch.\n");
|
||||
goto close_pub;
|
||||
}
|
||||
|
||||
tmp = crypto_pk_get_parameter(privk, 0, &tmp_len);
|
||||
if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %zu len %zu\n", tmp_len, sizeof(pk_N));
|
||||
free(tmp);
|
||||
goto close;
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
tmp = crypto_pk_get_parameter(privk, 1, &tmp_len);
|
||||
if (tmp_len != sizeof(pk_E) || memcmp(tmp, pk_E, tmp_len)) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_get_parameter(1) Exponent.\n");
|
||||
free(tmp);
|
||||
goto close;
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
tmp = crypto_pk_decrypt(privk, msg, msg_len, &tmp_len);
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_decrypt.\n");
|
||||
goto close;
|
||||
}
|
||||
|
||||
tmp2 = crypto_pk_encrypt(pubk, tmp, tmp_len, &tmp2_len);
|
||||
if (!tmp2) {
|
||||
fprintf(stderr, "ERROR: crypto_pk_encrypt.\n");
|
||||
goto free_tmp;
|
||||
}
|
||||
|
||||
if (tmp2_len == msg_len && !memcmp(tmp2, msg, tmp2_len)) {
|
||||
ret = 0;
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: encrypt-decrypt sequence length or data error.\n");
|
||||
}
|
||||
|
||||
free(tmp2);
|
||||
free_tmp:
|
||||
free(tmp);
|
||||
|
||||
close:
|
||||
crypto_pk_close(privk);
|
||||
close_pub:
|
||||
crypto_pk_close(pubk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int exec_crypto_test(bool verbose, bool include_slow_tests) {
|
||||
unsigned int keylengths[] = {1024, 2048};
|
||||
unsigned int extra_keylengths[] = {1152, 1408, 1984, 3072, 4096};
|
||||
int i;
|
||||
int ret;
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
ret = test_pk(verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Crypto raw test: failed\n");
|
||||
return ret;
|
||||
}
|
||||
fprintf(stdout, "Crypto raw test: passed\n\n");
|
||||
|
||||
for (i = 0; i < ARRAYLEN(keylengths); i++) {
|
||||
unsigned int kl = keylengths[i];
|
||||
ret = test_genkey(kl, message, kl / 8, verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Crypto generate key[%u] test: failed\n", kl);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (include_slow_tests) {
|
||||
for (i = 0; i < ARRAYLEN(extra_keylengths); i++) {
|
||||
unsigned int kl = extra_keylengths[i];
|
||||
ret = test_genkey(kl, message, kl / 8, verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Crypto generate key[%u] test: failed\n", kl);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
21
client/src/emv/test/crypto_test.h
Normal file
21
client/src/emv/test/crypto_test.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* emv-tools - a set of tools 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 __CRYPTO_TEST_H
|
||||
#define __CRYPTO_TEST_H
|
||||
#include <stdbool.h>
|
||||
|
||||
int exec_crypto_test(bool verbose, bool include_slow_tests);
|
||||
#endif
|
||||
112
client/src/emv/test/cryptotest.c
Normal file
112
client/src/emv/test/cryptotest.c
Normal file
@@ -0,0 +1,112 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Crypto algorithms testing
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "cryptotest.h"
|
||||
#include "util.h"
|
||||
#include "ui.h"
|
||||
|
||||
#include "mbedtls/bignum.h"
|
||||
#include "mbedtls/aes.h"
|
||||
#include "mbedtls/cmac.h"
|
||||
#include "mbedtls/des.h"
|
||||
#include "mbedtls/ecp.h"
|
||||
#include "mbedtls/rsa.h"
|
||||
#include "mbedtls/sha1.h"
|
||||
#include "mbedtls/md5.h"
|
||||
#include "mbedtls/x509.h"
|
||||
#include "mbedtls/base64.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/timing.h"
|
||||
|
||||
#include "crypto_test.h"
|
||||
#include "sda_test.h"
|
||||
#include "dda_test.h"
|
||||
#include "cda_test.h"
|
||||
#include "crypto/libpcrypto.h"
|
||||
#include "emv/emv_roca.h"
|
||||
|
||||
int ExecuteCryptoTests(bool verbose, bool ignore_time, bool include_slow_tests) {
|
||||
int res;
|
||||
bool TestFail = false;
|
||||
|
||||
res = mbedtls_mpi_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_aes_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_des_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_sha1_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_md5_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_rsa_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_entropy_self_test(verbose);
|
||||
if (res && !ignore_time) TestFail = true;
|
||||
|
||||
// retry for CI (when resources too low)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
res = mbedtls_timing_self_test(verbose);
|
||||
if (!res)
|
||||
break;
|
||||
PrintAndLogEx(WARNING, "Repeat timing test %d", i + 1);
|
||||
}
|
||||
if (res && !ignore_time) TestFail = true;
|
||||
|
||||
res = mbedtls_ctr_drbg_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_base64_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_cmac_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = ecdsa_nist_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_ecp_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = mbedtls_x509_self_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = exec_sda_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = exec_dda_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = exec_cda_test(verbose);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = exec_crypto_test(verbose, include_slow_tests);
|
||||
if (res) TestFail = true;
|
||||
|
||||
res = roca_self_test();
|
||||
if (res) TestFail = true;
|
||||
|
||||
PrintAndLogEx(NORMAL, "\n--------------------------");
|
||||
|
||||
if (TestFail)
|
||||
PrintAndLogEx(FAILED, "\tTest(s) [ %s ]", _RED_("Fail"));
|
||||
else
|
||||
PrintAndLogEx(SUCCESS, "\tTest(s) [ %s ]", _GREEN_("OK"));
|
||||
|
||||
return TestFail;
|
||||
}
|
||||
|
||||
16
client/src/emv/test/cryptotest.h
Normal file
16
client/src/emv/test/cryptotest.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Crypto algorithms testing
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef __CRYPTOTEST_H
|
||||
#define __CRYPTOTEST_H
|
||||
#include <stdbool.h>
|
||||
|
||||
int ExecuteCryptoTests(bool verbose, bool ignore_time, bool include_slow_tests);
|
||||
#endif
|
||||
388
client/src/emv/test/dda_test.c
Normal file
388
client/src/emv/test/dda_test.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* emv-tools - a set of tools 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "dda_test.h"
|
||||
|
||||
#include "../emv_pk.h"
|
||||
#include "../crypto.h"
|
||||
#include "../dump.h"
|
||||
#include "../tlv.h"
|
||||
#include "../emv_pki.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct emv_pk mchip_05 = {
|
||||
.rid = { 0xa0, 0x00, 0x00, 0x00, 0x04, },
|
||||
.index = 5,
|
||||
.hash_algo = HASH_SHA_1,
|
||||
.pk_algo = PK_RSA,
|
||||
.hash = {
|
||||
0xeb, 0xfa, 0x0d, 0x5d,
|
||||
0x06, 0xd8, 0xce, 0x70,
|
||||
0x2d, 0xa3, 0xea, 0xe8,
|
||||
0x90, 0x70, 0x1d, 0x45,
|
||||
0xe2, 0x74, 0xc8, 0x45,
|
||||
},
|
||||
.exp = { 0x03, },
|
||||
.elen = 1,
|
||||
.mlen = 1408 / 8,
|
||||
.modulus = (unsigned char[]) {
|
||||
0xb8, 0x04, 0x8a, 0xbc, 0x30, 0xc9, 0x0d, 0x97, 0x63, 0x36, 0x54, 0x3e, 0x3f, 0xd7, 0x09, 0x1c,
|
||||
0x8f, 0xe4, 0x80, 0x0d, 0xf8, 0x20, 0xed, 0x55, 0xe7, 0xe9, 0x48, 0x13, 0xed, 0x00, 0x55, 0x5b,
|
||||
0x57, 0x3f, 0xec, 0xa3, 0xd8, 0x4a, 0xf6, 0x13, 0x1a, 0x65, 0x1d, 0x66, 0xcf, 0xf4, 0x28, 0x4f,
|
||||
0xb1, 0x3b, 0x63, 0x5e, 0xdd, 0x0e, 0xe4, 0x01, 0x76, 0xd8, 0xbf, 0x04, 0xb7, 0xfd, 0x1c, 0x7b,
|
||||
0xac, 0xf9, 0xac, 0x73, 0x27, 0xdf, 0xaa, 0x8a, 0xa7, 0x2d, 0x10, 0xdb, 0x3b, 0x8e, 0x70, 0xb2,
|
||||
0xdd, 0xd8, 0x11, 0xcb, 0x41, 0x96, 0x52, 0x5e, 0xa3, 0x86, 0xac, 0xc3, 0x3c, 0x0d, 0x9d, 0x45,
|
||||
0x75, 0x91, 0x64, 0x69, 0xc4, 0xe4, 0xf5, 0x3e, 0x8e, 0x1c, 0x91, 0x2c, 0xc6, 0x18, 0xcb, 0x22,
|
||||
0xdd, 0xe7, 0xc3, 0x56, 0x8e, 0x90, 0x02, 0x2e, 0x6b, 0xba, 0x77, 0x02, 0x02, 0xe4, 0x52, 0x2a,
|
||||
0x2d, 0xd6, 0x23, 0xd1, 0x80, 0xe2, 0x15, 0xbd, 0x1d, 0x15, 0x07, 0xfe, 0x3d, 0xc9, 0x0c, 0xa3,
|
||||
0x10, 0xd2, 0x7b, 0x3e, 0xfc, 0xcd, 0x8f, 0x83, 0xde, 0x30, 0x52, 0xca, 0xd1, 0xe4, 0x89, 0x38,
|
||||
0xc6, 0x8d, 0x09, 0x5a, 0xac, 0x91, 0xb5, 0xf3, 0x7e, 0x28, 0xbb, 0x49, 0xec, 0x7e, 0xd5, 0x97,
|
||||
},
|
||||
};
|
||||
|
||||
const unsigned char d_issuer_cert[] = {
|
||||
0x17, 0x14, 0x28, 0x4f, 0x76, 0x3b, 0x85, 0x86, 0xee, 0x6d, 0x31, 0x99, 0x51, 0xf7, 0xe6, 0x3f,
|
||||
0xa2, 0x50, 0x76, 0xe5, 0x0d, 0xc9, 0xd3, 0x20, 0x0b, 0xa9, 0x98, 0xd3, 0xa0, 0x52, 0xad, 0xba,
|
||||
0x9a, 0xb6, 0x9a, 0xc6, 0xad, 0x6a, 0xdd, 0x3c, 0xe0, 0x9f, 0x02, 0x78, 0xf4, 0x07, 0x4e, 0xc4,
|
||||
0xee, 0x9b, 0x1d, 0x22, 0x68, 0xa3, 0xe9, 0x53, 0x57, 0x5e, 0x45, 0x4e, 0x50, 0xcd, 0x86, 0x0b,
|
||||
0xf4, 0x24, 0xc5, 0x1c, 0x59, 0x77, 0x12, 0xd2, 0xaa, 0x05, 0x70, 0x89, 0xdd, 0x86, 0x73, 0xe5,
|
||||
0x1b, 0x1e, 0x1d, 0x71, 0x88, 0x03, 0x48, 0x92, 0x07, 0x7a, 0xc1, 0x8a, 0x6a, 0xe2, 0x34, 0x88,
|
||||
0xbe, 0xa9, 0xdf, 0x3b, 0x1a, 0x83, 0xf2, 0xc0, 0x80, 0x0c, 0xd7, 0xc5, 0xcd, 0xf2, 0xfd, 0xe0,
|
||||
0x49, 0x6f, 0x7b, 0xc3, 0x9f, 0xb4, 0xbf, 0x36, 0x32, 0x99, 0xbf, 0xa6, 0x37, 0xb2, 0xec, 0x33,
|
||||
0xc5, 0x07, 0xe3, 0x68, 0x21, 0xee, 0xc2, 0x07, 0x5f, 0x0e, 0x42, 0x0d, 0x38, 0xa1, 0xc9, 0xf3,
|
||||
0x12, 0x72, 0x61, 0xba, 0x31, 0x6c, 0x98, 0x76, 0x74, 0xfa, 0xdb, 0x20, 0xea, 0x7f, 0xeb, 0x75,
|
||||
0xee, 0x45, 0x5d, 0x12, 0x14, 0x6e, 0xa6, 0xf0, 0x2e, 0x8b, 0x01, 0xec, 0x2f, 0xa7, 0xa1, 0x15,
|
||||
};
|
||||
|
||||
const unsigned char d_issuer_rem[] = {
|
||||
0x6e, 0x63, 0xb7, 0xbc, 0x70, 0xab, 0xdd, 0x09, 0x34, 0x1b, 0x34, 0xc0, 0x32, 0x86, 0xba, 0x9b,
|
||||
0xd8, 0x3b, 0xa7, 0x93, 0x6c, 0x5b, 0x77, 0x98, 0xfb, 0x22, 0xc5, 0xe5, 0x3f, 0xf2, 0x40, 0xa2,
|
||||
0x6d, 0xbd, 0x64, 0x15,
|
||||
};
|
||||
|
||||
const unsigned char d_issuer_exp[] = {
|
||||
0x03,
|
||||
};
|
||||
|
||||
const unsigned char d_icc_cert[] = {
|
||||
0xa4, 0x2f, 0xbe, 0xb1, 0x56, 0xb9, 0x8d, 0xcb, 0x05, 0x54, 0xda, 0x06, 0x2a, 0xdc, 0xa5, 0x30,
|
||||
0x9a, 0x91, 0xf0, 0x4f, 0xa2, 0xc7, 0xbd, 0x71, 0x02, 0xa8, 0xd7, 0x3f, 0x16, 0xa3, 0xcf, 0xad,
|
||||
0xe8, 0xaa, 0xdf, 0x4f, 0x3f, 0xe2, 0xa2, 0x12, 0x5c, 0xcd, 0xd7, 0x7c, 0x6b, 0x9f, 0x78, 0xb5,
|
||||
0xb4, 0x37, 0x1c, 0xe0, 0x80, 0x57, 0x25, 0xb0, 0xf9, 0xc0, 0x27, 0xaf, 0x14, 0x7d, 0x91, 0xe1,
|
||||
0xff, 0xdb, 0x20, 0x1e, 0x9c, 0x17, 0x0c, 0xe7, 0x77, 0x05, 0x3a, 0x17, 0x2a, 0xd5, 0x26, 0xdc,
|
||||
0xaf, 0xd3, 0x38, 0x95, 0xe1, 0xa9, 0x47, 0x30, 0x5c, 0x5b, 0x16, 0x7f, 0x2e, 0x7c, 0x6f, 0x99,
|
||||
0x15, 0x81, 0xa6, 0x52, 0xee, 0x47, 0x31, 0x54, 0x76, 0x0c, 0x2e, 0xd7, 0x74, 0x21, 0x4e, 0x50,
|
||||
0xdf, 0xec, 0xdd, 0x4c, 0xf2, 0x94, 0xc9, 0x74, 0xb8, 0x9e, 0xbc, 0xa2, 0x5b, 0x5a, 0xb3, 0xc0,
|
||||
0xbe, 0xb5, 0x0d, 0xfa, 0xf7, 0x82, 0xaf, 0xde, 0x14, 0x33, 0xd9, 0x0c, 0xa2, 0xa8, 0x9d, 0x65,
|
||||
0x1e, 0x75, 0xd6, 0x7e, 0xbc, 0x7c, 0x3e, 0x36, 0xf5, 0xa1, 0x65, 0xee, 0x61, 0x32, 0x61, 0x29,
|
||||
0x39, 0xc1, 0xec, 0xd3, 0x99, 0xe4, 0x60, 0x74, 0xb9, 0x96, 0xd9, 0x3a, 0x88, 0xe0, 0x1e, 0x0a,
|
||||
};
|
||||
|
||||
const unsigned char d_icc_exp[] = {
|
||||
0x03,
|
||||
};
|
||||
|
||||
const unsigned char d_sdad_cr[] = {
|
||||
0x3d, 0x87, 0xf3, 0x10, 0x56, 0x10, 0x2d, 0x25, 0x12, 0xcf, 0xde, 0x30, 0x90, 0x06, 0x27, 0xc1,
|
||||
0x26, 0x3a, 0x76, 0xd1, 0xda, 0xa8, 0x21, 0xf5, 0x08, 0x31, 0xe6, 0x06, 0xc5, 0x45, 0x44, 0xc2,
|
||||
0x58, 0x13, 0x1e, 0xae, 0xbe, 0x87, 0x4d, 0xcb, 0x1a, 0x28, 0xcf, 0x82, 0xd3, 0xff, 0x91, 0x11,
|
||||
0x82, 0x60, 0xbc, 0x91, 0x11, 0x37, 0x11, 0xd3, 0xb2, 0x89, 0xfa, 0x41, 0xbe, 0x69, 0xc7, 0xa7,
|
||||
0xb5, 0xc7, 0x83, 0xe6, 0xf8, 0xf9, 0x7f, 0xce, 0x13, 0xf0, 0x8b, 0x13, 0xfa, 0x44, 0x18, 0x3e,
|
||||
0x37, 0x18, 0xce, 0xbf, 0x0c, 0x41, 0x47, 0x3d, 0x2b, 0x0f, 0xf4, 0xde, 0x44, 0xb6, 0xa0, 0x2d,
|
||||
0x75, 0xad, 0xb6, 0xd4, 0x96, 0x23, 0x93, 0xff, 0xdf, 0x4e, 0x69, 0x02, 0x6c, 0xdf, 0x38, 0xff,
|
||||
};
|
||||
|
||||
const unsigned char d_ssd1[] = {
|
||||
0x5f, 0x25, 0x03, 0x14, 0x05, 0x01, 0x5f, 0x24, 0x03, 0x15, 0x06, 0x30, 0x5a, 0x08, 0x52, 0x85,
|
||||
0x88, 0x12, 0x54, 0x34, 0x56, 0x53, 0x5f, 0x34, 0x01, 0x01, 0x8e, 0x0c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x1f, 0x03, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05,
|
||||
0xbc, 0x50, 0xbc, 0x00, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05,
|
||||
0xbc, 0x70, 0xbc, 0x98, 0x00, 0x9f, 0x4a, 0x01, 0x82, 0x5f, 0x28, 0x02, 0x06, 0x43, 0x8c, 0x21,
|
||||
0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03,
|
||||
0x9c, 0x01, 0x9f, 0x37, 0x04, 0x9f, 0x35, 0x01, 0x9f, 0x45, 0x02, 0x9f, 0x4c, 0x08, 0x9f, 0x34,
|
||||
0x03, 0x8d, 0x0c, 0x91, 0x0a, 0x8a, 0x02, 0x95, 0x05, 0x9f, 0x37, 0x04, 0x9f, 0x4c, 0x08,
|
||||
0x39, 0x00,
|
||||
};
|
||||
static const struct tlv ssd1_tlv = {
|
||||
.len = sizeof(d_ssd1),
|
||||
.value = d_ssd1,
|
||||
};
|
||||
|
||||
const unsigned char d_pan[] = {
|
||||
0x52, 0x85, 0x88, 0x12, 0x54, 0x34, 0x56, 0x53,
|
||||
};
|
||||
|
||||
const unsigned char d_dd1[] = {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
static const struct tlv dd1_tlv = {
|
||||
.len = sizeof(d_dd1),
|
||||
.value = d_dd1,
|
||||
};
|
||||
|
||||
static int dda_test_raw(bool verbose) {
|
||||
const struct emv_pk *pk = &mchip_05;
|
||||
|
||||
struct crypto_pk *kcp = crypto_pk_open(PK_RSA,
|
||||
pk->modulus, pk->mlen,
|
||||
pk->exp, pk->elen);
|
||||
if (!kcp)
|
||||
return 1;
|
||||
|
||||
unsigned char *ipk_data;
|
||||
size_t ipk_data_len;
|
||||
ipk_data = crypto_pk_encrypt(kcp, d_issuer_cert, sizeof(d_issuer_cert), &ipk_data_len);
|
||||
crypto_pk_close(kcp);
|
||||
|
||||
if (!ipk_data)
|
||||
return 1;
|
||||
|
||||
if (verbose) {
|
||||
printf("issuer cert:\n");
|
||||
dump_buffer(ipk_data, ipk_data_len, stdout, 0);
|
||||
}
|
||||
|
||||
size_t ipk_pk_len = ipk_data[13];
|
||||
unsigned char *ipk_pk = malloc(ipk_pk_len);
|
||||
memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36);
|
||||
memcpy(ipk_pk + ipk_data_len - 36, d_issuer_rem, sizeof(d_issuer_rem));
|
||||
|
||||
struct crypto_hash *ch;
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, ipk_data + 1, 14);
|
||||
crypto_hash_write(ch, ipk_pk, ipk_pk_len);
|
||||
crypto_hash_write(ch, d_issuer_exp, sizeof(d_issuer_exp));
|
||||
|
||||
unsigned char *h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash:\n");
|
||||
dump_buffer(h, 20, stdout, 0);
|
||||
}
|
||||
|
||||
if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) {
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_data);
|
||||
|
||||
struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len,
|
||||
d_issuer_exp, (int) sizeof(d_issuer_exp));
|
||||
free(ipk_pk);
|
||||
if (!ikcp)
|
||||
return 1;
|
||||
|
||||
unsigned char *iccpk_data;
|
||||
size_t iccpk_data_len;
|
||||
iccpk_data = crypto_pk_encrypt(ikcp, d_icc_cert, sizeof(d_icc_cert), &iccpk_data_len);
|
||||
crypto_pk_close(ikcp);
|
||||
|
||||
if (!iccpk_data)
|
||||
return 1;
|
||||
|
||||
if (verbose) {
|
||||
printf("icc cert:\n");
|
||||
dump_buffer(iccpk_data, iccpk_data_len, stdout, 0);
|
||||
}
|
||||
|
||||
size_t iccpk_pk_len = iccpk_data[19];
|
||||
unsigned char *iccpk_pk = malloc(iccpk_pk_len);
|
||||
memcpy(iccpk_pk, iccpk_data + 21, /*iccpk_data_len - 36*/iccpk_pk_len);
|
||||
/*memcpy(iccpk_pk + iccpk_data_len - 36, icc_rem, sizeof(icc_rem));*/
|
||||
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(iccpk_pk);
|
||||
free(iccpk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, iccpk_data + 1, iccpk_data_len - 22);
|
||||
crypto_hash_write(ch, d_icc_exp, sizeof(d_icc_exp));
|
||||
crypto_hash_write(ch, d_ssd1, sizeof(d_ssd1));
|
||||
|
||||
h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
free(iccpk_pk);
|
||||
free(iccpk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash1.1:\n");
|
||||
dump_buffer(h, 20, stdout, 0);
|
||||
}
|
||||
|
||||
if (memcmp(iccpk_data + iccpk_data_len - 21, h, 20)) {
|
||||
crypto_hash_close(ch);
|
||||
free(iccpk_pk);
|
||||
free(iccpk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
free(iccpk_data);
|
||||
|
||||
struct crypto_pk *icckcp = crypto_pk_open(PK_RSA, iccpk_pk, (int) iccpk_pk_len,
|
||||
d_issuer_exp, (int) sizeof(d_issuer_exp));
|
||||
free(iccpk_pk);
|
||||
if (!icckcp)
|
||||
return 1;
|
||||
|
||||
size_t sdad_len;
|
||||
unsigned char *sdad = crypto_pk_encrypt(icckcp, d_sdad_cr, sizeof(d_sdad_cr), &sdad_len);
|
||||
crypto_pk_close(icckcp);
|
||||
if (!sdad)
|
||||
return 1;
|
||||
|
||||
if (verbose) {
|
||||
printf("sdad:\n");
|
||||
dump_buffer(sdad, sdad_len, stdout, 0);
|
||||
}
|
||||
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(sdad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, sdad + 1, sdad_len - 22);
|
||||
crypto_hash_write(ch, d_dd1, sizeof(d_dd1));
|
||||
|
||||
unsigned char *h2 = crypto_hash_read(ch);
|
||||
if (!h2) {
|
||||
crypto_hash_close(ch);
|
||||
free(sdad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash2:\n");
|
||||
dump_buffer(h2, 20, stdout, 0);
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
|
||||
free(sdad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dda_test_pk(bool verbose) {
|
||||
const struct emv_pk *pk = &mchip_05;
|
||||
struct tlvdb *db;
|
||||
|
||||
db = tlvdb_external(0x90, sizeof(d_issuer_cert), d_issuer_cert);
|
||||
tlvdb_add(db, tlvdb_external(0x9f32, sizeof(d_issuer_exp), d_issuer_exp));
|
||||
tlvdb_add(db, tlvdb_external(0x92, sizeof(d_issuer_rem), d_issuer_rem));
|
||||
tlvdb_add(db, tlvdb_external(0x5a, sizeof(d_pan), d_pan));
|
||||
|
||||
struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db);
|
||||
if (!ipk) {
|
||||
fprintf(stderr, "Could not recover Issuer certificate!\n");
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
tlvdb_add(db, tlvdb_external(0x9f46, sizeof(d_icc_cert), d_icc_cert));
|
||||
tlvdb_add(db, tlvdb_external(0x9f47, sizeof(d_icc_exp), d_icc_exp));
|
||||
/*tlvdb_add(db, tlvdb_external(0x9f48, sizeof(d_issuer_rem), d_issuer_rem));*/
|
||||
|
||||
struct emv_pk *iccpk = emv_pki_recover_icc_cert(ipk, db, &ssd1_tlv);
|
||||
if (!iccpk) {
|
||||
fprintf(stderr, "Could not recover ICC certificate!\n");
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
tlvdb_add(db, tlvdb_external(0x9f4b, sizeof(d_sdad_cr), d_sdad_cr));
|
||||
|
||||
struct tlvdb *idndb = emv_pki_recover_idn(iccpk, db, &dd1_tlv);
|
||||
if (!idndb) {
|
||||
fprintf(stderr, "Could not recover IDN!\n");
|
||||
emv_pk_free(iccpk);
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const struct tlv *idn = tlvdb_get(idndb, 0x9f4c, NULL);
|
||||
if (!idn) {
|
||||
fprintf(stderr, "IDN not found!\n");
|
||||
tlvdb_free(idndb);
|
||||
emv_pk_free(iccpk);
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("IDN:\n");
|
||||
dump_buffer(idn->value, idn->len, stdout, 0);
|
||||
}
|
||||
|
||||
tlvdb_free(idndb);
|
||||
emv_pk_free(iccpk);
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_dda_test(bool verbose) {
|
||||
int ret;
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
ret = dda_test_raw(verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "DDA raw test: failed\n");
|
||||
return ret;
|
||||
}
|
||||
fprintf(stdout, "DDA raw test: passed\n");
|
||||
|
||||
ret = dda_test_pk(verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "DDA test pk: failed\n");
|
||||
return ret;
|
||||
}
|
||||
fprintf(stdout, "DDA test pk: passed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
client/src/emv/test/dda_test.h
Normal file
21
client/src/emv/test/dda_test.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* emv-tools - a set of tools 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.
|
||||
*/
|
||||
|
||||
#ifndef __DDA_TEST_H
|
||||
#define __DDA_TEST_H
|
||||
#include <stdbool.h>
|
||||
|
||||
int exec_dda_test(bool verbose);
|
||||
#endif
|
||||
276
client/src/emv/test/sda_test.c
Normal file
276
client/src/emv/test/sda_test.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* emv-tools - a set of tools 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "../emv_pk.h"
|
||||
#include "../crypto.h"
|
||||
#include "../dump.h"
|
||||
#include "../tlv.h"
|
||||
#include "../emv_pki.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "sda_test.h"
|
||||
|
||||
struct emv_pk vsdc_01 = {
|
||||
.rid = { 0xa0, 0x00, 0x00, 0x00, 0x03, },
|
||||
.index = 1,
|
||||
.hash_algo = HASH_SHA_1,
|
||||
.pk_algo = PK_RSA,
|
||||
.hash = {
|
||||
0xd3, 0x4a, 0x6a, 0x77,
|
||||
0x60, 0x11, 0xc7, 0xe7,
|
||||
0xce, 0x3a, 0xec, 0x5f,
|
||||
0x03, 0xad, 0x2f, 0x8c,
|
||||
0xfc, 0x55, 0x03, 0xcc,
|
||||
},
|
||||
.exp = { 0x03, },
|
||||
.elen = 1,
|
||||
.mlen = 1024 / 8,
|
||||
.modulus = (unsigned char[]) {
|
||||
0xc6, 0x96, 0x03, 0x42, 0x13, 0xd7, 0xd8, 0x54, 0x69, 0x84, 0x57, 0x9d, 0x1d, 0x0f, 0x0e, 0xa5,
|
||||
0x19, 0xcf, 0xf8, 0xde, 0xff, 0xc4, 0x29, 0x35, 0x4c, 0xf3, 0xa8, 0x71, 0xa6, 0xf7, 0x18, 0x3f,
|
||||
0x12, 0x28, 0xda, 0x5c, 0x74, 0x70, 0xc0, 0x55, 0x38, 0x71, 0x00, 0xcb, 0x93, 0x5a, 0x71, 0x2c,
|
||||
0x4e, 0x28, 0x64, 0xdf, 0x5d, 0x64, 0xba, 0x93, 0xfe, 0x7e, 0x63, 0xe7, 0x1f, 0x25, 0xb1, 0xe5,
|
||||
0xf5, 0x29, 0x85, 0x75, 0xeb, 0xe1, 0xc6, 0x3a, 0xa6, 0x17, 0x70, 0x69, 0x17, 0x91, 0x1d, 0xc2,
|
||||
0xa7, 0x5a, 0xc2, 0x8b, 0x25, 0x1c, 0x7e, 0xf4, 0x0f, 0x23, 0x65, 0x91, 0x24, 0x90, 0xb9, 0x39,
|
||||
0xbc, 0xa2, 0x12, 0x4a, 0x30, 0xa2, 0x8f, 0x54, 0x40, 0x2c, 0x34, 0xae, 0xca, 0x33, 0x1a, 0xb6,
|
||||
0x7e, 0x1e, 0x79, 0xb2, 0x85, 0xdd, 0x57, 0x71, 0xb5, 0xd9, 0xff, 0x79, 0xea, 0x63, 0x0b, 0x75,
|
||||
},
|
||||
};
|
||||
|
||||
const unsigned char issuer_cert[] = {
|
||||
0x3c, 0x5f, 0xea, 0xd4, 0xdd, 0x7b, 0xca, 0x44, 0xf9, 0x3e, 0x90, 0xc4, 0x4f, 0x76, 0xed, 0xe5,
|
||||
0x4a, 0x32, 0x88, 0xec, 0xdc, 0x78, 0x46, 0x9f, 0xcb, 0x12, 0x25, 0xc0, 0x3b, 0x2c, 0x04, 0xf2,
|
||||
0xc2, 0xf4, 0x12, 0x28, 0x1a, 0x08, 0x22, 0xdf, 0x14, 0x64, 0x92, 0x30, 0x98, 0x9f, 0xb1, 0x49,
|
||||
0x40, 0x70, 0xda, 0xf8, 0xc9, 0x53, 0x4a, 0x78, 0x81, 0x96, 0x01, 0x48, 0x61, 0x6a, 0xce, 0x58,
|
||||
0x17, 0x88, 0x12, 0x0d, 0x35, 0x06, 0xac, 0xe4, 0xce, 0xe5, 0x64, 0xfb, 0x27, 0xee, 0x53, 0x34,
|
||||
0x1c, 0x22, 0xf0, 0xb4, 0x5b, 0x31, 0x87, 0x3d, 0x05, 0xde, 0x54, 0x5e, 0xfe, 0x33, 0xbc, 0xd2,
|
||||
0x9b, 0x21, 0x85, 0xd0, 0x35, 0xa8, 0x06, 0xad, 0x08, 0xc6, 0x97, 0x6f, 0x35, 0x05, 0xa1, 0x99,
|
||||
0x99, 0x93, 0x0c, 0xa8, 0xa0, 0x3e, 0xfa, 0x32, 0x1c, 0x48, 0x60, 0x61, 0xf7, 0xdc, 0xec, 0x9f,
|
||||
};
|
||||
|
||||
const unsigned char issuer_rem[] = {
|
||||
0x1e, 0xbc, 0xa3, 0x0f, 0x00, 0xce, 0x59, 0x62, 0xa8, 0xc6, 0xe1, 0x30, 0x54, 0x4b, 0x82, 0x89,
|
||||
0x1b, 0x23, 0x6c, 0x65, 0xde, 0x29, 0x31, 0x7f, 0x36, 0x47, 0x35, 0xde, 0xe6, 0x3f, 0x65, 0x98,
|
||||
0x97, 0x58, 0x35, 0xd5
|
||||
};
|
||||
|
||||
const unsigned char issuer_exp[] = {
|
||||
0x03,
|
||||
};
|
||||
|
||||
const unsigned char ssad_cr[] = {
|
||||
0x99, 0xa5, 0x58, 0xb6, 0x2b, 0x67, 0x4a, 0xa5, 0xe7, 0xd2, 0xa5, 0x7e, 0x5e, 0xf6, 0xa6, 0xf2,
|
||||
0x25, 0x8e, 0x5d, 0xa0, 0x52, 0xd0, 0x5b, 0x54, 0xe5, 0xc1, 0x15, 0xff, 0x1c, 0xec, 0xf9, 0x4a,
|
||||
0xa2, 0xdf, 0x8f, 0x39, 0xa0, 0x1d, 0x71, 0xc6, 0x19, 0xeb, 0x81, 0x9d, 0xa5, 0x2e, 0xf3, 0x81,
|
||||
0xe8, 0x49, 0x79, 0x58, 0x6a, 0xea, 0x78, 0x55, 0xff, 0xbe, 0xf4, 0x0a, 0xa3, 0xa7, 0x1c, 0xd3,
|
||||
0xb0, 0x4c, 0xfd, 0xf2, 0x70, 0xae, 0xc8, 0x15, 0x8a, 0x27, 0x97, 0xf2, 0x4f, 0xd6, 0x13, 0xb7,
|
||||
0x48, 0x13, 0x46, 0x61, 0x13, 0x5c, 0xd2, 0x90, 0xe4, 0x5b, 0x04, 0xa8, 0xe0, 0xcc, 0xc7, 0x11,
|
||||
0xae, 0x04, 0x2f, 0x15, 0x9e, 0x73, 0xc8, 0x9c, 0x2a, 0x7e, 0x65, 0xa4, 0xc2, 0xfd, 0x1d, 0x61,
|
||||
0x06, 0x02, 0x4a, 0xa2, 0x71, 0x30, 0xb0, 0xec, 0xec, 0x02, 0x38, 0xf9, 0x16, 0x59, 0xde, 0x96,
|
||||
};
|
||||
|
||||
const unsigned char ssd1[] = {
|
||||
0x5f, 0x24, 0x03, 0x08, 0x12, 0x31, 0x5a, 0x08, 0x42, 0x76, 0x55, 0x00, 0x13, 0x23, 0x45, 0x99, 0x5f, 0x34, 0x01, 0x01, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05, 0xd0, 0x40, 0xac, 0xa8, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05, 0xd0, 0x68, 0xbc, 0xf8, 0x00,
|
||||
0x5c, 0x00,
|
||||
};
|
||||
static const struct tlv ssd1_tlv = {
|
||||
.len = sizeof(ssd1),
|
||||
.value = ssd1,
|
||||
};
|
||||
|
||||
const unsigned char pan[] = {
|
||||
0x42, 0x76, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static int sda_test_raw(bool verbose) {
|
||||
const struct emv_pk *pk = &vsdc_01;
|
||||
|
||||
struct crypto_pk *kcp = crypto_pk_open(PK_RSA,
|
||||
pk->modulus, pk->mlen,
|
||||
pk->exp, pk->elen);
|
||||
if (!kcp)
|
||||
return 1;
|
||||
|
||||
unsigned char *ipk_data;
|
||||
size_t ipk_data_len;
|
||||
ipk_data = crypto_pk_encrypt(kcp, issuer_cert, sizeof(issuer_cert), &ipk_data_len);
|
||||
crypto_pk_close(kcp);
|
||||
|
||||
if (!ipk_data)
|
||||
return 1;
|
||||
|
||||
if (verbose) {
|
||||
printf("issuer cert:\n");
|
||||
dump_buffer(ipk_data, ipk_data_len, stdout, 0);
|
||||
}
|
||||
|
||||
size_t ipk_pk_len = ipk_data[13];
|
||||
unsigned char *ipk_pk = malloc(ipk_pk_len);
|
||||
memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36);
|
||||
memcpy(ipk_pk + ipk_data_len - 36, issuer_rem, sizeof(issuer_rem));
|
||||
|
||||
struct crypto_hash *ch;
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, ipk_data + 1, 14);
|
||||
crypto_hash_write(ch, ipk_pk, ipk_pk_len);
|
||||
crypto_hash_write(ch, issuer_exp, sizeof(issuer_exp));
|
||||
|
||||
unsigned char *h = crypto_hash_read(ch);
|
||||
if (!h) {
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash:\n");
|
||||
dump_buffer(h, 20, stdout, 0);
|
||||
}
|
||||
|
||||
if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) {
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_pk);
|
||||
free(ipk_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
free(ipk_data);
|
||||
|
||||
struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len,
|
||||
issuer_exp, (int) sizeof(issuer_exp));
|
||||
free(ipk_pk);
|
||||
if (!ikcp)
|
||||
return 1;
|
||||
|
||||
size_t ssad_len;
|
||||
unsigned char *ssad = crypto_pk_encrypt(ikcp, ssad_cr, sizeof(ssad_cr), &ssad_len);
|
||||
crypto_pk_close(ikcp);
|
||||
if (!ssad)
|
||||
return 1;
|
||||
|
||||
if (verbose) {
|
||||
printf("ssad:\n");
|
||||
dump_buffer(ssad, ssad_len, stdout, 0);
|
||||
}
|
||||
|
||||
ch = crypto_hash_open(HASH_SHA_1);
|
||||
if (!ch) {
|
||||
free(ssad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
crypto_hash_write(ch, ssad + 1, ssad_len - 22);
|
||||
crypto_hash_write(ch, ssd1, sizeof(ssd1));
|
||||
|
||||
unsigned char *h2 = crypto_hash_read(ch);
|
||||
if (!h2) {
|
||||
crypto_hash_close(ch);
|
||||
free(ssad);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("crypto hash2:\n");
|
||||
dump_buffer(h2, 20, stdout, 0);
|
||||
}
|
||||
|
||||
crypto_hash_close(ch);
|
||||
|
||||
free(ssad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sda_test_pk(bool verbose) {
|
||||
const struct emv_pk *pk = &vsdc_01;
|
||||
struct tlvdb *db;
|
||||
|
||||
db = tlvdb_external(0x90, sizeof(issuer_cert), issuer_cert);
|
||||
tlvdb_add(db, tlvdb_external(0x9f32, sizeof(issuer_exp), issuer_exp));
|
||||
tlvdb_add(db, tlvdb_external(0x92, sizeof(issuer_rem), issuer_rem));
|
||||
tlvdb_add(db, tlvdb_external(0x5a, sizeof(pan), pan));
|
||||
|
||||
struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db);
|
||||
if (!ipk) {
|
||||
fprintf(stderr, "Could not recover Issuer certificate!\n");
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
tlvdb_add(db, tlvdb_external(0x93, sizeof(ssad_cr), ssad_cr));
|
||||
|
||||
struct tlvdb *dacdb = emv_pki_recover_dac(ipk, db, &ssd1_tlv);
|
||||
if (!dacdb) {
|
||||
fprintf(stderr, "Could not recover DAC!\n");
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const struct tlv *dac = tlvdb_get(dacdb, 0x9f45, NULL);
|
||||
if (!dac) {
|
||||
fprintf(stderr, "DAC not found!\n");
|
||||
tlvdb_free(dacdb);
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("dac:\n");
|
||||
dump_buffer(dac->value, dac->len, stdout, 0);
|
||||
}
|
||||
|
||||
tlvdb_free(dacdb);
|
||||
emv_pk_free(ipk);
|
||||
tlvdb_free(db);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_sda_test(bool verbose) {
|
||||
int ret;
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
ret = sda_test_raw(verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "SDA raw test: failed\n");
|
||||
return ret;
|
||||
}
|
||||
fprintf(stdout, "SDA raw test: passed\n");
|
||||
|
||||
ret = sda_test_pk(verbose);
|
||||
if (ret) {
|
||||
fprintf(stderr, "SDA test pk: failed\n");
|
||||
return ret;
|
||||
}
|
||||
fprintf(stdout, "SDA test pk: passed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
19
client/src/emv/test/sda_test.h
Normal file
19
client/src/emv/test/sda_test.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* emv-tools - a set of tools 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.
|
||||
*/
|
||||
|
||||
#ifndef __SDA_TEST_H
|
||||
#define __SDA_TEST_H
|
||||
int exec_sda_test(bool verbose);
|
||||
#endif
|
||||
575
client/src/emv/tlv.c
Normal file
575
client/src/emv/tlv.c
Normal file
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "tlv.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 (ll > 5)
|
||||
return TLV_LEN_INVALID;
|
||||
|
||||
l = 0;
|
||||
for (int i = 1; i <= ll; i++) {
|
||||
l = (l << 8) + **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);
|
||||
}
|
||||
}
|
||||
|
||||
struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag) {
|
||||
if (!tlvdb)
|
||||
return NULL;
|
||||
|
||||
return tlvdb_find(tlvdb->next, tag);
|
||||
}
|
||||
|
||||
struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag) {
|
||||
if (!tlvdb)
|
||||
return NULL;
|
||||
|
||||
for (; tlvdb; tlvdb = tlvdb->next) {
|
||||
if (tlvdb->tag.tag == tag)
|
||||
return tlvdb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag) {
|
||||
if (!tlvdb)
|
||||
return NULL;
|
||||
|
||||
for (; tlvdb; tlvdb = tlvdb->next) {
|
||||
if (tlvdb->tag.tag == tag)
|
||||
return tlvdb;
|
||||
|
||||
if (tlvdb->children) {
|
||||
struct tlvdb *ch = tlvdb_find_full(tlvdb->children, tag);
|
||||
if (ch)
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) {
|
||||
int i = 0;
|
||||
struct tlvdb *tnext = tlvdb;
|
||||
|
||||
while (tnext && tag[i]) {
|
||||
tnext = tlvdb_find(tnext, tag[i]);
|
||||
i++;
|
||||
if (tag[i] && tnext) {
|
||||
tnext = tnext->children;
|
||||
}
|
||||
}
|
||||
|
||||
return tnext;
|
||||
}
|
||||
|
||||
void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) {
|
||||
if (tlvdb == other)
|
||||
return;
|
||||
|
||||
while (tlvdb->next) {
|
||||
if (tlvdb->next == other)
|
||||
return;
|
||||
|
||||
tlvdb = tlvdb->next;
|
||||
}
|
||||
|
||||
tlvdb->next = other;
|
||||
}
|
||||
|
||||
void tlvdb_change_or_add_node_ex(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value, struct tlvdb **tlvdb_elm) {
|
||||
struct tlvdb *telm = tlvdb_find_full(tlvdb, tag);
|
||||
if (telm == NULL) {
|
||||
// new tlv element
|
||||
struct tlvdb *elm = tlvdb_fixed(tag, len, value);
|
||||
tlvdb_add(tlvdb, elm);
|
||||
if (tlvdb_elm)
|
||||
*tlvdb_elm = elm;
|
||||
} else {
|
||||
// the same tlv structure
|
||||
if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len))
|
||||
return;
|
||||
|
||||
// replace tlv element
|
||||
struct tlvdb *tnewelm = tlvdb_fixed(tag, len, value);
|
||||
bool tnewelm_linked = false;
|
||||
tnewelm->next = telm->next;
|
||||
tnewelm->parent = telm->parent;
|
||||
|
||||
// if telm stayed first in children chain
|
||||
if (telm->parent && telm->parent->children == telm) {
|
||||
telm->parent->children = tnewelm;
|
||||
tnewelm_linked = true;
|
||||
}
|
||||
|
||||
// if telm have previous element
|
||||
if (telm != tlvdb) {
|
||||
// elm in root
|
||||
struct tlvdb *celm = tlvdb;
|
||||
// elm in child list of node
|
||||
if (telm->parent && telm->parent->children)
|
||||
celm = telm->parent->children;
|
||||
|
||||
// find previous element
|
||||
for (; celm; celm = celm->next) {
|
||||
if (celm->next == telm) {
|
||||
celm->next = tnewelm;
|
||||
tnewelm_linked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free old element with childrens
|
||||
telm->next = NULL;
|
||||
tlvdb_free(telm);
|
||||
|
||||
if (tlvdb_elm) {
|
||||
*tlvdb_elm = tnewelm;
|
||||
tnewelm_linked = true;
|
||||
}
|
||||
if (! tnewelm_linked) {
|
||||
tlvdb_free(tnewelm);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value) {
|
||||
tlvdb_change_or_add_node_ex(tlvdb, tag, len, value, NULL);
|
||||
}
|
||||
|
||||
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) {
|
||||
struct tlvdb *next = NULL;
|
||||
|
||||
if (!tlvdb)
|
||||
return;
|
||||
|
||||
for (; tlvdb; tlvdb = next) {
|
||||
next = tlvdb->next;
|
||||
cb(data, &tlvdb->tag, level, (tlvdb->children == NULL));
|
||||
tlvdb_visit(tlvdb->children, cb, data, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev) {
|
||||
tlvdb = tlvdb->children;
|
||||
return tlvdb_get(tlvdb, tag, prev);
|
||||
}
|
||||
|
||||
const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb) {
|
||||
if (tlvdb)
|
||||
return &tlvdb->tag;
|
||||
else
|
||||
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) == 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);
|
||||
}
|
||||
|
||||
struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb) {
|
||||
return tlvdb->next;
|
||||
}
|
||||
|
||||
struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb) {
|
||||
return tlvdb->children;
|
||||
}
|
||||
|
||||
struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb) {
|
||||
return tlvdb->parent;
|
||||
}
|
||||
|
||||
bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value) {
|
||||
const struct tlv *tlvelm = tlvdb_get(tlvRoot, tag, NULL);
|
||||
return tlv_get_uint8(tlvelm, value);
|
||||
}
|
||||
|
||||
bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value) {
|
||||
*value = 0;
|
||||
if (etlv) {
|
||||
if (etlv->len == 0)
|
||||
return true;
|
||||
|
||||
if (etlv->len == 1) {
|
||||
*value = etlv->value[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tlv_get_int(const struct tlv *etlv, int *value) {
|
||||
*value = 0;
|
||||
if (etlv) {
|
||||
if (etlv->len == 0)
|
||||
return true;
|
||||
|
||||
if (etlv->len <= 4) {
|
||||
for (int i = 0; i < etlv->len; i++) {
|
||||
*value += etlv->value[i] * (1 << (i * 8));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
68
client/src/emv/tlv.h
Normal file
68
client/src/emv/tlv.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
|
||||
typedef uint32_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, int level, bool is_leaf);
|
||||
|
||||
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);
|
||||
|
||||
struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb);
|
||||
struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb);
|
||||
struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb);
|
||||
|
||||
struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag); // search also in childrens
|
||||
struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag);
|
||||
struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag);
|
||||
struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]);
|
||||
|
||||
void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other);
|
||||
void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value);
|
||||
void tlvdb_change_or_add_node_ex(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value, struct tlvdb **tlvdb_elm);
|
||||
|
||||
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level);
|
||||
const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
|
||||
const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
|
||||
const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb);
|
||||
|
||||
bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv);
|
||||
unsigned char *tlv_encode(const struct tlv *tlv, size_t *len);
|
||||
bool tlv_is_constructed(const struct tlv *tlv);
|
||||
bool tlv_equal(const struct tlv *a, const struct tlv *b);
|
||||
|
||||
bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value);
|
||||
bool tlv_get_int(const struct tlv *etlv, int *value);
|
||||
|
||||
bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user