mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-07-05 20:41:34 -07:00
appears to work - using normal mifare sim init
working demo works seems to work so far more cleanup and works working copy working, clean one more pass cleanup continues back in buisness babyyy final cleanup before PR I hope
This commit is contained in:
parent
32f06db2e8
commit
3eb0238481
7 changed files with 367 additions and 603 deletions
737
armsrc/emvsim.c
737
armsrc/emvsim.c
|
@ -27,19 +27,13 @@
|
|||
// /!\ Printing Debug message is disrupting emulation,
|
||||
// Only use with caution during debugging
|
||||
|
||||
// These are the old flags which have changed in master since this fork was created.
|
||||
// Just a temp fix and not intended to go into master
|
||||
#define FLAG_4B_UID_IN_DATA_OLD 0x02
|
||||
#define FLAG_7B_UID_IN_DATA_OLD 0x04
|
||||
#define FLAG_10B_UID_IN_DATA_OLD 0x08
|
||||
#define FLAG_UID_IN_EMUL_OLD 0x10
|
||||
#define FLAG_MF_MINI_OLD 0x80
|
||||
#define FLAG_MF_1K_OLD 0x100
|
||||
#define FLAG_MF_2K_OLD 0x200
|
||||
#define FLAG_MF_4K_OLD 0x400
|
||||
#define FLAG_FORCED_ATQA 0x800
|
||||
#define FLAG_FORCED_SAK 0x1000
|
||||
#define FLAG_CVE21_0430_OLD 0x2000
|
||||
// indices into responses array copied from mifare sim init:
|
||||
#define ATQA 0
|
||||
#define SAK 1
|
||||
#define SAKuid 2
|
||||
#define UIDBCC1 3
|
||||
#define UIDBCC2 8
|
||||
#define UIDBCC3 13
|
||||
|
||||
#include "emvsim.h"
|
||||
#include <inttypes.h>
|
||||
|
@ -48,6 +42,7 @@
|
|||
#include "BigBuf.h"
|
||||
#include "string.h"
|
||||
#include "mifareutil.h"
|
||||
#include "mifaresim.h"
|
||||
#include "fpgaloader.h"
|
||||
#include "proxmark3_arm.h"
|
||||
#include "protocols.h"
|
||||
|
@ -57,322 +52,266 @@
|
|||
#include "ticks.h"
|
||||
#include "i2c_direct.h"
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
|
||||
// Hardcoded response to the reader for file not found, plus the checksum
|
||||
static uint8_t filenotfound[] = {0x02, 0x6a, 0x82, 0x93, 0x2f};
|
||||
|
||||
// query and response that inserts PDOL so as to continue process...
|
||||
static uint8_t fci_query[] = {0x02, 0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00, 0x56, 0x3f};
|
||||
static uint8_t fci_template[] = {0x02, 0x6f, 0x5e, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x53, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x38, 0x18, 0x9f, 0x66, 0x04, 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x11, 0x01, 0x01, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0xbf, 0x0c, 0x13, 0x9f, 0x5a, 0x05, 0x31, 0x08, 0x26, 0x08, 0x26, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xd8, 0x15};
|
||||
// TLV response for PPSE directory request
|
||||
static uint8_t pay1_response[] = { 0x6F, 0x1E, 0x84, 0x0E };
|
||||
|
||||
static uint8_t pay1_response[] = { 0x6F, 0x1E, 0x84, 0x0E, 0x31, 0x50, 0x41, 0x59 };
|
||||
// The WTX we want to send out... The format:
|
||||
// 0xf2 is the command
|
||||
// 0x0e is the time to wait (currently at max)
|
||||
// The remaining bytes are CRC, precalculated for speed
|
||||
static uint8_t extend_resp[] = {0xf2, 0x0e, 0x66, 0xb8};
|
||||
|
||||
|
||||
// For reference, we have here the pay1 template we receive from the card, and the pay2 template we send back to the reader
|
||||
// These can be inspected at https://emvlab.org/tlvutils/
|
||||
// Note that the pay2 template is coded for visa ps in the UK - other countries may have different templates. Refer:
|
||||
// https://mstcompany.net/blog/acquiring-emv-transaction-flow-part-3-get-processing-options-with-and-without-pdol
|
||||
// Specifically, 9F5A: Application Program Identifier: 3108260826 might have to become 31 0840 0840 for USA for example.
|
||||
// todo: see if this can be read from the card and automatically populated rather than hard coded
|
||||
//static uint8_t fci_template_pay1[] = {0xff, 0x6f, 0x3b, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x30, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x11, 0x01, 0x01, 0xbf, 0x0c, 0x0b, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x17, 0x48};
|
||||
static uint8_t fci_template_pay2[] = {0x02, 0x6f, 0x5e, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x53, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x38, 0x18, 0x9f, 0x66, 0x04, 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x11, 0x01, 0x01, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0xbf, 0x0c, 0x13, 0x9f, 0x5a, 0x05, 0x31, 0x08, 0x26, 0x08, 0x26, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xd8, 0x15};
|
||||
|
||||
// This is the hardcoded response that a contactless card would respond with when asked to select PPSE.
|
||||
// It is a TLV structure, and can be seen here:
|
||||
// https://emvlab.org/tlvutils/?data=6f3e840e325041592e5359532e4444463031a52cbf0c2961274f07a0000000031010500a566973612044656269749f0a080001050100000000bf6304df200180
|
||||
// The first byte is the class byte, and the payload is followed by 0x9000, which is the success code, and the CRC (precalculated)
|
||||
static uint8_t pay2_response[] = { 0x03, 0x6f, 0x3e, 0x84, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0xa5, 0x2c, 0xbf, 0x0c, 0x29, 0x61, 0x27, 0x4f, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x63, 0x04, 0xdf, 0x20, 0x01, 0x80, 0x90, 0x00, 0x07, 0x9d};
|
||||
|
||||
static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) {
|
||||
void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *receivedCmd_copy, uint16_t receivedCmd_len_copy);
|
||||
|
||||
// SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf
|
||||
// ATQA
|
||||
static uint8_t rATQA_Mini[] = {0x04, 0x00}; // indicate Mifare classic Mini 4Byte UID
|
||||
static uint8_t rATQA_1k[] = {0x04, 0x00}; // indicate Mifare classic 1k 4Byte UID
|
||||
static uint8_t rATQA_2k[] = {0x04, 0x00}; // indicate Mifare classic 2k 4Byte UID
|
||||
static uint8_t rATQA_4k[] = {0x02, 0x00}; // indicate Mifare classic 4k 4Byte UID
|
||||
typedef enum {
|
||||
STATE_DEFAULT,
|
||||
SELECT_PAY1,
|
||||
SELECT_PAY1_AID,
|
||||
REQUESTING_CARD_PDOL,
|
||||
GENERATE_AC,
|
||||
} SystemState;
|
||||
|
||||
// SAK
|
||||
static uint8_t rSAK_Mini = 0x09; // mifare Mini
|
||||
static uint8_t rSAK_1k = 0x08; // mifare 1k
|
||||
static uint8_t rSAK_2k = 0x08; // mifare 2k with RATS support
|
||||
static uint8_t rSAK_4k = 0x18; // mifare 4k
|
||||
static SystemState currentState = STATE_DEFAULT;
|
||||
|
||||
static uint8_t rUIDBCC1[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level
|
||||
static uint8_t rUIDBCC1b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level, last 4 bytes
|
||||
static uint8_t rUIDBCC1b3[] = {0x00, 0x00, 0x00}; // UID 1st cascade level, last 3 bytes
|
||||
static uint8_t rUIDBCC1b2[] = {0x00, 0x00}; // UID 1st cascade level, last 2 bytes
|
||||
static uint8_t rUIDBCC1b1[] = {0x00}; // UID 1st cascade level, last byte
|
||||
static uint8_t rUIDBCC2[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 2nd cascade level
|
||||
static uint8_t rUIDBCC2b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 2st cascade level, last 4 bytes
|
||||
static uint8_t rUIDBCC2b3[] = {0x00, 0x00, 0x00}; // UID 2st cascade level, last 3 bytes
|
||||
static uint8_t rUIDBCC2b2[] = {0x00, 0x00}; // UID 2st cascade level, last 2 bytes
|
||||
static uint8_t rUIDBCC2b1[] = {0x00}; // UID 2st cascade level, last byte
|
||||
static uint8_t rUIDBCC3[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 3nd cascade level
|
||||
static uint8_t rUIDBCC3b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 3st cascade level, last 4 bytes
|
||||
static uint8_t rUIDBCC3b3[] = {0x00, 0x00, 0x00}; // UID 3st cascade level, last 3 bytes
|
||||
static uint8_t rUIDBCC3b2[] = {0x00, 0x00}; // UID 3st cascade level, last 2 bytes
|
||||
static uint8_t rUIDBCC3b1[] = {0x00}; // UID 3st cascade level, last byte
|
||||
// This is the main entry point for the EMV attack, everything before this has just been setup/handshaking.
|
||||
// In order to meet the timing requirements, as soon as the proxmark sees a command it immediately
|
||||
// caches the command to process and responds with a WTX
|
||||
// (waiting time extension). When it get the response to this WTX, it can process the cached command through the I2C interface.
|
||||
//
|
||||
// The full flow is:
|
||||
// 1. Handshake with RATS
|
||||
// 2. Reader attempts to find out which payment environment the proxmark supports (may start with SELECT OSE for example)
|
||||
// 3. Reader eventually makes a request for the PAY2 application (select PPSE) (contactless payment)
|
||||
// 4. We read the PAY1 environment and transform it into PAY2 to respond
|
||||
// 5. Reader will select AID we responded in step 4
|
||||
// 6. We get the response from selecting the PAY1 AID and transform it into PAY2 response (fci template)
|
||||
// - This is important as it contains the PDOL (processing data object list) which specifies the data which is
|
||||
// signed by the card and sent to the reader to verify the transaction.
|
||||
// 7. The reader will then issue 'get processing options' which seems to be used here to provide the fields to be signed
|
||||
// as specified by the PDOL.
|
||||
// 8. In contactless flow, GPO should at least return the Application Interchange Profile (AIP) and
|
||||
// Application File Locator (AFL). However, here we return track 2 data, the cryptogram, everything. This completes the transaction.
|
||||
// 9. To construct this final response, behind the scenes we need to interact with the card to make it think its completing a contact transaction:
|
||||
// - Request PDOL to prime the card (response not used)
|
||||
// - Rearrange the GPO data provided into a 'generate AC' command for the card
|
||||
// - Extract the cryptogram, track 2 data and anything else required
|
||||
// - Respond. Transaction is complete
|
||||
void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *receivedCmd_copy, uint16_t receivedCmd_len_copy) {
|
||||
uint8_t responseToReader[MAX_FRAME_SIZE] = {0x00};
|
||||
uint16_t responseToReader_len;
|
||||
|
||||
static uint8_t rATQA[] = {0x00, 0x00}; // Current ATQA
|
||||
static uint8_t rSAK[] = {0x00, 0x00, 0x00}; // Current SAK, CRC
|
||||
static uint8_t rSAKuid[] = {0x04, 0xda, 0x17}; // UID incomplete cascade bit, CRC
|
||||
// special print me
|
||||
Dbprintf("\nrecvd from reader:");
|
||||
Dbhexdump(receivedCmd_len, receivedCmd, false);
|
||||
Dbprintf("");
|
||||
|
||||
// RATS answer for 2K NXP mifare classic (with CRC)
|
||||
static uint8_t rRATS[] = {0x0c, 0x75, 0x77, 0x80, 0x02, 0xc1, 0x05, 0x2f, 0x2f, 0x01, 0xbc, 0xd6, 0x60, 0xd3};
|
||||
// use annotate to give some hints about the command
|
||||
annotate(&receivedCmd[1], receivedCmd_len-1);
|
||||
//Dbprintf("Annotated: %s", explanation);
|
||||
|
||||
*uid_len = 0;
|
||||
// This is a common request from the reader which we can just immediately respond to since we know we can't
|
||||
// handle it.
|
||||
if (receivedCmd[6] == 'O' && receivedCmd[7] == 'S' && receivedCmd[8] == 'E') {
|
||||
Dbprintf("We saw OSE... ignore it!");
|
||||
EmSendCmd(filenotfound, sizeof(filenotfound));
|
||||
return;
|
||||
}
|
||||
|
||||
// By default use 1K tag
|
||||
memcpy(rATQA, rATQA_1k, sizeof(rATQA));
|
||||
rSAK[0] = rSAK_1k;
|
||||
// We want to modify corrupted request
|
||||
if ((receivedCmd_len > 5 && receivedCmd[0] != 0x03 && receivedCmd[0] != 0x02 && receivedCmd[1] == 0 && receivedCmd[4] == 0) || (receivedCmd[2] == 0xa8)) {
|
||||
//if (receivedCmd[2] == 0xa8) {
|
||||
Dbprintf("We saw signing request... modifying it into a generate ac transaction !!!!");
|
||||
|
||||
//by default RATS not supported
|
||||
*rats_len = 0;
|
||||
*rats = NULL;
|
||||
currentState = GENERATE_AC;
|
||||
|
||||
// -- Determine the UID
|
||||
// Can be set from emulator memory or incoming data
|
||||
// Length: 4,7,or 10 bytes
|
||||
memcpy(receivedCmd, (unsigned char[]){ 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6);
|
||||
|
||||
// Get UID, SAK, ATQA from EMUL
|
||||
if ((flags & FLAG_UID_IN_EMUL_OLD) == FLAG_UID_IN_EMUL_OLD) {
|
||||
uint8_t block0[16];
|
||||
emlGet(block0, 0, 16);
|
||||
for (int i = 0; i < 29; i++) {
|
||||
receivedCmd[6 + i] = receivedCmd[12 + i];
|
||||
}
|
||||
|
||||
// If uid size defined, copy only uid from EMUL to use, backward compatibility for 'hf_colin.c', 'hf_mattyrun.c'
|
||||
if ((flags & (FLAG_4B_UID_IN_DATA_OLD | FLAG_7B_UID_IN_DATA_OLD | FLAG_10B_UID_IN_DATA_OLD)) != 0) {
|
||||
memcpy(datain, block0, 10); // load 10bytes from EMUL to the datain pointer. to be used below.
|
||||
} else {
|
||||
// Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA
|
||||
if ((block0[0] ^ block0[1] ^ block0[2] ^ block0[3]) == block0[4] && (block0[6] & 0xc0) == 0) {
|
||||
flags |= FLAG_4B_UID_IN_DATA_OLD;
|
||||
memcpy(datain, block0, 4);
|
||||
rSAK[0] = block0[5];
|
||||
memcpy(rATQA, &block0[6], sizeof(rATQA));
|
||||
// clear final byte just in case
|
||||
receivedCmd[35] = 0;
|
||||
|
||||
receivedCmd_len = 35 + 3; // Core command is 35, then there is control code and the crc
|
||||
|
||||
Dbprintf("\nthe command has now become:");
|
||||
Dbhexdump(receivedCmd_len, receivedCmd, false);
|
||||
}
|
||||
|
||||
// Seems unlikely
|
||||
if (receivedCmd_len >= 9 && receivedCmd[6] == '1' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') {
|
||||
Dbprintf("We saw 1PA... !!!!");
|
||||
}
|
||||
|
||||
// Request more time for 2PAY and respond with a modified 1PAY request. We literally just change the 2 to a 1.
|
||||
if (receivedCmd_len >= 9 && receivedCmd[6] == '2' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') {
|
||||
Dbprintf("We saw 2PA... switching it to 1PAY !!!!");
|
||||
receivedCmd[6] = '1';
|
||||
currentState = SELECT_PAY1;
|
||||
}
|
||||
|
||||
// We are selecting a short AID - assume it is pay2 aid
|
||||
if (receivedCmd[2] == 0xA4 && receivedCmd[5] == 0x07) {
|
||||
Dbprintf("Selecting pay2 AID");
|
||||
currentState = SELECT_PAY1_AID;
|
||||
}
|
||||
|
||||
static uint8_t rnd_resp[] = {0xb2, 0x67, 0xc7};
|
||||
if (memcmp(receivedCmd, rnd_resp, sizeof(rnd_resp)) == 0) {
|
||||
Dbprintf("We saw bad response... !");
|
||||
return;
|
||||
}
|
||||
|
||||
// We have received the response from a WTX command! Process the cached command at this point.
|
||||
if (memcmp(receivedCmd, extend_resp, sizeof(extend_resp)) == 0) {
|
||||
// Special case: if we are about to do a generate AC, we also need to
|
||||
// make a request for pdol first (and discard response)...
|
||||
if (receivedCmd_copy[1] == 0x80 && receivedCmd_copy[2] == 0xae) {
|
||||
Dbprintf("We are about to do a generate AC... we need to request PDOL first...");
|
||||
uint8_t pdol_request[] = { 0x80, 0xa8, 0x00, 0x00, 0x02, 0x83, 0x00 };
|
||||
|
||||
currentState = REQUESTING_CARD_PDOL;
|
||||
CmdSmartRaw(0xff, &(pdol_request[0]), sizeof(pdol_request), (&responseToReader[0]), &responseToReader_len);
|
||||
}
|
||||
|
||||
// Send the cached command to the card via ISO7816
|
||||
// This is minus 3 because we don't include the first byte (prepend), plus we don't want to send the
|
||||
// last two bytes (CRC) to the card.
|
||||
// On the return, the first class byte must be the same, so it's preserved in responseToReader
|
||||
CmdSmartRaw(receivedCmd_copy[0], &(receivedCmd_copy[1]), receivedCmd_len_copy - 3, (&responseToReader[0]), &responseToReader_len);
|
||||
|
||||
// Print the unadultered response we got from the card here
|
||||
Dbprintf("The response from the card is ==> :");
|
||||
Dbhexdump(responseToReader_len, responseToReader, false);
|
||||
|
||||
// We have passed the reader's query to the card, but before we return it, we need to check if we need to modify
|
||||
// the response to 'pretend' to be a PAY2 environment.
|
||||
// This is always the same response for VISA, the only currently supported card
|
||||
if (currentState == SELECT_PAY1) {
|
||||
Dbprintf("We saw a PAY1 response... modifying it to a PAY2 response !!!!");
|
||||
|
||||
if (!memcmp(&responseToReader[1], &pay1_response[0], sizeof(pay1_response)) == 0) {
|
||||
Dbprintf("The response from the card is not a PAY1 response. This is unexpected and probably fatal.");
|
||||
}
|
||||
// Check for 7 bytes UID: double size uid bits in ATQA
|
||||
else if ((block0[8] & 0xc0) == 0x40) {
|
||||
flags |= FLAG_7B_UID_IN_DATA_OLD;
|
||||
memcpy(datain, block0, 7);
|
||||
rSAK[0] = block0[7];
|
||||
memcpy(rATQA, &block0[8], sizeof(rATQA));
|
||||
} else {
|
||||
Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found"));
|
||||
return false;
|
||||
|
||||
if (pay2_response[0] != responseToReader[0]) {
|
||||
Dbprintf("The first byte of the PAY2 response is different from the request. This is unexpected and probably fatal.");
|
||||
}
|
||||
|
||||
memcpy(responseToReader, &pay2_response[0], sizeof(pay2_response));
|
||||
responseToReader_len = sizeof(pay2_response);
|
||||
}
|
||||
|
||||
}
|
||||
if (responseToReader[0] != 0xff && responseToReader[1] == 0x77 && true) {
|
||||
Dbprintf("we have detected a generate ac response, lets repackage it!!");
|
||||
Dbhexdump(responseToReader_len, responseToReader, false); // special print
|
||||
|
||||
// Tune tag type, if defined directly
|
||||
// Otherwise use defined by default or extracted from EMUL
|
||||
if ((flags & FLAG_MF_MINI_OLD) == FLAG_MF_MINI_OLD) {
|
||||
memcpy(rATQA, rATQA_Mini, sizeof(rATQA));
|
||||
rSAK[0] = rSAK_Mini;
|
||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare Mini ATQA/SAK");
|
||||
} else if ((flags & FLAG_MF_1K_OLD) == FLAG_MF_1K_OLD) {
|
||||
memcpy(rATQA, rATQA_1k, sizeof(rATQA));
|
||||
rSAK[0] = rSAK_1k;
|
||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 1K ATQA/SAK (!!!!)");
|
||||
} else if ((flags & FLAG_MF_2K_OLD) == FLAG_MF_2K_OLD) {
|
||||
memcpy(rATQA, rATQA_2k, sizeof(rATQA));
|
||||
rSAK[0] = rSAK_2k;
|
||||
*rats = rRATS;
|
||||
*rats_len = sizeof(rRATS);
|
||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 2K ATQA/SAK with RATS support");
|
||||
} else if ((flags & FLAG_MF_4K_OLD) == FLAG_MF_4K_OLD) {
|
||||
memcpy(rATQA, rATQA_4k, sizeof(rATQA));
|
||||
rSAK[0] = rSAK_4k;
|
||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 4K ATQA/SAK");
|
||||
}
|
||||
// 11 and 12 are trans counter.
|
||||
// 16 to 24 are the cryptogram
|
||||
// 27 to 34 is issuer application data
|
||||
Dbprintf("atc: %d %d, cryptogram: %d ", responseToReader[11], responseToReader[12], responseToReader[13]);
|
||||
|
||||
// Prepare UID arrays
|
||||
if ((flags & FLAG_4B_UID_IN_DATA_OLD) == FLAG_4B_UID_IN_DATA_OLD) { // get UID from datain
|
||||
memcpy(rUIDBCC1, datain, 4);
|
||||
*uid_len = 4;
|
||||
if (g_dbglevel >= DBG_EXTENDED)
|
||||
Dbprintf("MifareSimInit - FLAG_4B_UID_IN_DATA_OLD => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_4B_UID_IN_DATA_OLD, flags, rUIDBCC1);
|
||||
// then, on the template:
|
||||
// 60 and 61 for counter
|
||||
// 45 to 53 for cryptogram
|
||||
// 35 to 42 for issuer application data
|
||||
uint8_t template[] = { 0x00, 0x77, 0x47, 0x82, 0x02, 0x39, 0x00, 0x57, 0x13, 0x47,
|
||||
0x62, 0x28, 0x00, 0x05, 0x93, 0x38, 0x64, 0xd2, 0x70, 0x92,
|
||||
0x01, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x0f, 0x5f, 0x34,
|
||||
0x01, 0x00, 0x9f, 0x10, 0x07, 0x06, 0x01, 0x12, 0x03, 0xa0,
|
||||
0x20, 0x00, 0x9f, 0x26, 0x08, 0x56, 0xcb, 0x4e, 0xe1, 0xa4,
|
||||
0xef, 0xac, 0x74, 0x9f, 0x27, 0x01, 0x80, 0x9f, 0x36, 0x02,
|
||||
0x00, 0x07, 0x9f, 0x6c, 0x02, 0x3e, 0x00, 0x9f, 0x6e, 0x04,
|
||||
0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff};
|
||||
|
||||
// save CUID
|
||||
*cuid = bytes_to_num(rUIDBCC1, 4);
|
||||
// BCC
|
||||
rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
|
||||
if (g_dbglevel > DBG_NONE) {
|
||||
Dbprintf("4B UID: %02x%02x%02x%02x", rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3]);
|
||||
// do the replacement
|
||||
template[0] = responseToReader[0]; // class bit 0
|
||||
|
||||
template[60] = responseToReader[10];
|
||||
template[61] = responseToReader[11];
|
||||
|
||||
// Copy responseToReader[15..23] to template[45..53]
|
||||
for (int i = 0; i <= 8; i++) {
|
||||
template[45 + i] = responseToReader[15 + i];
|
||||
}
|
||||
|
||||
// Copy responseToReader[26..32] to template[35..41]
|
||||
for (int i = 0; i <= 6; i++) {
|
||||
template[35 + i] = responseToReader[26 + i];
|
||||
}
|
||||
|
||||
Dbprintf("\nrearranged is: ");
|
||||
responseToReader_len = sizeof(template);
|
||||
|
||||
// We DO NOT add the CRC here, this way we can avoid a million penny payments!
|
||||
// The CRC is calculated here, but doesn't include the class bit at the beginning, plus
|
||||
// also obvisously doesn't include the CRC bytes itself.
|
||||
AddCrc14A(&template[0], responseToReader_len - 2);
|
||||
|
||||
responseToReader_len = sizeof(template);
|
||||
memcpy(responseToReader, &template[0], responseToReader_len);
|
||||
|
||||
Dbprintf("\nafter crc rearranged is: ");
|
||||
Dbhexdump(responseToReader_len, &responseToReader[0], false); // special print
|
||||
}
|
||||
|
||||
// Correct uid size bits in ATQA
|
||||
rATQA[0] = (rATQA[0] & 0x3f) | 0x00; // single size uid
|
||||
|
||||
} else if ((flags & FLAG_7B_UID_IN_DATA_OLD) == FLAG_7B_UID_IN_DATA_OLD) {
|
||||
memcpy(&rUIDBCC1[1], datain, 3);
|
||||
memcpy(rUIDBCC2, datain + 3, 4);
|
||||
*uid_len = 7;
|
||||
if (g_dbglevel >= DBG_EXTENDED)
|
||||
Dbprintf("MifareSimInit - FLAG_7B_UID_IN_DATA_OLD => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_7B_UID_IN_DATA_OLD, flags, rUIDBCC1);
|
||||
|
||||
// save CUID
|
||||
*cuid = bytes_to_num(rUIDBCC2, 4);
|
||||
// CascadeTag, CT
|
||||
rUIDBCC1[0] = MIFARE_SELECT_CT;
|
||||
// BCC
|
||||
rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
|
||||
rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3];
|
||||
if (g_dbglevel > DBG_NONE) {
|
||||
Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x",
|
||||
rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], rUIDBCC2[0], rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3]);
|
||||
// If we would return a PAY1 fci response, we instead return a PAY2 fci response
|
||||
//if (responseToReader[1] == fci_template_pay1[1] && true) {
|
||||
if (currentState == SELECT_PAY1_AID) {
|
||||
Dbprintf("We saw a PAY1 response... modifying it to a PAY2 response for outgoing !!!!");
|
||||
memcpy(responseToReader, fci_template_pay2, sizeof(fci_template_pay2));
|
||||
responseToReader_len = sizeof(fci_template_pay2);
|
||||
}
|
||||
|
||||
// Correct uid size bits in ATQA
|
||||
rATQA[0] = (rATQA[0] & 0x3f) | 0x40; // double size uid
|
||||
EmSendCmd(responseToReader, responseToReader_len);
|
||||
|
||||
} else if ((flags & FLAG_10B_UID_IN_DATA_OLD) == FLAG_10B_UID_IN_DATA_OLD) {
|
||||
memcpy(&rUIDBCC1[1], datain, 3);
|
||||
memcpy(&rUIDBCC2[1], datain + 3, 3);
|
||||
memcpy(rUIDBCC3, datain + 6, 4);
|
||||
*uid_len = 10;
|
||||
if (g_dbglevel >= DBG_EXTENDED)
|
||||
Dbprintf("MifareSimInit - FLAG_10B_UID_IN_DATA_OLD => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_10B_UID_IN_DATA_OLD, flags, rUIDBCC1);
|
||||
|
||||
// save CUID
|
||||
*cuid = bytes_to_num(rUIDBCC3, 4);
|
||||
// CascadeTag, CT
|
||||
rUIDBCC1[0] = MIFARE_SELECT_CT;
|
||||
rUIDBCC2[0] = MIFARE_SELECT_CT;
|
||||
// BCC
|
||||
rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3];
|
||||
rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3];
|
||||
rUIDBCC3[4] = rUIDBCC3[0] ^ rUIDBCC3[1] ^ rUIDBCC3[2] ^ rUIDBCC3[3];
|
||||
|
||||
if (g_dbglevel > DBG_NONE) {
|
||||
Dbprintf("10B UID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3],
|
||||
rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3],
|
||||
rUIDBCC3[0], rUIDBCC3[1], rUIDBCC3[2], rUIDBCC3[3]
|
||||
);
|
||||
}
|
||||
|
||||
// Correct uid size bits in ATQA
|
||||
rATQA[0] = (rATQA[0] & 0x3f) | 0x80; // triple size uid
|
||||
} else {
|
||||
Dbprintf("ERROR: " _RED_("UID size not defined"));
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & FLAG_FORCED_ATQA) {
|
||||
rATQA[0] = atqa >> 8;
|
||||
rATQA[1] = atqa & 0xff;
|
||||
}
|
||||
if (flags & FLAG_FORCED_SAK) {
|
||||
rSAK[0] = sak;
|
||||
}
|
||||
|
||||
if (g_dbglevel > DBG_NONE) {
|
||||
Dbprintf("ATQA : %02X %02X", rATQA[1], rATQA[0]);
|
||||
Dbprintf("SAK : %02X", rSAK[0]);
|
||||
}
|
||||
|
||||
// clone UIDs for byte-frame anti-collision multiple tag selection procedure
|
||||
memcpy(rUIDBCC1b4, &rUIDBCC1[1], 4);
|
||||
memcpy(rUIDBCC1b3, &rUIDBCC1[2], 3);
|
||||
memcpy(rUIDBCC1b2, &rUIDBCC1[3], 2);
|
||||
memcpy(rUIDBCC1b1, &rUIDBCC1[4], 1);
|
||||
if (*uid_len >= 7) {
|
||||
memcpy(rUIDBCC2b4, &rUIDBCC2[1], 4);
|
||||
memcpy(rUIDBCC2b3, &rUIDBCC2[2], 3);
|
||||
memcpy(rUIDBCC2b2, &rUIDBCC2[3], 2);
|
||||
memcpy(rUIDBCC2b1, &rUIDBCC2[4], 1);
|
||||
}
|
||||
if (*uid_len == 10) {
|
||||
memcpy(rUIDBCC3b4, &rUIDBCC3[1], 4);
|
||||
memcpy(rUIDBCC3b3, &rUIDBCC3[2], 3);
|
||||
memcpy(rUIDBCC3b2, &rUIDBCC3[3], 2);
|
||||
memcpy(rUIDBCC3b1, &rUIDBCC3[4], 1);
|
||||
}
|
||||
|
||||
// Calculate actual CRC
|
||||
AddCrc14A(rSAK, sizeof(rSAK) - 2);
|
||||
|
||||
#define TAG_RESPONSE_COUNT 18
|
||||
static tag_response_info_t responses_init[TAG_RESPONSE_COUNT] = {
|
||||
{ .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type
|
||||
{ .response = rSAK, .response_n = sizeof(rSAK) }, //
|
||||
{ .response = rSAKuid, .response_n = sizeof(rSAKuid) }, //
|
||||
// Do not reorder. Block used via relative index of rUIDBCC1
|
||||
{ .response = rUIDBCC1, .response_n = sizeof(rUIDBCC1) }, // Anticollision cascade1 - respond with first part of uid
|
||||
{ .response = rUIDBCC1b4, .response_n = sizeof(rUIDBCC1b4)},
|
||||
{ .response = rUIDBCC1b3, .response_n = sizeof(rUIDBCC1b3)},
|
||||
{ .response = rUIDBCC1b2, .response_n = sizeof(rUIDBCC1b2)},
|
||||
{ .response = rUIDBCC1b1, .response_n = sizeof(rUIDBCC1b1)},
|
||||
// Do not reorder. Block used via relative index of rUIDBCC2
|
||||
{ .response = rUIDBCC2, .response_n = sizeof(rUIDBCC2) }, // Anticollision cascade2 - respond with 2nd part of uid
|
||||
{ .response = rUIDBCC2b4, .response_n = sizeof(rUIDBCC2b4)},
|
||||
{ .response = rUIDBCC2b3, .response_n = sizeof(rUIDBCC2b3)},
|
||||
{ .response = rUIDBCC2b2, .response_n = sizeof(rUIDBCC2b2)},
|
||||
{ .response = rUIDBCC2b1, .response_n = sizeof(rUIDBCC2b1)},
|
||||
// Do not reorder. Block used via relative index of rUIDBCC3
|
||||
{ .response = rUIDBCC3, .response_n = sizeof(rUIDBCC3) }, // Anticollision cascade3 - respond with 3th part of uid
|
||||
{ .response = rUIDBCC3b4, .response_n = sizeof(rUIDBCC3b4)},
|
||||
{ .response = rUIDBCC3b3, .response_n = sizeof(rUIDBCC3b3)},
|
||||
{ .response = rUIDBCC3b2, .response_n = sizeof(rUIDBCC3b2)},
|
||||
{ .response = rUIDBCC3b1, .response_n = sizeof(rUIDBCC3b1)}
|
||||
};
|
||||
|
||||
// Prepare ("precompile") the responses of the anticollision phase.
|
||||
// There will be not enough time to do this at the moment the reader sends its REQA or SELECT
|
||||
// There are 18 predefined responses with a total of 53 bytes data to transmit.
|
||||
// Coded responses need one byte per bit to transfer (data, parity, start, stop, correction)
|
||||
// 53 * 8 data bits, 53 * 1 parity bits, 18 start bits, 18 stop bits, 18 correction bits -> need 571 bytes buffer
|
||||
#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 571
|
||||
|
||||
uint8_t *free_buffer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE);
|
||||
// modulation buffer pointer and current buffer free space size
|
||||
uint8_t *free_buffer_pointer = free_buffer;
|
||||
size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE;
|
||||
|
||||
for (size_t i = 0; i < TAG_RESPONSE_COUNT; i++) {
|
||||
if (prepare_allocated_tag_modulation(&responses_init[i], &free_buffer_pointer, &free_buffer_size) == false) {
|
||||
Dbprintf("Not enough modulation buffer size, exit after %d elements", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*responses = responses_init;
|
||||
|
||||
// indices into responses array:
|
||||
#define ATQA 0
|
||||
#define SAK 1
|
||||
#define SAKuid 2
|
||||
#define UIDBCC1 3
|
||||
#define UIDBCC2 8
|
||||
#define UIDBCC3 13
|
||||
|
||||
return true;
|
||||
// Send a request for more time, and cache the command we want to process
|
||||
EmSendCmd(extend_resp, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
*xxxxxxxxxxxxxxxxxx.
|
||||
* EMVsim - simulate an EMV contactless card transaction by
|
||||
*
|
||||
*@param flags :
|
||||
*@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ...
|
||||
*@param flags: See pm3_cmd.h for the full definitions
|
||||
*@param exitAfterNReads, exit simulation after n transactions (default 1)
|
||||
*@param uid, UID - must be length 7
|
||||
*@param atqa, override for ATQA, flags indicate if this is used
|
||||
*@param sak, override for SAK, flags indicate if this is used
|
||||
* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted)
|
||||
*/
|
||||
void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak) {
|
||||
void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak) {
|
||||
|
||||
tag_response_info_t *responses;
|
||||
uint8_t cardSTATE = MFEMUL_NOFIELD;
|
||||
uint8_t uid_len = 0; // 4, 7, 10
|
||||
uint32_t cuid = 0, authTimer = 0;
|
||||
uint32_t nr, ar;
|
||||
uint8_t uid_len = 0; // 7
|
||||
uint32_t cuid = 0;
|
||||
|
||||
uint8_t cardWRBL = 0;
|
||||
uint8_t cardAUTHSC = 0;
|
||||
uint8_t cardAUTHKEY = AUTHKEYNONE; // no authentication
|
||||
uint32_t cardRr = 0;
|
||||
uint32_t ans = 0;
|
||||
uint32_t cardINTREG = 0;
|
||||
uint8_t cardINTBLOCK = 0;
|
||||
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs;
|
||||
pcs = &mpcs;
|
||||
|
||||
//uint32_t numReads = 0; //Counts numer of times reader reads a block
|
||||
uint8_t receivedCmd[MAX_FRAME_SIZE] = {0x00};
|
||||
uint8_t receivedCmd_copy[MAX_FRAME_SIZE] = {0x00};
|
||||
uint8_t receivedCmd_dec[MAX_FRAME_SIZE] = {0x00};
|
||||
//uint8_t convenient_buffer[64] = {0x00};
|
||||
uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE] = {0x00};
|
||||
uint8_t responseToReader[MAX_FRAME_SIZE] = {0x00};
|
||||
uint16_t responseToReader_len;
|
||||
uint16_t receivedCmd_len;
|
||||
uint16_t receivedCmd_len_copy = 0;
|
||||
|
||||
|
@ -383,34 +322,28 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
uint8_t *rats = NULL;
|
||||
uint8_t rats_len = 0;
|
||||
|
||||
// if fct is called with NULL we need to assign some memory since this pointer is passaed around
|
||||
uint8_t datain_tmp[10] = {0};
|
||||
if (datain == NULL) {
|
||||
datain = datain_tmp;
|
||||
// if fct is called with NULL we need to assign some memory since this pointer is passed around
|
||||
uint8_t uid_tmp[10] = {0};
|
||||
if (uid == NULL) {
|
||||
uid = uid_tmp;
|
||||
}
|
||||
|
||||
//Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2
|
||||
// This will be used in the reader-only attack.
|
||||
|
||||
//allow collecting up to 7 sets of nonces to allow recovery of up to 7 keys
|
||||
#define ATTACK_KEY_COUNT 7 // keep same as define in cmdhfmf.c -> readerAttack() (Cannot be more than 7)
|
||||
nonces_t ar_nr_resp[ATTACK_KEY_COUNT * 2]; // *2 for 2 separate attack types (nml, moebius) 36 * 7 * 2 bytes = 504 bytes
|
||||
memset(ar_nr_resp, 0x00, sizeof(ar_nr_resp));
|
||||
|
||||
uint8_t ar_nr_collected[ATTACK_KEY_COUNT * 2]; // *2 for 2nd attack type (moebius)
|
||||
memset(ar_nr_collected, 0x00, sizeof(ar_nr_collected));
|
||||
bool gettingMoebius = false;
|
||||
|
||||
const tUart14a *uart = GetUart14a();
|
||||
|
||||
// free eventually allocated BigBuf memory but keep Emulator Memory
|
||||
BigBuf_free_keep_EM();
|
||||
|
||||
if (MifareSimInit(flags, datain, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) {
|
||||
// Print all arguments going into mifare sim init
|
||||
Dbprintf("EMVsim: flags: %04x, uid: %p, atqa: %04x, sak: %02x", flags, uid, atqa, sak);
|
||||
|
||||
if (MifareSimInit(flags, uid, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) {
|
||||
BigBuf_free_keep_EM();
|
||||
return;
|
||||
}
|
||||
|
||||
// Print all the outputs after the sim init
|
||||
Dbprintf("EMVsim: cuid: %08x, uid_len: %d, rats: %p, rats_len: %d", cuid, uid_len, rats, rats_len);
|
||||
|
||||
// We need to listen to the high-frequency, peak-detected path.
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN);
|
||||
|
||||
|
@ -420,8 +353,6 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
LED_D_ON();
|
||||
ResetSspClk();
|
||||
|
||||
uint8_t *p_em = BigBuf_get_EM_addr();
|
||||
|
||||
int counter = 0;
|
||||
bool finished = false;
|
||||
bool button_pushed = BUTTON_PRESS();
|
||||
|
@ -445,9 +376,6 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
int res = EmGetCmd(receivedCmd, sizeof(receivedCmd), &receivedCmd_len, receivedCmd_par);
|
||||
|
||||
if (res == 2) { //Field is off!
|
||||
if ((flags & FLAG_CVE21_0430_OLD) == FLAG_CVE21_0430_OLD) {
|
||||
p_em[1] = 0x21;
|
||||
}
|
||||
LEDsoff();
|
||||
if (cardSTATE != MFEMUL_NOFIELD) {
|
||||
Dbprintf("cardSTATE = MFEMUL_NOFIELD");
|
||||
|
@ -600,17 +528,17 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
break;
|
||||
}
|
||||
|
||||
memcpy(receivedCmd_dec, receivedCmd, receivedCmd_len);
|
||||
|
||||
// all commands must have a valid CRC
|
||||
if (!CheckCrc14A(receivedCmd_dec, receivedCmd_len)) {
|
||||
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] All commands must have a valid CRC %02X (%d)", receivedCmd_dec, receivedCmd_len);
|
||||
if (!CheckCrc14A(receivedCmd, receivedCmd_len)) {
|
||||
if (g_dbglevel >= DBG_EXTENDED)
|
||||
Dbprintf("[MFEMUL_WORK] All commands must have a valid CRC %02X (%d)", receivedCmd,
|
||||
receivedCmd_len);
|
||||
break;
|
||||
}
|
||||
|
||||
// rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued
|
||||
// BUT... ACK --> NACK
|
||||
if (receivedCmd_len == 1 && receivedCmd_dec[0] == CARD_ACK) {
|
||||
if (receivedCmd_len == 1 && receivedCmd[0] == CARD_ACK) {
|
||||
Dbprintf("[MFEMUL_WORK] ACK --> NACK !!");
|
||||
EmSend4bit(CARD_NACK_NA);
|
||||
FpgaDisableTracing();
|
||||
|
@ -618,7 +546,7 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
}
|
||||
|
||||
// rule 12 of 7.5.3. in ISO 14443-4. R(NAK) --> R(ACK)
|
||||
if (receivedCmd_len == 1 && receivedCmd_dec[0] == CARD_NACK_NA) {
|
||||
if (receivedCmd_len == 1 && receivedCmd[0] == CARD_NACK_NA) {
|
||||
Dbprintf("[MFEMUL_WORK] NACK --> NACK !!");
|
||||
EmSend4bit(CARD_ACK);
|
||||
FpgaDisableTracing();
|
||||
|
@ -626,7 +554,7 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
}
|
||||
|
||||
// case MFEMUL_WORK => CMD RATS
|
||||
if (receivedCmd_len == 4 && receivedCmd_dec[0] == ISO14443A_CMD_RATS && receivedCmd_dec[1] == 0x80) {
|
||||
if (receivedCmd_len == 4 && receivedCmd[0] == ISO14443A_CMD_RATS && receivedCmd[1] == 0x80) {
|
||||
if (rats && rats_len) {
|
||||
EmSendCmd(rats, rats_len);
|
||||
FpgaDisableTracing();
|
||||
|
@ -641,9 +569,9 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
}
|
||||
|
||||
// case MFEMUL_WORK => ISO14443A_CMD_NXP_DESELECT
|
||||
if (receivedCmd_len == 3 && receivedCmd_dec[0] == ISO14443A_CMD_NXP_DESELECT) {
|
||||
if (receivedCmd_len == 3 && receivedCmd[0] == ISO14443A_CMD_NXP_DESELECT) {
|
||||
if (rats && rats_len) {
|
||||
EmSendCmd(receivedCmd_dec, receivedCmd_len);
|
||||
EmSendCmd(receivedCmd, receivedCmd_len);
|
||||
|
||||
FpgaDisableTracing();
|
||||
if (g_dbglevel >= DBG_EXTENDED)
|
||||
|
@ -658,111 +586,12 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
break;
|
||||
}
|
||||
|
||||
// The WTX we want to send out...
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x01, 0x91, 0x40};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x02, 0x0a, 0x72};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x03, 0x83, 0x63};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x04, 0x3c, 0x17};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x05, 0x50, 0x6b};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x06, 0x2e, 0x34};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x07, 0xa7, 0x25};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x08, 0x50, 0xdd}; // This works
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x09, 0xd9, 0xcc};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x0a, 0x42, 0xfe};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x0b, 0xcb, 0xef};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x0c, 0x74, 0x9b};
|
||||
//static uint8_t extend_resp[] = {0xf2, 0x0d, 0xfd, 0x8a};
|
||||
static uint8_t extend_resp[] = {0xf2, 0x0e, 0x66, 0xb8};
|
||||
|
||||
// special print me
|
||||
Dbprintf("\nrecvd from reader:");
|
||||
Dbhexdump(receivedCmd_len, receivedCmd, false);
|
||||
Dbprintf("");
|
||||
// From this point onwards is where the 'magic' happens
|
||||
ExecuteEMVSim(receivedCmd, receivedCmd_len, receivedCmd_copy, receivedCmd_len_copy);
|
||||
|
||||
// lets handle some obvious stuff here!
|
||||
if (receivedCmd[6] == 'O' && receivedCmd[7] == 'S' && receivedCmd[8] == 'E') {
|
||||
Dbprintf("We saw OSE... ignore it!");
|
||||
//Full: 02 6a 82 93 2f
|
||||
EmSendCmd(filenotfound, 5);
|
||||
continue;
|
||||
}
|
||||
|
||||
// rather than asing for more time, lets just send the response with the PDOL there too
|
||||
// there are two of this for some reason?? Ach, this one is not at the card read level, that is why.
|
||||
if (memcmp(&fci_query[0], receivedCmd, sizeof(fci_query)) == 0 && false) {
|
||||
Dbprintf("***** returning fast FCI response...!");
|
||||
//uint8_t modified_response[] = { 0x03, 0x77, 0x0e, 0x82, 0x02, 0x39, 0x80, 0x94, 0x08, 0x18, 0x01, 0x02, 0x01, 0x20, 0x01, 0x04, 0x00, 0x90, 0x00, 0x03, 0xec };
|
||||
//uint8_t modified_response[] = { 0x03, 0x77, 0x0e, 0x82, 0x02, 0x39, 0x80, 0x94, 0x08, 0x18, 0x01, 0x02, 0x01, 0x20, 0x01, 0x04, 0x00, 0x90, 0x00, 0x03, 0xec };
|
||||
EmSendCmd(&fci_template[0], sizeof(fci_template));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// We want to modify corrupted request
|
||||
if ((receivedCmd_len > 5 && receivedCmd[0] != 0x03 && receivedCmd[0] != 0x02 && receivedCmd[1] == 0 && receivedCmd[4] == 0) || (receivedCmd[2] == 0xa8)) {
|
||||
//if (receivedCmd[2] == 0xa8) {
|
||||
Dbprintf("We saw signing request... modifying it into a generate ac transaction !!!!");
|
||||
receivedCmd[0] = 0x03;
|
||||
receivedCmd[1] = 0x80;
|
||||
receivedCmd[2] = 0xae;
|
||||
receivedCmd[3] = 0x80;
|
||||
receivedCmd[4] = 0x00;
|
||||
receivedCmd[5] = 0x1d;
|
||||
|
||||
for (int i = 0; i < 29; i++) {
|
||||
receivedCmd[6 + i] = receivedCmd[12 + i];
|
||||
}
|
||||
|
||||
// clear final byte just in case
|
||||
receivedCmd[35] = 0;
|
||||
|
||||
receivedCmd_len = 35 + 3; // Core command is 35, then there is control code and hte crc
|
||||
|
||||
Dbprintf("\nthe command has now become:");
|
||||
Dbhexdump(receivedCmd_len, receivedCmd, false);
|
||||
}
|
||||
|
||||
// Seems unlikely
|
||||
if (receivedCmd_len >= 9 && receivedCmd[6] == '1' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') {
|
||||
Dbprintf("We saw 1PA... !!!!");
|
||||
}
|
||||
|
||||
// Request more time for 2PAY and respond with a modified 1PAY request
|
||||
if (receivedCmd_len >= 9 && receivedCmd[6] == '2' && receivedCmd[7] == 'P' && receivedCmd[8] == 'A') {
|
||||
Dbprintf("We saw 2PA... switching it to 1PAY !!!!");
|
||||
receivedCmd[6] = '1';
|
||||
}
|
||||
|
||||
static uint8_t rnd_resp[] = {0xb2, 0x67, 0xc7};
|
||||
if (memcmp(receivedCmd, rnd_resp, sizeof(rnd_resp)) == 0) {
|
||||
Dbprintf("We saw bad response... !");
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have received the response from a WTX command! Process the cached command at this point.
|
||||
if (memcmp(receivedCmd, extend_resp, sizeof(extend_resp)) == 0) {
|
||||
// Special case: if we are about to do a generate AC, we also need to
|
||||
// make a request for pdol...
|
||||
if (receivedCmd_copy[1] == 0x80 && receivedCmd_copy[2] == 0xae) {
|
||||
Dbprintf("We are about to do a generate AC... we need to request PDOL first...");
|
||||
uint8_t pdol_request[] = { 0x80, 0xa8, 0x00, 0x00, 0x02, 0x83, 0x00 };
|
||||
|
||||
CmdSmartRaw(0xff, &(pdol_request[0]), sizeof(pdol_request), (&responseToReader[0]), &responseToReader_len);
|
||||
}
|
||||
|
||||
// This is minus 3 because we don't include the first byte (prepend), plus we don't want to send the
|
||||
// last two bytes (CRC) to the card
|
||||
CmdSmartRaw(receivedCmd_copy[0], &(receivedCmd_copy[1]), receivedCmd_len_copy - 3, (&responseToReader[0]), &responseToReader_len);
|
||||
EmSendCmd(responseToReader, responseToReader_len);
|
||||
|
||||
Dbprintf("Sent delayed command to card...");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Send a request for more time, and cache the command we want to process
|
||||
EmSendCmd(extend_resp, 4);
|
||||
|
||||
// copy the command and its length (minus 1???)
|
||||
// We want to keep a copy of the command we just saw, because we will process it once we get the
|
||||
// WTX response
|
||||
Dbprintf("Caching command for later processing... its length is %d", receivedCmd_len);
|
||||
memcpy(receivedCmd_copy, receivedCmd, receivedCmd_len);
|
||||
receivedCmd_len_copy = receivedCmd_len;
|
||||
|
@ -785,3 +614,71 @@ void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t a
|
|||
set_tracing(false);
|
||||
BigBuf_free_keep_EM();
|
||||
}
|
||||
|
||||
// annotate iso 7816
|
||||
void annotate(uint8_t *cmd, uint8_t cmdsize) {
|
||||
if (cmdsize < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// From https://mvallim.github.io/emv-qrcode/docs/EMV_v4.3_Book_3_Application_Specification_20120607062110791.pdf
|
||||
// section 6.3.2
|
||||
switch (cmd[1]) {
|
||||
case ISO7816_APPLICATION_BLOCK: {
|
||||
Dbprintf("APPLICATION BLOCK");
|
||||
break;
|
||||
}
|
||||
case ISO7816_APPLICATION_UNBLOCK: {
|
||||
Dbprintf("APPLICATION UNBLOCK");
|
||||
break;
|
||||
}
|
||||
case ISO7816_CARD_BLOCK: {
|
||||
Dbprintf("CARD BLOCK");
|
||||
break;
|
||||
}
|
||||
case ISO7816_EXTERNAL_AUTHENTICATION: {
|
||||
Dbprintf("EXTERNAL AUTHENTICATE");
|
||||
break;
|
||||
}
|
||||
case ISO7816_GENERATE_APPLICATION_CRYPTOGRAM: {
|
||||
Dbprintf("GENERATE APPLICATION CRYPTOGRAM");
|
||||
break;
|
||||
}
|
||||
case ISO7816_GET_CHALLENGE: {
|
||||
Dbprintf("GET CHALLENGE");
|
||||
break;
|
||||
}
|
||||
case ISO7816_GET_DATA: {
|
||||
Dbprintf("GET DATA");
|
||||
break;
|
||||
}
|
||||
case ISO7816_GET_PROCESSING_OPTIONS: {
|
||||
Dbprintf("GET PROCESSING OPTIONS");
|
||||
break;
|
||||
}
|
||||
case ISO7816_INTERNAL_AUTHENTICATION: {
|
||||
Dbprintf("INTERNAL AUTHENTICATION");
|
||||
break;
|
||||
}
|
||||
case ISO7816_PIN_CHANGE: {
|
||||
Dbprintf("PIN CHANGE");
|
||||
break;
|
||||
}
|
||||
case ISO7816_READ_RECORDS: {
|
||||
Dbprintf("READ RECORDS");
|
||||
break;
|
||||
}
|
||||
case ISO7816_SELECT_FILE: {
|
||||
Dbprintf("SELECT FILE");
|
||||
break;
|
||||
}
|
||||
case ISO7816_VERIFY: {
|
||||
Dbprintf("VERIFY");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Dbprintf("NOT RECOGNISED");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,5 +25,6 @@
|
|||
#define AUTHKEYNONE 0xff
|
||||
|
||||
void EMVsim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak);
|
||||
void annotate(uint8_t *cmd, uint8_t cmdsize);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -36,11 +36,6 @@
|
|||
#include "i2c.h"
|
||||
#include "i2c_direct.h"
|
||||
|
||||
static uint8_t fci_template[] = {0x02, 0x6f, 0x5e, 0x84, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xa5, 0x53, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x38, 0x18, 0x9f, 0x66, 0x04, 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x5f, 0x2d, 0x02, 0x65, 0x6e, 0x9f, 0x11, 0x01, 0x01, 0x9f, 0x12, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0xbf, 0x0c, 0x13, 0x9f, 0x5a, 0x05, 0x31, 0x08, 0x26, 0x08, 0x26, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xd8, 0x15};
|
||||
|
||||
static uint8_t pay1_response[] = { 0x6F, 0x1E, 0x84, 0x0E, 0x31, 0x50, 0x41, 0x59 };
|
||||
static uint8_t pay2_response[] = { 0x03, 0x6f, 0x3e, 0x84, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0xa5, 0x2c, 0xbf, 0x0c, 0x29, 0x61, 0x27, 0x4f, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x0a, 0x56, 0x69, 0x73, 0x61, 0x20, 0x44, 0x65, 0x62, 0x69, 0x74, 0x9f, 0x0a, 0x08, 0x00, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x63, 0x04, 0xdf, 0x20, 0x01, 0x80, 0x90, 0x00, 0x07, 0x9d};
|
||||
|
||||
static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint8_t *output, uint16_t *olen) {
|
||||
LED_D_ON();
|
||||
|
||||
|
@ -50,9 +45,6 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
|||
// check if alloacted...
|
||||
smartcard_command_t flags = p->flags;
|
||||
|
||||
//if ((flags & SC_CLEARLOG) == SC_CLEARLOG)
|
||||
//clear_trace();
|
||||
|
||||
if ((flags & SC_LOG) == SC_LOG)
|
||||
set_tracing(true);
|
||||
else
|
||||
|
@ -65,10 +57,9 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
|||
if ((flags & SC_SELECT) == SC_SELECT) {
|
||||
smart_card_atr_t card;
|
||||
bool gotATR = GetATR(&card, true);
|
||||
//reply_old(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t));
|
||||
|
||||
if (gotATR == false) {
|
||||
Dbprintf("No ATR received...\n");
|
||||
//reply_ng(CMD_SMART_RAW, PM3_ESOFT, NULL, 0);
|
||||
goto OUT;
|
||||
}
|
||||
}
|
||||
|
@ -92,8 +83,6 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
|||
);
|
||||
|
||||
if (res == false && g_dbglevel > 3) {
|
||||
//DbpString(I2C_ERROR);
|
||||
//reply_ng(CMD_SMART_RAW, PM3_ESOFT, NULL, 0);
|
||||
Dbprintf("SmartCardDirectSend: I2C_BufferWrite failed\n");
|
||||
goto OUT;
|
||||
}
|
||||
|
@ -109,11 +98,8 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
|||
}
|
||||
|
||||
if (len == 2 && resp[1] == 0x61) {
|
||||
//Dbprintf("Data to be read: len = %d\n", len);
|
||||
//Dbprintf("\n");
|
||||
|
||||
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, resp[2]};
|
||||
//smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(cmd_getresp));
|
||||
|
||||
smart_card_raw_t *payload = (smart_card_raw_t *)BigBuf_calloc(sizeof(smart_card_raw_t) + sizeof(cmd_getresp));
|
||||
payload->flags = SC_RAW | SC_LOG;
|
||||
payload->len = sizeof(cmd_getresp);
|
||||
|
@ -129,8 +115,6 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
|||
resp[2] = 0x82;
|
||||
AddCrc14A(resp, 3);
|
||||
|
||||
//Dbhexdump(5, &resp[0], false); // special print
|
||||
//EmSendCmd(&resp[0], 5);
|
||||
memcpy(output, resp, 5);
|
||||
*olen = 5;
|
||||
}
|
||||
|
@ -142,9 +126,6 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
|||
resp[2] = 0x82;
|
||||
AddCrc14A(resp, 3);
|
||||
|
||||
//Dbhexdump(5, &resp[0], false); // special print
|
||||
//EmSendCmd14443aRaw(&resp[0], 5);
|
||||
//EmSendCmd(&resp[0], 5);
|
||||
memcpy(output, resp, 5);
|
||||
*olen = 5;
|
||||
FpgaDisableTracing();
|
||||
|
@ -154,108 +135,23 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint
|
|||
Dbprintf("***** sending it over the wire... len: %d =>\n", len);
|
||||
resp[1] = prepend;
|
||||
|
||||
// if we have a generate AC request, lets extract the data and populate the template
|
||||
if (resp[1] != 0xff && resp[2] == 0x77) {
|
||||
Dbprintf("we have detected a generate ac response, lets repackage it!");
|
||||
Dbhexdump(len, &resp[1], false); // special print
|
||||
// 11 and 12 are trans counter.
|
||||
// 16 to 24 are the cryptogram
|
||||
// 27 to 34 is issuer application data
|
||||
Dbprintf("atc: %d %d, cryptogram: %d ", resp[11], resp[12], resp[13]);
|
||||
|
||||
// then, on the template:
|
||||
// 61 and 62 for counter
|
||||
// 46 to 54 for cryptogram
|
||||
// 36 to 43 for issuer application data
|
||||
|
||||
uint8_t template[] = { 0x00, 0x00, 0x77, 0x47, 0x82, 0x02, 0x39, 0x00, 0x57, 0x13, 0x47, 0x62, 0x28, 0x00, 0x05, 0x93, 0x38, 0x64, 0xd2, 0x70, 0x92, 0x01, 0x00, 0x00, 0x01, 0x42, 0x00, 0x00, 0x0f, 0x5f, 0x34, 0x01, 0x00, 0x9f, 0x10, 0x07, 0x06, 0x01, 0x12, 0x03, 0xa0, 0x20, 0x00, 0x9f, 0x26, 0x08, 0x56, 0xcb, 0x4e, 0xe1, 0xa4, 0xef, 0xac, 0x74, 0x9f, 0x27, 0x01, 0x80, 0x9f, 0x36, 0x02, 0x00, 0x07, 0x9f, 0x6c, 0x02, 0x3e, 0x00, 0x9f, 0x6e, 0x04, 0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff};
|
||||
|
||||
// do the replacement
|
||||
template[1] = resp[1]; // class bit
|
||||
|
||||
template[61] = resp[11];
|
||||
template[62] = resp[12];
|
||||
|
||||
template[46] = resp[16];
|
||||
template[47] = resp[17];
|
||||
template[48] = resp[18];
|
||||
template[49] = resp[19];
|
||||
template[50] = resp[20];
|
||||
template[51] = resp[21];
|
||||
template[52] = resp[22];
|
||||
template[53] = resp[23];
|
||||
template[54] = resp[24];
|
||||
|
||||
template[36] = resp[27];
|
||||
template[37] = resp[28];
|
||||
template[38] = resp[29];
|
||||
template[39] = resp[30];
|
||||
template[40] = resp[31];
|
||||
template[41] = resp[32];
|
||||
template[42] = resp[33];
|
||||
|
||||
Dbprintf("\nrearranged is: ");
|
||||
len = sizeof(template);
|
||||
Dbhexdump(len, &template[0], false); // special print
|
||||
|
||||
AddCrc14A(&template[1], len - 3);
|
||||
Dbprintf("\nafter crc rearranged is: ");
|
||||
Dbhexdump(len, &template[0], false); // special print
|
||||
Dbprintf("\n");
|
||||
|
||||
//EmSendCmd(&template[1], len-1);
|
||||
memcpy(output, &template[1], len - 1);
|
||||
*olen = len - 1;
|
||||
|
||||
BigBuf_free();
|
||||
return;
|
||||
}
|
||||
|
||||
//Dbhexdump(len, &resp[1], false); // special print
|
||||
AddCrc14A(&resp[1], len);
|
||||
Dbhexdump(len + 2, &resp[1], false); // special print
|
||||
|
||||
// Check we don't want to modify the response (application profile response)
|
||||
//uint8_t modifyme[] = {0x03, 0x77, 0x0e, 0x82, 0x02};
|
||||
Dbhexdump(len + 2, &resp[1], false);
|
||||
|
||||
BigBuf_free();
|
||||
|
||||
if (prepend == 0xff) {
|
||||
Dbprintf("pdol request, we can can the response...");
|
||||
Dbprintf("pdol request, we can ignore the response...");
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp(&resp[2], &pay1_response[0], sizeof(pay1_response)) == 0 && true) {
|
||||
Dbprintf("Switching out the pay1 response for a pay2 response...");
|
||||
//EmSendCmd(&pay2_response[0], sizeof(pay2_response));
|
||||
memcpy(output, &pay2_response[0], sizeof(pay2_response));
|
||||
*olen = sizeof(pay2_response);
|
||||
} else if (memcmp(&resp[1], &fci_template[0], 2) == 0 && true) {
|
||||
Dbprintf("***** modifying response to have full fci template...!");
|
||||
//EmSendCmd(&fci_template[0], sizeof(fci_template));
|
||||
memcpy(output, &fci_template[0], sizeof(fci_template));
|
||||
*olen = sizeof(fci_template);
|
||||
} else {
|
||||
//Dbprintf("***** not modifying response...");
|
||||
//EmSendCmd(&resp[1], len + 2);
|
||||
memcpy(output, &resp[1], len + 2);
|
||||
*olen = len + 2;
|
||||
}
|
||||
memcpy(output, &resp[1], len + 2);
|
||||
*olen = len + 2;
|
||||
|
||||
BigBuf_free();
|
||||
|
||||
//memcpy(saved_command, &resp[1], len+2);
|
||||
//saved_command_len = len+2;
|
||||
//EmSendCmd14443aRaw(&resp[1], len+2);
|
||||
//FpgaDisableTracing();
|
||||
//EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA);
|
||||
}
|
||||
|
||||
//reply_ng(CMD_SMART_RAW, PM3_SUCCESS, resp, len);
|
||||
|
||||
OUT:
|
||||
//BigBuf_free();
|
||||
//set_tracing(false);
|
||||
LEDsoff();
|
||||
}
|
||||
|
||||
|
@ -269,7 +165,6 @@ int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *o
|
|||
dlen = data[4] + 5;
|
||||
}
|
||||
|
||||
//smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + dlen);
|
||||
smart_card_raw_t *payload = (smart_card_raw_t *)BigBuf_calloc(sizeof(smart_card_raw_t) + dlen);
|
||||
if (payload == NULL) {
|
||||
Dbprintf("failed to allocate memory");
|
||||
|
@ -285,7 +180,6 @@ int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *o
|
|||
bool use_t0 = true;
|
||||
|
||||
if (active || active_select) {
|
||||
|
||||
payload->flags |= (SC_CONNECT | SC_CLEARLOG);
|
||||
if (active_select)
|
||||
payload->flags |= SC_SELECT;
|
||||
|
@ -296,7 +190,6 @@ int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *o
|
|||
payload->flags |= SC_WAIT;
|
||||
payload->wait_delay = timeout;
|
||||
}
|
||||
//Dbprintf("SIM Card timeout... %u ms", payload->wait_delay);
|
||||
|
||||
if (dlen > 0) {
|
||||
if (use_t0)
|
||||
|
@ -305,66 +198,7 @@ int CmdSmartRaw(const uint8_t prepend, const uint8_t *data, int dlen, uint8_t *o
|
|||
payload->flags |= SC_RAW;
|
||||
}
|
||||
|
||||
////uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
|
||||
//uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
|
||||
//if (buf == NULL) {
|
||||
// Dbprintf("failed to allocate memory");
|
||||
// free(payload);
|
||||
// return PM3_EMALLOC;
|
||||
//}
|
||||
|
||||
|
||||
//clearCommandBuffer();
|
||||
//SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + dlen);
|
||||
|
||||
//for (int i = 0; i < dlen; i++) {
|
||||
// Dbprintf("%02x ", data[i]);
|
||||
//}
|
||||
|
||||
SmartCardDirectSend(prepend, payload, output, olen);
|
||||
|
||||
//if (reply == false) {
|
||||
// Dbprintf("failed to talk to smart card!!!");
|
||||
// goto out;
|
||||
//}
|
||||
|
||||
//// reading response from smart card
|
||||
//int len = smart_response(buf, PM3_CMD_DATA_SIZE);
|
||||
//if (len < 0) {
|
||||
// free(payload);
|
||||
// free(buf);
|
||||
// return PM3_ESOFT;
|
||||
//}
|
||||
|
||||
//if (buf[0] == 0x6C) {
|
||||
|
||||
// // request more bytes to download
|
||||
// data[4] = buf[1];
|
||||
// memcpy(payload->data, data, dlen);
|
||||
// clearCommandBuffer();
|
||||
// SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + dlen);
|
||||
|
||||
// len = smart_response(buf, PM3_CMD_DATA_SIZE);
|
||||
|
||||
// data[4] = 0;
|
||||
//}
|
||||
|
||||
//if (decode_tlv && len > 4) {
|
||||
// TLVPrintFromBuffer(buf, len - 2);
|
||||
//} else {
|
||||
// if (len > 2) {
|
||||
// Dbprintf("Response data:");
|
||||
// Dbprintf(" # | bytes | ascii");
|
||||
// Dbprintf("---+-------------------------------------------------+-----------------");
|
||||
// print_hex_break(buf, len, 16);
|
||||
// }
|
||||
//}
|
||||
|
||||
//memcpy(buffer, buf, len);
|
||||
|
||||
//out:
|
||||
//free(payload);
|
||||
//free(buf);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -198,7 +198,8 @@ static uint8_t MifareMaxSector(uint16_t flags) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) {
|
||||
bool MifareSimInit (uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) {
|
||||
//static bool MifareSimInitX(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) {
|
||||
|
||||
uint8_t uid_tmp[10] = {0};
|
||||
// SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf
|
||||
|
@ -293,6 +294,7 @@ static bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t s
|
|||
rSAK[0] = rSAK_1k;
|
||||
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 1K ATQA/SAK");
|
||||
} else if (IS_FLAG_MF_SIZE(flags, MIFARE_2K_MAX_BYTES)) {
|
||||
Dbprintf("We got this to happen!!!\n");
|
||||
memcpy(rATQA, rATQA_2k, sizeof(rATQA));
|
||||
rSAK[0] = rSAK_2k;
|
||||
*rats = rRATS;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define __MIFARESIM_H
|
||||
|
||||
#include "common.h"
|
||||
#include "mifare.h"
|
||||
|
||||
#ifndef CheckCrc14A
|
||||
# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len))
|
||||
|
@ -42,5 +43,6 @@
|
|||
#define AUTHKEYNONE 0xff
|
||||
|
||||
void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak);
|
||||
bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -643,21 +643,21 @@ static int CmdEMVSmartToNFC(const char *Cmd) {
|
|||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
int uid_len = 0;
|
||||
int uidlen = 0;
|
||||
uint8_t uid[7] = {0};
|
||||
CLIGetHexWithReturn(ctx, 2, uid, &uid_len);
|
||||
CLIGetHexWithReturn(ctx, 2, uid, &uidlen);
|
||||
|
||||
if (uid_len == 0) {
|
||||
if (uidlen == 0) {
|
||||
PrintAndLogEx(SUCCESS, "No UID provided, using default.");
|
||||
uint8_t default_uid[7] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
memcpy(uid, default_uid, sizeof(default_uid));
|
||||
uid_len = sizeof(default_uid);
|
||||
} else if (uid_len != 7) {
|
||||
uidlen = sizeof(default_uid);
|
||||
} else if (uidlen != 7) {
|
||||
PrintAndLogEx(FAILED, "UID must be 7 bytes long.");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "UID length is %d", uid_len);
|
||||
PrintAndLogEx(SUCCESS, "UID length is %d", uidlen);
|
||||
|
||||
bool testMode = arg_get_lit(ctx, 1);
|
||||
bool show_apdu = true;
|
||||
|
@ -670,7 +670,7 @@ static int CmdEMVSmartToNFC(const char *Cmd) {
|
|||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
// todo: check this is relevant for us.
|
||||
// todo for PR: check this is relevant for us.
|
||||
SetAPDULogging(show_apdu);
|
||||
|
||||
struct {
|
||||
|
@ -681,14 +681,33 @@ static int CmdEMVSmartToNFC(const char *Cmd) {
|
|||
uint8_t sak;
|
||||
} PACKED payload;
|
||||
|
||||
memcpy(payload.uid, uid, uid_len);
|
||||
payload.flags = 0x1204;
|
||||
memcpy(payload.uid, uid, uidlen);
|
||||
|
||||
// Set up the flags for 2K mifare sim with RATS
|
||||
uint16_t flags = 0;
|
||||
|
||||
FLAG_SET_UID_IN_DATA(flags, uidlen);
|
||||
if (IS_FLAG_UID_IN_EMUL(flags)) {
|
||||
PrintAndLogEx(WARNING, "Invalid parameter for UID");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
FLAG_SET_MF_SIZE(flags, MIFARE_2K_MAX_BYTES);
|
||||
//snprintf(csize, sizeof(csize), "2K with RATS");
|
||||
//k_sectors_cnt = MIFARE_2K_MAXSECTOR; // todo: delete
|
||||
|
||||
flags |= FLAG_ATQA_IN_DATA;
|
||||
flags |= FLAG_SAK_IN_DATA;
|
||||
|
||||
payload.flags = flags;
|
||||
//payload.flags = 0x1204;
|
||||
payload.exitAfter = 0x1;
|
||||
payload.atqa = 0x0;
|
||||
payload.sak = 0x20;
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(0x0386, (uint8_t *)&payload, sizeof(payload));
|
||||
SendCommandNG(CMD_HF_ISO14443A_EMV_SIMULATE, (uint8_t *)&payload, sizeof(payload));
|
||||
|
||||
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation");
|
||||
|
||||
|
|
|
@ -490,6 +490,15 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
|
|||
#define ISO7816_GET_CHALLENGE 0x84
|
||||
#define ISO7816_MANAGE_CHANNEL 0x70
|
||||
|
||||
#define ISO7816_APPLICATION_BLOCK 0x1E
|
||||
#define ISO7816_APPLICATION_UNBLOCK 0x18
|
||||
#define ISO7816_CARD_BLOCK 0x16
|
||||
#define ISO7816_GENERATE_APPLICATION_CRYPTOGRAM 0xAE
|
||||
#define ISO7816_GET_PROCESSING_OPTIONS 0xA8
|
||||
#define ISO7816_PIN_CHANGE 0x24
|
||||
#define ISO7816_PIN_CHANGE 0x24
|
||||
|
||||
|
||||
#define ISO7816_GET_RESPONSE 0xC0
|
||||
|
||||
// ISO7816-4 For response APDU's
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue