Skip to content

Instantly share code, notes, and snippets.

@gtors
Last active December 25, 2024 08:37
Show Gist options
  • Select an option

  • Save gtors/2542a4f7eb5a0a5573b4b11de21abd6e to your computer and use it in GitHub Desktop.

Select an option

Save gtors/2542a4f7eb5a0a5573b4b11de21abd6e to your computer and use it in GitHub Desktop.
NFC 3ka
//Access Control Reader based on ApplePay (EMV) and Troyka cards
#include <Adafruit_PN532.h>
// If using the breakout with SPI, define the pins for SPI communication.
#define PN532_SCK (2)
#define PN532_MOSI (3)
#define PN532_SS (4)
#define PN532_MISO (5)
// Use this line for a breakout with a software SPI connection (recommended):
Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);
//EMV card global vars
static const uint8_t aid_marker[] = {0x4F, 0x07};
static const uint8_t pan_marker[] = {0x5A, 0x08};
void setup(void) {
Serial.begin(115200);
Serial.println("Hello!");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
while (1); // halt
}
// Got ok data, print it out!
Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
// configure board to read RFID tags
nfc.SAMConfig();
Serial.println("Waiting for an ISO14443A Card ...");
}
// dumb_memmem
static char* dumb_memmem(const char* haystack, int hlen, const char* needle, int nlen) {
// naive implementation
if (nlen > hlen) return 0;
int i;
for (i=0; i<hlen-nlen+1; i++) {
if (memcmp(haystack+i,needle,nlen)==0) {
return (char*)(haystack+i);
}
}
return NULL;
}
// PAN search
static int pan_search(char *haystack, int hlen, char *outbuff){
if (hlen > 20) {
void * searchresult = dumb_memmem(haystack, hlen, pan_marker, 2);
if (searchresult) {
memcpy(outbuff, searchresult + 2, 8);
//Serial.print("Full PAN: ");
//nfc.PrintHex(outbuff, 8);
return 1;
}
return 0;
} // end of if
return 0;
} //end of pan_search
// Get Troyka serial from data block
char * gettroykaserial(char * blockdata)
{
uint32_t serial_temp;
char troykaserial[11];
// Parse Troyka 10 digits serial number (not UID) that printed on card
// located from 3 byte up to 4 lower bits of 3 byte
memcpy(&serial_temp, &(blockdata[3]), 4);
// Covert MSB to LSB byte order
serial_temp = ((serial_temp>>24)&0xf0) | // move half from byte 3 to byte 0
((serial_temp<<8)&0xff0000) | // move byte 1 to byte 2
((serial_temp>>8)&0xff00) | // move byte 2 to byte 1
((serial_temp<<24)&0xff000000); // byte 0 to byte 3
// Shift to half byte to the right
serial_temp = serial_temp >> 4;
// Add zero's padding from the left if necessary
sprintf(troykaserial, "%010lu", serial_temp);
Serial.print("Troyka Serial: ");
Serial.println(troykaserial);
return troykaserial;
}
void loop(void) {
uint8_t success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
// Troyka
uint8_t TroykaKeyA[6] = { 0xA7, 0x3F, 0x5D, 0xC1, 0xD3, 0x33 }; // Key А from 8 sector Troyka card
uint8_t KeyTypeA = 0;
uint8_t KeyTypeB = 1;
uint8_t TroykaBlockAddr = 32; // Sector 8, block 0 (block number 32)
// EMV card
bool apdusuccess;
uint8_t apdu[255];
uint8_t berBuffer[255];
uint8_t berLength = 255;
uint8_t aid[7];
void *searchresult;
uint8_t pan[8] = {0x00};
uint8_t pan_found = 0;
char temp[3];
char * cardID;
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate length
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success) {
// Display some basic information about the card
Serial.println("");
Serial.println("");
Serial.println("Found an ISO14443A card");
Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
Serial.print(" UID Value: ");
nfc.PrintHex(uid, uidLength);
Serial.println("");
if (uid)
{
if (uidLength == 7)
{
// If 7 bytes UID, we probably have a Troyka card
// TODO: check ATS for detect mifare plus instead of UID length
Serial.println("Seems to be a Troyka card (7 byte UID). Lets try authenticate it as Mifare Classic");
success = nfc.mifareclassic_AuthenticateBlock(uid+3, uidLength - 3, TroykaBlockAddr, KeyTypeA, TroykaKeyA);
if (success)
{
Serial.println("Sector 8 (Blocks 32..35) has been authenticated");
uint8_t data[16];
// Try to read content of block 32 (sector 8 block 0)
success = nfc.mifareclassic_ReadDataBlock(TroykaBlockAddr, data);
// Extract Troyka serial from block data
cardID = gettroykaserial(data);
Serial.println("-----------------------");
Serial.print("Card ID: ");
Serial.println(cardID);
delay(2000);
} // end of if (success)
else
{
// If authentication failed, probably it's not mifare card
// tryes to check for EMV card
Serial.println("Failed to authenticate card as Mifare. Try to check for EMV card");
delay(2000);
}
} // end of if (uidLength == 7)
else if (uidLength == 4)
{
// If 4 bytes UID, we probably have a EMV card
Serial.println("Seems to be a EMV card (4 byte UID). Lets try to read PAN nubmer");
// Reading PSE file for AID number
uint8_t apdu[] = {0x00, 0xA4, 0x04, 0x00, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0x00};
apdusuccess = nfc.inDataExchange(apdu, sizeof(apdu), berBuffer, &berLength);
if (apdusuccess)
{
if (berLength > 10) {
searchresult = dumb_memmem(berBuffer, berLength, aid_marker, 2);
if (searchresult) {
memcpy(&aid, searchresult + 2, 7);
//Serial.print("AID: ");
//nfc.PrintHex(aid, 7);
}
}
// Now start application on card (AID)
uint8_t startAID[] = {0x00, 0xa4, 0x04, 0x00, 0x07, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
memcpy(&startAID[5], &aid, 7);
//Serial.print("StartAID is: "); nfc.PrintHex(startAID, sizeof(startAID));
berLength = 255;
apdusuccess = nfc.inDataExchange(startAID, sizeof(startAID), berBuffer, &berLength);
memset(pan, 0x00, 8);
pan_found = 0;
// Looking for PAN
//uint8_t apdu1[] = {0x00, 0xb2, 0x01, 0x0c, 0x00};
//berLength = 255;
//apdusuccess = nfc.inDataExchange(apdu1, sizeof(apdu1), berBuffer, &berLength);
//Serial.print("berBuffer1 is: "); nfc.PrintHex(berBuffer, berLength);
//pan_found = pan_search(berBuffer, berLength, pan);
//if (pan_found) Serial.println("Pan found"); else Serial.println("Pan not found");
//uint8_t apdu2[] = {0x00, 0xb2, 0x02, 0x0c, 0x00};
//berLength = 255;
//apdusuccess = nfc.inDataExchange(apdu2, sizeof(apdu2), berBuffer, &berLength);
//Serial.print("berBuffer3 is: "); nfc.PrintHex(berBuffer, berLength);
//if (!pan_found)
//pan_found = pan_search(berBuffer, berLength, pan);
//if (pan_found) Serial.println("Pan found"); else Serial.println("Pan not found");
uint8_t apdu3[] = {0x00, 0xb2, 0x01, 0x14, 0x00};
berLength = 255;
apdusuccess = nfc.inDataExchange(apdu3, sizeof(apdu3), berBuffer, &berLength);
//Serial.print("berBuffer4 is: "); nfc.PrintHex(berBuffer, berLength);
if (!pan_found)
pan_found = pan_search(berBuffer, berLength, pan);
//if (pan_found) Serial.println("Pan found"); else Serial.println("Pan not found");
uint8_t apdu4[] = {0x00, 0xb2, 0x02, 0x14, 0x00};
uint8_t berLength = 255;
apdusuccess = nfc.inDataExchange(apdu4, sizeof(apdu4), berBuffer, &berLength);
//Serial.print("berBuffer5 is: "); nfc.PrintHex(berBuffer, berLength);
if (!pan_found)
pan_found = pan_search(berBuffer, berLength, pan);
//if (pan_found) Serial.println("Pan found"); else Serial.println("Pan not found");
if (pan_found) {
Serial.println("-----------------------");
Serial.print("Сard ID: ");
for (int i = 3; i < 8; i++) {
sprintf(temp, "%02x", pan[i]);
Serial.print(temp);
}
}
}
}
else
{
Serial.print("Abnormal UID Length: ");
nfc.PrintHex(uid, uidLength);
}
} // end of if (uid)
else
{
// Failed to read card
Serial.println("Failed to read card. Retry after 2 seconds.");
delay(2000);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment