diff --git a/brmdoor_nfc.cpp b/brmdoor_nfc.cpp index c64ef82..8e79d10 100644 --- a/brmdoor_nfc.cpp +++ b/brmdoor_nfc.cpp @@ -9,9 +9,23 @@ 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), @@ -105,6 +119,36 @@ std::string NFCDevice::scanUID() throw(NFCError) 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; + } +} + const nfc_modulation NFCDevice::_modulations[5] = { { /*.nmt = */ NMT_ISO14443A, /* .nbr = */ NBR_106 } //{ /*.nmt = */ NMT_ISO14443B, /* .nbr = */ NBR_106 }, diff --git a/brmdoor_nfc.h b/brmdoor_nfc.h index bb1c649..aee6e90 100644 --- a/brmdoor_nfc.h +++ b/brmdoor_nfc.h @@ -29,6 +29,45 @@ protected: std::string _msg; }; +/** + * Represents response APDU for ISO14443-4. + */ +class ResponseAPDU +{ +public: + + /** Parse response APDU from raw data */ + ResponseAPDU(const std::string& data); + + ~ResponseAPDU() {} + + /** Return whole status word */ + uint16_t sw() const {return _sw;} + + /** Return first byte of status word */ + uint8_t sw1() const {return _sw >> 8;} + + /** Return second byte of status word */ + uint8_t sw2() const {return _sw & 0xFF;} + + /** Return whether this is properly formed response */ + bool valid() const {return _valid;} + + /** Returns APDU data */ + const std::string& data() const {return _data;} + +private: + + /** Data from response, without SW1 and SW2 */ + std::string _data; + + /** SW1 and SW2 */ + uint16_t _sw; + + /** Whether response APDU has had enough data to be valid */ + bool _valid; +}; + /** * Represents one PN532 reader device. Config is taken from default * libnfc-specified location. That usually means first device found is used. @@ -67,6 +106,20 @@ public: * @throws NFCError if polling failed */ std::string scanUID() throw(NFCError); + + /** + * Wait for one passive or emulated target and select it by reader. + */ + void selectPassiveTarget() throw(NFCError); + + /** + * Send APDU to passive or emulated target. The target must be already + * selected by selectPassiveTarget() or scanUID(). + * + * @param apdu command APDU to send + * @param returns response APDU received from target + */ + ResponseAPDU sendAPDU(const std::string& apdu) throw(NFCError); /** Open device explicitly. May be useful after explicit close */ void open() throw(NFCError); @@ -94,6 +147,11 @@ public: * (0x01 – 0x0F: 150ms – 2.25s) */ uint8_t pollPeriod; + + /** + * Timeout for waiting response to sent APDU. Value -1 means wait forever. + */ + int apduTimeout; protected: diff --git a/test_nfc.py b/test_nfc.py index a1d13b4..57e7cb6 100755 --- a/test_nfc.py +++ b/test_nfc.py @@ -2,9 +2,33 @@ from brmdoor_nfc import NFCDevice, NFCError from binascii import hexlify +def formatAPDU(apdu): + return " ".join(["%02X" % ord(b) for b in apdu]) + +# Reading of file E104, where usually NDEF message is +hex_apdus = [ + "00 A4 04 00 07 D2760000850101", + "00 a4 00 0c 02 E104", + "00 b0 00 00 30", +] + +apdus = [hex_apdu.replace(" ","").decode("hex") for hex_apdu in hex_apdus] + try: nfc = NFCDevice() - print hexlify(nfc.scanUID()) + uid = nfc.scanUID() + print "UID", hexlify(uid) + + print "Now trying to send ISO14443-4 APDUs" + try: + #nfc.selectPassiveTarget() + for apdu in apdus: + print "Command APDU:", formatAPDU(apdu) + rapdu = nfc.sendAPDU(apdu) + print "Response APDU valid: %s, SW %04x, data %s" % (rapdu.valid(), rapdu.sw(), hexlify(rapdu.data())) + except NFCError, e: + print "Failed to transmit APDU:", e.what() + print "Device is opened:", nfc.opened() print "Closing device" nfc.close()