├── Makefile ├── README.md ├── examples ├── apdu_cli.cpp └── select.cpp └── nfc ├── include ├── apdu.h ├── nfcManager.h └── nfcUtils.h └── src ├── apdu.cpp ├── nfcManager.cpp └── nfcUtils.cpp /Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | BIN = bin 3 | ODIR = obj 4 | CXXFLAGS = -std=c++11 -lnfc 5 | 6 | OBJS = $(ODIR)/apdu.o $(ODIR)/nfcManager.o $(ODIR)/nfcUtils.o $(ODIR)/apdu_cli.o 7 | 8 | .PHONY : examples 9 | examples : $(ODIR) $(OBJS) 10 | $(CC) -o $(BIN)/apdu_cli $(OBJS) $(CXXFLAGS) 11 | $(CC) -o $(BIN)/select $(OBJS) $(CXXFLAGS) 12 | 13 | $(ODIR)/apdu.o : ./nfc/src/apdu.cpp ./nfc/include/apdu.h ./nfc/include/nfcUtils.h 14 | $(CC) -c ./nfc/src/apdu.cpp -o $@ $(CXXFLAGS) 15 | 16 | $(ODIR)/nfcManager.o : ./nfc/src/nfcManager.cpp ./nfc/include/nfcManager.h ./nfc/include/apdu.h ./nfc/include/nfcUtils.h 17 | $(CC) -c ./nfc/src/nfcManager.cpp -o $@ $(CXXFLAGS) 18 | 19 | $(ODIR)/nfcUtils.o : ./nfc/src/nfcUtils.cpp ./nfc/include/nfcUtils.h 20 | $(CC) -c ./nfc/src/nfcUtils.cpp -o $@ $(CXXFLAGS) 21 | 22 | $(ODIR)/apdu_cli.o : ./examples/apdu_cli.cpp ./nfc/include/nfcManager.h ./nfc/include/apdu.h ./nfc/include/nfcUtils.h 23 | $(CC) -c ./examples/apdu_cli.cpp -o $@ $(CXXFLAGS) 24 | 25 | $(ODIR)/select.o : ./examples/select.cpp ./nfc/include/nfcManager.h ./nfc/include/apdu.h ./nfc/include/nfcUtils.h 26 | $(CC) -c ./examples/select.cpp -o $@ $(CXXFLAGS) 27 | 28 | $(ODIR) : 29 | if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi 30 | if [ ! -d $(BIN) ]; then mkdir $(BIN); fi 31 | 32 | .PHONY : clean 33 | clean : 34 | if [ -d $(ODIR) ]; then rm $(ODIR) -r; fi 35 | if [ -d $(BIN) ]; then rm $(BIN) -r; fi 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APDU Library 2 | 3 | Library built on top of libNfc, that allows to easily send APDU commands through a terminal. 4 | 5 | # Compile 6 | 7 | Enter the repo directory then simply : 8 | 9 | ``` 10 | make 11 | ``` 12 | 13 | # Dependency : Libnfc 14 | 15 | This code uses [libnfc](https://github.com/nfc-tools/libnfc) 16 | 17 | If you're on Linux, you can install libnfc simply by doing : 18 | 19 | ``` 20 | git clone https://github.com/nfc-tools/libnfc.git 21 | autoreconf -vis 22 | ./configure --enable-doc 23 | make 24 | sudo make install 25 | ``` 26 | 27 | # Notice 28 | 29 | The code contains an implementation of the APDU protocol according to [Wikipedia](https://fr.wikipedia.org/wiki/Application_Protocol_Data_Unit). 30 | However, I'm not entierly sure if I implemented correctly the **extended apdu** protocol. The `PN532` doesn't support it anyway... 31 | 32 | # How to use 33 | 34 | Use the `NfcManager` to simplify the process of detecting, opening and transceiving data. 35 | 36 | ```c++ 37 | NfcManager manager; 38 | if(manager.open()){ 39 | if(manager.isTargetPresent()){ 40 | // do things ... 41 | } 42 | manager.close(); 43 | } 44 | ``` 45 | 46 | Once the device opened, use the `APDU` class to send and receive APDUs commands. 47 | If you want to perform a **select** command, you would do : 48 | 49 | ```c++ 50 | APDU apdu; 51 | apdu.setClass(0x00); 52 | apdu.setInstruction(0xA4); 53 | apdu.setParams(0x04, 0x00); 54 | apdu.setCmd(hexStringToByteArray(appId)); 55 | apdu.buildAPDU(); 56 | manager.transceive(apdu); 57 | ``` 58 | 59 | To know more about APDUs instruction codes etc, read the beautiful [ISO standard](http://cardwerk.com/smart-card-standard-iso7816-4-section-5-basic-organizations) 60 | 61 | When `manager.transceive()` ends, the `APDU` object will contain the response, if any. You can get it like this : 62 | 63 | ```c++ 64 | std::vector responseBytes = apdu.getRespBytes(); 65 | std::string responseString = apdu.getRespString(); 66 | ``` 67 | 68 | That said, `NfcManager` already implements the `select` command, you just need to do this : 69 | 70 | ```c++ 71 | manager.selectApplication(appId, apdu); 72 | ``` 73 | 74 | # Examples 75 | 76 | You can find a full examples **[here](https://github.com/OmarAflak/APDU-Library/blob/master/examples)**. 77 | -------------------------------------------------------------------------------- /examples/apdu_cli.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../nfc/include/nfcManager.h" 3 | 4 | void spaceless(std::string& str){ 5 | std::string tmp; 6 | for(int i=0 ; i> "; 29 | std::getline(std::cin, input); 30 | spaceless(input); 31 | 32 | if(input == "string"){ 33 | std::cout << apdu.getRespString() << std::endl; 34 | } else{ 35 | std::vector cmd = hexStringToByteArray(input); 36 | apdu.setAPDU(cmd); 37 | 38 | if(manager.transceive(apdu)){ 39 | std::vector response = apdu.getRespBytes(); 40 | std::cout << "<< "; 41 | print(response); 42 | } 43 | } 44 | } 45 | 46 | manager.close(); 47 | } 48 | else{ 49 | std::cout << "Could not open device" << std::endl; 50 | } 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /examples/select.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../nfc/include/nfcManager.h" 3 | 4 | int main() { 5 | NfcManager manager; 6 | APDU apdu; 7 | 8 | if(manager.open()){ 9 | std::cout << "Nfc device opened. Waiting for target..." << std::endl; 10 | 11 | // wait for target to tap 12 | manager.waitForTarget(); 13 | 14 | // SELECT command 15 | if(manager.selectApplication("F222222222", apdu)){ 16 | // do something with response, if any 17 | std::vector responseByte = apdu.getRespBytes(); 18 | std::string responseString = apdu.getRespString(); 19 | 20 | // answer back 21 | apdu.setClass(0x00); 22 | apdu.setInstruction(0x00); 23 | apdu.setParams(0x00, 0x00); 24 | apdu.setCmd("Hello World !"); 25 | apdu.buildAPDU(); 26 | if(!manager.transceive(apdu)){ 27 | std::cout << "Could not send message" << std::endl; 28 | } 29 | } 30 | 31 | manager.close(); 32 | } 33 | else{ 34 | std::cout << "Could not open device" << std::endl; 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /nfc/include/apdu.h: -------------------------------------------------------------------------------- 1 | #ifndef APDU_H 2 | #define APDU_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "nfcUtils.h" 10 | 11 | class APDU { 12 | private: 13 | // command 14 | uint8_t mCla; 15 | uint8_t mIns; 16 | std::vector mParams; 17 | std::vector mLc; 18 | std::vector mCmd; 19 | std::vector mLe; 20 | size_t mExpectedRespLength; 21 | 22 | // whole apdu command 23 | std::vector mAPDU; 24 | 25 | // response 26 | uint8_t mSW1; 27 | uint8_t mSW2; 28 | std::vector mResp; 29 | 30 | // extended apdu 31 | bool mExtended; 32 | size_t mCmdMaxLength; 33 | size_t mRespMaxLength; 34 | 35 | public: 36 | APDU(); 37 | void reset(); 38 | 39 | // command 40 | uint8_t getClass() const; 41 | uint8_t getInstruction() const; 42 | std::vector getParams() const; 43 | std::string getCmdString() const; 44 | std::vector getCmdBytes() const; 45 | size_t getExpectedRespLength() const; 46 | std::vector getAPDU() const; 47 | 48 | void setClass(const uint8_t& cla); 49 | void setInstruction(const uint8_t& ins); 50 | void setParams(const uint8_t& p1, const uint8_t& p2); 51 | void setCmd(const std::string& data); 52 | void setCmd(const std::vector& data); 53 | void setExpectedRespLength(const size_t& length); 54 | void setAPDU(const std::vector& apdu); 55 | void updateCmdLength(); 56 | 57 | std::vector buildAPDU(); 58 | 59 | // response 60 | uint8_t getSW1() const; 61 | uint8_t getSW2() const; 62 | std::string getRespString() const; 63 | std::vector getRespBytes() const; 64 | std::vector buildResp() const; 65 | 66 | void setSW1(const uint8_t& SW1); 67 | void setSW2(const uint8_t& SW2); 68 | void setResp(const std::string& response); 69 | void setResp(const uint8_t* resp, const size_t& length); 70 | 71 | // extended apdu 72 | void setExtended(bool enabled); 73 | bool isExtended() const; 74 | size_t getCmdMaxLength() const; 75 | size_t getRespMaxLength() const; 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /nfc/include/nfcManager.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMUNICATOR 2 | #define COMMUNICATOR 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "apdu.h" 10 | 11 | class NfcManager { 12 | private: 13 | nfc_context* mNfcContext; 14 | nfc_device* mNfcDevice; 15 | nfc_modulation mNfcModulation; 16 | 17 | public: 18 | NfcManager(); 19 | 20 | bool open(); 21 | void close(); 22 | void waitForTarget(); 23 | bool isTargetPresent(); 24 | bool transceive(APDU& apdu); 25 | bool selectApplication(const std::string& appId, APDU& apdu); 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /nfc/include/nfcUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef NFCUTILS 2 | #define NFCUTILS 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | uint8_t charToHex(const char& c); 11 | std::string byteArrayToString(const uint8_t* byteArray, const int& start, const int& end); 12 | std::vector hexStringToByteArray(const std::string& str); 13 | std::vector intToBytes(const size_t& value); 14 | void print(const std::vector& v); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /nfc/src/apdu.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/apdu.h" 2 | 3 | APDU::APDU(){ 4 | mExtended = false; 5 | reset(); 6 | } 7 | 8 | void APDU::reset(){ 9 | mCla = '\x00'; 10 | mIns = '\x00'; 11 | mParams = {'\x00', '\x00'}; 12 | mLc = {}; 13 | mCmd = {}; 14 | 15 | mSW1 = '\x00'; 16 | mSW2 = '\x00'; 17 | mResp = {}; 18 | 19 | setExtended(mExtended); 20 | } 21 | 22 | void APDU::setClass(const uint8_t& cla){ 23 | mCla = cla; 24 | } 25 | 26 | void APDU::setInstruction(const uint8_t& ins){ 27 | mIns = ins; 28 | } 29 | 30 | void APDU::setParams(const uint8_t& p1, const uint8_t& p2){ 31 | mParams[0] = p1; 32 | mParams[1] = p2; 33 | } 34 | 35 | void APDU::setCmd(const std::string& data){ 36 | size_t length = data.size(); 37 | assert(length <= mCmdMaxLength); 38 | mCmd = std::vector(data.begin(), data.end()); 39 | updateCmdLength(); 40 | } 41 | 42 | void APDU::setCmd(const std::vector& data){ 43 | size_t length = data.size(); 44 | assert(length <= mCmdMaxLength); 45 | mCmd = std::vector(data.begin(), data.end()); 46 | updateCmdLength(); 47 | } 48 | 49 | void APDU::setAPDU(const std::vector& apdu){ 50 | mAPDU = std::vector(apdu.begin(), apdu.end());; 51 | } 52 | 53 | void APDU::updateCmdLength(){ 54 | size_t length = mCmd.size(); 55 | std::vector lBytes = intToBytes(length); 56 | if(length==0){ 57 | mLc = {}; 58 | } 59 | else if(length <= 255){ 60 | mLc = {lBytes[3]}; 61 | } 62 | else if(length <= 65535){ 63 | mLc = {'\x00', lBytes[2], lBytes[3]}; 64 | } 65 | } 66 | 67 | void APDU::setExpectedRespLength(const size_t& length){ 68 | assert(length <= mRespMaxLength); 69 | std::vector lBytes = intToBytes(length); 70 | mExpectedRespLength = length; 71 | 72 | if(length==0){ 73 | mLe = {}; 74 | } 75 | else if(length <= 255){ 76 | mLe = {lBytes[3]}; 77 | } 78 | else if(length==256){ 79 | mLe = {'\x00'}; 80 | } 81 | else if(length <= 65535){ 82 | mLe = {lBytes[2], lBytes[3]}; 83 | } 84 | else if(length <= 65536){ 85 | mLe = {'\x00', '\x00'}; 86 | } 87 | } 88 | 89 | uint8_t APDU::getClass() const{ 90 | return mCla; 91 | } 92 | 93 | uint8_t APDU::getInstruction() const{ 94 | return mIns; 95 | } 96 | 97 | std::vector APDU::getParams() const{ 98 | return mParams; 99 | } 100 | 101 | std::string APDU::getCmdString() const{ 102 | return byteArrayToString(&mCmd[0], 0, mCmd.size()); 103 | } 104 | 105 | std::vector APDU::getCmdBytes() const{ 106 | return mCmd; 107 | } 108 | 109 | size_t APDU::getExpectedRespLength() const{ 110 | return mExpectedRespLength; 111 | } 112 | 113 | std::vector APDU::buildAPDU(){ 114 | std::vector apdu; 115 | apdu.push_back(mCla); 116 | apdu.push_back(mIns); 117 | apdu.insert(apdu.end(), mParams.begin(), mParams.end()); 118 | apdu.insert(apdu.end(), mLc.begin(), mLc.end()); 119 | apdu.insert(apdu.end(), mCmd.begin(), mCmd.end()); 120 | apdu.insert(apdu.end(), mLe.begin(), mLe.end()); 121 | mAPDU = apdu; 122 | return mAPDU; 123 | } 124 | 125 | std::vector APDU::getAPDU() const{ 126 | return mAPDU; 127 | } 128 | 129 | void APDU::setSW1(const uint8_t& SW1){ 130 | mSW1 = SW1; 131 | } 132 | 133 | void APDU::setSW2(const uint8_t& SW2){ 134 | mSW2 = SW2; 135 | } 136 | 137 | void APDU::setResp(const std::string& response){ 138 | size_t length = response.size(); 139 | assert(length <= mRespMaxLength); 140 | mResp = std::vector(response.begin(), response.end()); 141 | } 142 | 143 | void APDU::setResp(const uint8_t* resp, const size_t& length){ 144 | std::string response = byteArrayToString(resp, 0, length); 145 | mResp = std::vector(response.begin(), response.end()); 146 | } 147 | 148 | uint8_t APDU::getSW1() const{ 149 | return mSW1; 150 | } 151 | 152 | uint8_t APDU::getSW2() const{ 153 | return mSW2; 154 | } 155 | 156 | std::string APDU::getRespString() const{ 157 | return byteArrayToString(&mResp[0], 0, mResp.size()); 158 | } 159 | 160 | std::vector APDU::getRespBytes() const{ 161 | return mResp; 162 | } 163 | 164 | std::vector APDU::buildResp() const{ 165 | std::vector apdu; 166 | apdu.push_back(mSW1); 167 | apdu.push_back(mSW2); 168 | apdu.insert(apdu.end(), mResp.begin(), mResp.end()); 169 | return apdu; 170 | } 171 | 172 | void APDU::setExtended(bool enabled){ 173 | mExtended = enabled; 174 | mCmdMaxLength = enabled?65535:255; 175 | mRespMaxLength = enabled?65536:256; 176 | mExpectedRespLength = mRespMaxLength; 177 | mLe = std::vector(enabled?2:1, '\x00'); 178 | } 179 | 180 | bool APDU::isExtended() const{ 181 | return mExtended; 182 | } 183 | 184 | size_t APDU::getCmdMaxLength() const{ 185 | return mCmdMaxLength; 186 | } 187 | 188 | size_t APDU::getRespMaxLength() const{ 189 | return mRespMaxLength; 190 | } 191 | -------------------------------------------------------------------------------- /nfc/src/nfcManager.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/nfcManager.h" 2 | 3 | NfcManager::NfcManager(){ 4 | mNfcContext = NULL; 5 | mNfcDevice = NULL; 6 | } 7 | 8 | bool NfcManager::open(){ 9 | nfc_init(&mNfcContext); 10 | if (mNfcContext == NULL) { 11 | return false; 12 | } 13 | 14 | mNfcDevice = nfc_open(mNfcContext, NULL); 15 | if(mNfcDevice == NULL){ 16 | return false; 17 | } 18 | 19 | if(nfc_initiator_init(mNfcDevice) < 0) { 20 | return false; 21 | } 22 | 23 | mNfcModulation = { 24 | .nmt = NMT_ISO14443A, 25 | .nbr = NBR_106, 26 | }; 27 | 28 | return true; 29 | } 30 | 31 | void NfcManager::close(){ 32 | nfc_close(mNfcDevice); 33 | nfc_exit(mNfcContext); 34 | } 35 | 36 | void NfcManager::waitForTarget(){ 37 | while(!isTargetPresent()); 38 | } 39 | 40 | bool NfcManager::isTargetPresent(){ 41 | nfc_target nfcTarget; 42 | return (nfc_initiator_select_passive_target(mNfcDevice, mNfcModulation, NULL, 0, &nfcTarget)>0); 43 | } 44 | 45 | bool NfcManager::transceive(APDU& apdu){ 46 | std::vector cmdApdu = apdu.getAPDU(); 47 | uint8_t *capdu = &cmdApdu[0]; 48 | size_t capdulen = cmdApdu.size(); 49 | uint8_t rapdu[apdu.getRespMaxLength()]; 50 | size_t rapdulen = apdu.getRespMaxLength(); 51 | 52 | int res = nfc_initiator_transceive_bytes(mNfcDevice, capdu, capdulen, rapdu, rapdulen, 700); 53 | if(res<2){ 54 | return false; 55 | } 56 | 57 | apdu.setSW1(rapdu[res-2]); 58 | apdu.setSW2(rapdu[res-1]); 59 | 60 | if(rapdu[res-2] != 0x90 || rapdu[res-1] != 0x00){ 61 | return false; 62 | } 63 | 64 | apdu.setResp(rapdu, res-2); 65 | return true; 66 | } 67 | 68 | bool NfcManager::selectApplication(const std::string& appId, APDU& apdu){ 69 | apdu.setClass(0x00); 70 | apdu.setInstruction(0xA4); 71 | apdu.setParams(0x04, 0x00); 72 | apdu.setCmd(hexStringToByteArray(appId)); 73 | apdu.buildAPDU(); 74 | return transceive(apdu); 75 | } 76 | -------------------------------------------------------------------------------- /nfc/src/nfcUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/nfcUtils.h" 2 | 3 | uint8_t charToHex(const char& c){ 4 | if(c >= '0' && c <= '9') 5 | return (c - '0'); 6 | else 7 | return (c-'A'+10); 8 | } 9 | 10 | std::string byteArrayToString(const uint8_t* byteArray, const int& start, const int& end){ 11 | std::stringstream ss; 12 | for(int i=start ; i hexStringToByteArray(const std::string& str) { 19 | std::vector hex; 20 | for (int i=0 ; i intToBytes(const size_t& value){ 27 | std::vector result; 28 | result.push_back(value >> 24); 29 | result.push_back(value >> 16); 30 | result.push_back(value >> 8); 31 | result.push_back(value); 32 | return result; 33 | } 34 | 35 | void print(const std::vector& v) { 36 | for(int i=0 ; i