#include #include #include #include #include #include #include #include #include "nfc_smartcard.h" using namespace std; ResponseAPDU::ResponseAPDU(const string &data) { size_t len = data.size(); _valid = len >= 2; if (!_valid) { return; } _sw = (uint8_t(data[len-2]) << 8) | uint8_t(data[len-1]); _data = data.substr(0, len-2); } NFCDevice::NFCDevice() throw(NFCError): pollNr(20), pollPeriod(2), apduTimeout(500), _nfcContext(NULL), _nfcDevice(NULL), _opened(false), _unloaded(false) { nfc_init(&_nfcContext); if (_nfcContext == NULL) { throw NFCError("Unable to init libnfc (malloc)"); } open(); } NFCDevice::~NFCDevice() { close(); unload(); } void NFCDevice::open() throw(NFCError) { if (opened()) { return; } _nfcDevice = nfc_open(_nfcContext, NULL); if (_nfcDevice == NULL) { throw NFCError("Unable to open NFC device."); } _opened = true; if (nfc_initiator_init(_nfcDevice) < 0) { close(); throw NFCError("NFC initiator error"); } } void NFCDevice::close() { if (!opened()) { return; } assert(_nfcDevice); nfc_close(_nfcDevice); _nfcDevice = NULL; _opened = false; } void NFCDevice::unload() { if (_unloaded) { return; } assert(_nfcContext); nfc_exit(_nfcContext); _nfcContext = NULL; _unloaded = true; } std::string NFCDevice::scanUID() throw(NFCError) { int res; nfc_target nt; string uid; if (!opened()) { throw NFCError("NFC device not opened"); } /* nfc_initiator_poll_target works fine for USB-connected PN532 (sleeps until card is available or timeout), * but causes 100% CPU usage on SPI-connected PN532 */ //res = nfc_initiator_poll_target(_nfcDevice, _modulations, _modulationsLen, pollNr, pollPeriod, &nt); res = nfc_initiator_list_passive_targets(_nfcDevice, _modulations[0], &nt, 1); if (res < 0) { throw NFCError("NFC list passive targets error"); } else if (res == 0) { throw NFCError("No card in reader's field"); } const nfc_iso14443a_info& nai = nt.nti.nai; uid = string((const char*)nai.abtUid, nai.szUidLen); // nfc_initiator_deselect_target(_nfcDevice); return uid; } void NFCDevice::selectPassiveTarget() throw(NFCError) { nfc_target nt; while (nfc_initiator_select_passive_target(_nfcDevice, _modulations[0], NULL, 0, &nt) <= 0); } ResponseAPDU NFCDevice::sendAPDU(const string &apdu) throw(NFCError) { int res; uint8_t rapdu[512]; if ((res = nfc_initiator_transceive_bytes(_nfcDevice, (uint8_t*)apdu.data(), apdu.size(), rapdu, 512, apduTimeout)) < 0) { if (res == NFC_EOVFLOW) { throw NFCError("Response APDU too long"); } throw NFCError("Failed to transceive APDU"); } else { string rapduData((char *)rapdu, res); ResponseAPDU responseApdu(rapduData); if (!responseApdu.valid()) { throw NFCError("Invalid response APDU was received"); } return responseApdu; } } std::string NFCDevice::readDesfireNDEF() throw(NFCError) { uint8_t key_data_app[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //auth key int res; uint16_t ndef_msg_len; std::unique_ptr > tags = {freefare_get_tags (_nfcDevice), freefare_free_tags}; if (!tags) { throw NFCError("No tags detected"); } // freefare_get_tags() returns a NULL-terminated list of MifareTag if (!*tags) { throw NFCError("Empty array tag, bailing out"); } MifareTag& tag = *tags; //only first one is used if (DESFIRE != freefare_get_tag_type (tag)) { throw NFCError("Tag is not a Desfire tag"); } res = mifare_desfire_connect (tag); if (res < 0) { throw NFCError("Can't connect to Mifare DESFire target."); } // We've to track DESFire version as NDEF mapping is different struct mifare_desfire_version_info info; res = mifare_desfire_get_version (tag, &info); if (res < 0) { throw NFCError("Error getting Desfire version"); } std::unique_ptr > key_app{mifare_desfire_des_key_new_with_version (key_data_app), mifare_desfire_key_free}; // Mifare DESFire SelectApplication (Select application) MifareDESFireAID aid; if (info.software.version_major==0) { aid = mifare_desfire_aid_new(0xEEEE10); } else { // There is no more relationship between DESFire AID and ISO AID... // Let's assume it's in AID 000001h as proposed in the spec aid = mifare_desfire_aid_new(0x000001); } res = mifare_desfire_select_application(tag, aid); if (res < 0) throw NFCError("Application selection failed. NDEF message might not have been created yet."); free (aid); // Authentication with NDEF Tag Application master key (Authentication with key 0) res = mifare_desfire_authenticate (tag, 0, key_app.get()); if (res < 0) throw NFCError("Authentication with NDEF Tag Application master key failed"); // Read Capability Container file E103 uint8_t lendata[20]; // cf FIXME in mifare_desfire.c read_data() if (info.software.version_major==0) res = mifare_desfire_read_data (tag, 0x03, 0, 2, lendata); else // There is no more relationship between DESFire FID and ISO FileID... // Let's assume it's in FID 01h as proposed in the spec res = mifare_desfire_read_data (tag, 0x01, 0, 2, lendata); if (res < 0) throw NFCError("Read CC len failed"); uint16_t cclen = (((uint16_t) lendata[0]) << 8) + ((uint16_t) lendata[1]); if (cclen < 15) throw NFCError("CC too short IMHO"); std::unique_ptr cc_data{new uint8_t[cclen+20]}; if (info.software.version_major==0) res = mifare_desfire_read_data (tag, 0x03, 0, cclen, cc_data.get()); else res = mifare_desfire_read_data (tag, 0x01, 0, cclen, cc_data.get()); if (res < 0) throw NFCError("Read CC data failed"); // Search NDEF File Control TLV uint8_t off = 7; while (((off+7) < cclen) && (cc_data[off] != 0x04)) { // Skip TLV off += cc_data[off+1] + 2; } if (off+7 >= cclen) throw NFCError("CC does not contain expected NDEF File Control TLV"); if (cc_data[off+2] != 0xE1) throw NFCError("Unknown NDEF File reference in CC"); uint8_t file_no; if (info.software.version_major==0) file_no = cc_data[off+3]; else // There is no more relationship between DESFire FID and ISO FileID... // Let's assume it's in FID 02h as proposed in the spec file_no = 2; uint16_t ndefmaxlen = (((uint16_t) cc_data[off+4]) << 8) + ((uint16_t) cc_data[off+5]); std::unique_ptr ndef_msg{new uint8_t[ndefmaxlen+20]}; // cf FIXME in mifare_desfire.c read_data() res = mifare_desfire_read_data (tag, file_no, 0, 2, lendata); if (res < 0) throw NFCError("Read NDEF len failed"); ndef_msg_len = (((uint16_t) lendata[0]) << 8) + ((uint16_t) lendata[1]); if (ndef_msg_len + 2 > ndefmaxlen) throw NFCError("Declared NDEF size larger than max NDEF size"); res = mifare_desfire_read_data (tag, file_no, 2, ndef_msg_len, ndef_msg.get()); if (res < 0) throw NFCError("Read data failed"); std::string result{(char*)ndef_msg.get(), ndef_msg_len}; mifare_desfire_disconnect (tag); return result; } const nfc_modulation NFCDevice::_modulations[5] = { { /*.nmt = */ NMT_ISO14443A, /* .nbr = */ NBR_106 } //{ /*.nmt = */ NMT_ISO14443B, /* .nbr = */ NBR_106 }, //{ /*.nmt = */ NMT_FELICA, /* .nbr = */ NBR_212 }, //{ /*.nmt = */ NMT_FELICA, /* .nbr = */ NBR_424 }, //{ /*.nmt = */ NMT_JEWEL, /* .nbr = */ NBR_106 }, }; const size_t NFCDevice::_modulationsLen = 1; NFCError::NFCError(const std::string& msg) { _msg = msg; }