From 8dad9a5cc454145f7b605a60fc1bf0a5e6db189a Mon Sep 17 00:00:00 2001 From: Ondrej Mikle Date: Sun, 22 Oct 2017 19:44:43 +0200 Subject: [PATCH] Reading NDEF from Desfire --- nfc_smartcard.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++++- nfc_smartcard.h | 9 ++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/nfc_smartcard.cpp b/nfc_smartcard.cpp index bc729a8..ad37b81 100644 --- a/nfc_smartcard.cpp +++ b/nfc_smartcard.cpp @@ -1,9 +1,10 @@ #include #include +#include #include #include - +#include #include "nfc_smartcard.h" @@ -149,6 +150,113 @@ ResponseAPDU NFCDevice::sendAPDU(const string &apdu) throw(NFCError) } } +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"); + } + 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 }, diff --git a/nfc_smartcard.h b/nfc_smartcard.h index aee6e90..3101c5d 100644 --- a/nfc_smartcard.h +++ b/nfc_smartcard.h @@ -118,9 +118,18 @@ public: * * @param apdu command APDU to send * @param returns response APDU received from target + * @throws NFCError if response APDU is too long or couldn't send APDU */ ResponseAPDU sendAPDU(const std::string& apdu) throw(NFCError); + /** + * Read NDEF message from Desfire. + * + * @returns NDEF message or empty string if there wasn't message + * @throws NFCError if there was problem communication with card or couldn't authenticate + */ + std::string readDesfireNDEF() throw(NFCError); + /** Open device explicitly. May be useful after explicit close */ void open() throw(NFCError);