NFC - Help to exchange data between RC522 & Android HCE - android

I will explain my project :
I have my RC522 and a door connected on my Arduino UNO.
I can currently open the door with a MIFARE classic.
But now I want to open it with my Android smartphone, this is why I develop a HCE applet to accept the good APDU with the selected AID, then my phone will transfer the data in order to open the door.
But the problem is :
I don't know how to send an APDU command with my Arduino using the RC522.
Currently, for my MIFARE Cards, I use the https://github.com/miguelbalboa/rfid library.
My test code :
byte selectApdu[] = {
0x00, /* CLA */
0xA4, /* INS */
0x04, /* P1 */
0x00, /* P2 */
0x05, /* Length of AID */
0xF2, 0x22, 0x22, 0x22, 0x22,
};
byte * backData = (byte *)malloc(16*sizeof(byte));
byte * dataLen = (byte *)16;
status = mfrc522.PCD_TransceiveData(selectApdu,10,backData,dataLen,NULL,0,false);
if ( status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_TransceiveData() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
else
{
Serial.println(F("PICC_TransceiveData() success "));
}
Neverless, it doesn't work ( "Timeout in communication"), and I slowly need to think that the RC522 is not compatible...

It's an (well commented) open-source project. Just have a look to source code, for instance If you use MIFARE_Read function of MFRC522.cpp
MFRC522::StatusCode MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from.
byte *buffer, ///< The buffer to store the data in
byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK.
) {
MFRC522::StatusCode result;
// Sanity check
if (buffer == NULL || *bufferSize < 18) {
return STATUS_NO_ROOM;
}
// Build command buffer
buffer[0] = PICC_CMD_MF_READ;
buffer[1] = blockAddr;
// Calculate CRC_A
result = PCD_CalculateCRC(buffer, 2, &buffer[2]);
if (result != STATUS_OK) {
return result;
}
// Transmit the buffer and receive the response, validate CRC_A.
return PCD_TransceiveData(buffer, 4, buffer, bufferSize, NULL, 0, true);
} // End MIFARE_Read()
You could see function PCD_TransceiveData is called and check source of this function:
/**
* Executes the Transceive command.
* CRC validation can only be done if backData and backLen are specified.
*
* #return STATUS_OK on success, STATUS_??? otherwise.
*/
MFRC522::StatusCode MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO.
byte sendLen, ///< Number of bytes to transfer to the FIFO.
byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command.
byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned.
byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default NULL.
byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0.
bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated.
) {
byte waitIRq = 0x30; // RxIRq and IdleIRq
return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC);
} // End PCD_TransceiveData()
You could called PCD_TransceiveData or PCD_CommunicateWithPICC functions.
UPDATE
You put 0 values for parameters: "backData", "backLen", and "validBits".
validBits could be null.
backData and backLen must be defined as byte.
UPDATE2
If you have a look at library support protocols It supports ISO/IEC 14443-3 (type A) and not supports ISO/IEC 14443-4 (type B).
If you have a look at Android HCE documentation :
Specifically, Android 4.4 supports emulating cards that are based on
the NFC-Forum ISO-DEP specification (based on ISO/IEC 14443-4) and
process Application Protocol Data Units (APDUs) as defined in the
ISO/IEC 7816-4 specification. Android mandates emulating ISO-DEP only
on top of the Nfc-A (ISO/IEC 14443-3 Type A) technology. Support for
Nfc-B (ISO/IEC 14443-4 Type B) technology is optional. The layering of
all these specifications is shown in the figure 3
In this post: HCE support for ISO/IEC 14443-3 Type B?
Looking at devices in the field, some devices use Type A for HCE and
some seem to use Type B. So it's basically the device manufacturer who
decides if Type A or Type B is used. The Android API provides no means
for the app developer to influence this.
So you have to check with another Android NFC device if your device emulate ISO/IEC 14443-3 (Type A) or ISO/IEC 14443-4 (Type B). You could use NfcTagInfo application on other android device to check this.

Related

Cannot send large APDU commands with libnfc using nfc_initiator_transceive_bytes()

I'm trying to build a C++ wrapper around libnfc to make a communication between my Android and the PN532 RFID module.
This helped me a lot: http://nfc-tools.org/index.php/Libnfc:APDU_example
This code is meant to send an APDU command where the body is contained in message (I'm not sending any header bytes etc.) and read the response into response.
Problem: If message exceeds 262 characters then I get a buffer overflow detected error. Otherwise it works perfectly well. I don't even think the error is thrown by the NFC library.
bool send(const std::string &message, std::string &response){
std::vector<uint8_t> apduCmd(message.begin(), message.end());
uint8_t *capdu = &apduCmd[0];
size_t capdulen = apduCmd.size();
uint8_t rapdu[10];
size_t rapdulen = 10;
// BUFFER OVERFLOW HERE
int res = nfc_initiator_transceive_bytes(m_nfcDevice, capdu, capdulen, rapdu, rapdulen, 500);
if (res<0) {
return false;
}
if(res<2 || rapdu[res-2] != 0x90 || rapdu[res-1] != 0x00){
return false;
}
// byteArrayToString omitting the last two bytes
response = byteArrayToString(rapdu, 0, res-2);
return true;
}
The limit of 262 bytes is a hard limit imposed the PN532 NFC chip. This is the maximum size of the raw data that can be sent (and received) in one InDataExchange command. libnfc explicitly enforces this limit for the method nfc_initiator_transceive_bytes() (see the definition of abtCmd in pn53x_initiator_transceive_bytes() and the definition of PN53x_EXTENDED_FRAME__DATA_MAX_LEN).
What you could do to overcome this limit, is to compose your own ISO/IEC 14443-4 blocks (using InCommunicateThru, i.e. nfc_initiator_transceive_bytes() with m_nfcDevice->bEasyFraming turned off. While each frame would still be limited to 263 bytes (the PN532 actually allows 264 bytes for InCommunicateThru but libnfc seems to limit this to 263 bytes), you can then pack your extended length APDUs into multiple ISO/IEC 14443-4 I-blocks. However, you would need to handle the whole ISO/IEC 14443-4 framing on your own (which means that you also have to take care of receive acknowledgements, etc.)
Finally, since the other communication endpoint is an Android device: Many Android devices do not support extended length APDUs. Consequently, even if you send longer APDUs, you might not be able to receive and process them on the Android side. Also, be aware that you should send proper APDUs conforming to the structures defined in ISO/IEC 7816-4 (i.e. APDUs with valid header and lengths fields), otherwise you might run into issues when talking to some devices.

Android nfcA.connect(), nfcA.transceive(), nfcA.setTimeout() and nfcA.getMaxTransceiveLength()

I have a a number of newbie NfcA questions. There seems to be little guidance on this in the docs and elsewhere on the web, so I hope no-one minds me stringing a few basic questions together here...
I am using nfcA.transceive() to write data to my NTAG213 tag like this:
byte[] result = nfcA.transceive(new byte[] {
(byte)0xA2, // WRITE
(byte)(pageNum & 0x0ff),
myData[0], myData[1], myData[2], myData[3]
});
1. The result array is a single byte of value 10. What does this mean and what other values should I look out for?
I am also using the same method to read data from my NTAG213 tags:
byte[] result = nfcA.transceive(new byte[] {
(byte)0x30, // READ
(byte)(pageNum & 0x0ff)
});
2. I expected this to return 4 bytes of user data (i.e., the 4 bytes that correspond to my pageNum), but it returned 16 bytes. Why is that the case?
3. Is it good practise to check nfcA.isConnected() before calling nfcA.connect() and, if so, is there likely to be any sigificant performance penalty in doing so? (I ask as I have seen code examples from reputable sources of both.)
4. Is it better to call nfcA.setTimeout() before or after nfcA.connect()?
5. For my NTAG213 tags nfcA.getMaxTransceiveLength() returns 253. Does that really mean I can write up to 251 bytes of user data (plus the 2 other bytes) in one go and, if so, is that advisable or is it better to write each page (4 bytes) with separate nfcA.transceive() calls?
1. The result array for a WRITE command is a single byte of value 10. What does this mean and what other values should I look out for?
The value 10 (Ah in hexadecimal or 1010b in binary representation) is an explicit ACK, an acknowledgement returned when a command that returns no data succeeds.
The possible values are actual data, ACK, passive ACK, or NACK. These are defined by the NFC Forum Digital Protocol specification and by the NFC Forum Type 2 Tag Operation specification.
If the command is expected to return actual data on success, the data is returned instead of an explicit ACK value.
ACK is defined as a 4-bit short frame (see NFC Forum Digital Protocol specification and ISO/IEC 14443-3 for further details) with the value 1010b (Ah).
A passive ACK is defined as the tag not sending a response at all within a certain timeout.
NACK is defined as a 4-bit short frame with the value 0x0xb (where x is either 0 or 1).
The NTAG213/215/216 product data sheet is a bit more specific on possible NACK values:
0000b (0h) indicates an invalid command argument.
0001b (1h) indicates a parity or CRC error.
0100b (4h) indicates an invalid authentication counter overflow.
0101b (5h) indicates an EEPROM write error.
In addition to the above, the NFC stack implementations on some devices do not properly propagate NACK responses to the app. Instead they either throw a TagLostException or return null. Similarly, you might(?) get a TagLostException indicating a passive ACK.
Thus, you would typically check the result of the transceive method for the following (unless you send a command that is expected to result in a passive ACK):
try {
response = nfca.transceive(command);
if (response == null) {
// either communication to the tag was lost or a NACK was received
} else if ((response.length == 1) && ((response[0] & 0x00A) != 0x00A)) {
// NACK response according to Digital Protocol/T2TOP
} else {
// success: response contains ACK or actual data
}
} catch (TagLostException e) {
// either communication to the tag was lost or a NACK was received
}
2. I expected the READ method to to return 4 bytes of user data (i.e. the 4 bytes that correspond to my pageNum), but it returned 16 bytes. Why is that the case?
The READ command is defined to return 4 blocks of data starting with the specified block number (in the NFC Forum Type 2 Tag Operation specification). Thus, if you send a READ command for block 4, you get the data of blocks 4, 5, 6, and 7.
3. Is it good practise to check nfcA.isConnected() before calling nfcA.connect() and, if so, is there likely to be any sigificant performance penalty in doing so?
If you receive the Tag handle directly from the NFC system service (through an NFC intent) the tag won't be connected. So unless you use the Tag handle before calling nfca.connect(), I don't see why you would want to call nfca.isConnected() before. However, calling that method before connecting has barely any performance overhead since calling isConnected() on a closed tag technology object will be handled by the famework API without calling into the NFC system service. Hence, it's not much more overhead than a simple if over a boolean member variable of the NfcA object.
4. Is it better to call nfcA.setTimeout() before or after nfcA.connect()?
I'm not sure about that one. However, the transceive timeout is typically reset on disconnecting the tag technology.
5. For my NTAG213 tags nfcA.getMaxTransceiveLength() returns 253. Does that really mean I can write up to 251 bytes of user data (plus the 2 other bytes) in one go and, if so, is that advisable or is it better to write each page (4 bytes) with separate nfcA.transceive() calls?
No, you can only write one block at a time. This is limited by the WRITE command of the NTAG213, which only supports one block as data input.
However, a transceive buffer size of 253 allows you to use the FAST_READ command to read multiple blocks (up to 62, so up to 45 for the NTAG213) at a time:
int firstBlockNum = 0;
int lastBlockNum = 42;
byte[] result = nfcA.transceive(new byte[] {
(byte)0x3A, // FAST_READ
(byte)(firstBlockNum & 0x0ff),
(byte)(lastBlockNum & 0x0ff),
});

Android Mifare Classic authentication Key A not working

I'm having a problem in my application with the MifareClassic.autenticateSectorWithKeyA(int sector, byte[] keyA) method. I have tried many ways but it dose not authenticate.
My key A is:
byte[] key = new byte[] { (byte) 0x3c, (byte) 0x55, (byte) 0x28,
(byte) 0x12, (byte) 0x5c, (byte) 0x61, (byte) 0x00,
(byte) 0x5C, (byte) 0x71, (byte) 0x00 };
What does each byte in this key represent?
If I have a key like 3c5528125c61 as above, how should I write the byte array to authenticate, read block 2 and get the bytes?
What is an AID (Application code)? When should I use it?
Your key byte array does not make much sense as a MIFARE Classic key. A key for MIFARE Classic consists of only 6 bytes. Hence, this would be a possible value for key:
byte[] key = new byte[] { (byte)0x3c, (byte)0x55, (byte)0x28,
(byte)0x12, (byte)0x5c, (byte)0x61 };
How do I authenticate to a sector and read a data block?
In order to read block 2 (and assuming that it is readable after authenticationg with the above key), you would do something like the following:
Authenticate to the sector that contains the block to read (block 2 is located in sector 0, you can use the helber method MifareClassic.blockToSector() to obtain the sector number for a given block number.
If authentication was successful, you can read the block.
final MifareClassic mfc = MifareClassic.get(tag);
mfc.connect();
final int blockNumber = 2;
if (mfc.authenticateSectorWithKeyA(mfc.blockToSector(blockNumber), key)) {
final byte[] data = mfc.readBlock(blockNumber)
// TODO: do something with data
}
mfc.close();
What is AID (Application Identifier)? and when should I use it?
MIFARE Classic itself has a linear memory layout. I.e. one that is addressed based on block numbers, where each block contains 16 bytes. These blocks are grouped into sectors with individual access conditions and keys.
In order to logically assign this data (the sectors/groups of blocks) to specific applications (e.g. some data for a physical access control system, some data for an electronic purse, etc.) and, hence, to use one card for more that one application at the same time, the MIFARE Application Directory (MAD) was introduced. The MAD is basically a lookup table (located in sector 0 for MIFARE Classic 1K and in sectors 0 and 16 for MIFARE Classic 4K). This lookup table maps each sector of the card to one application. Applications are identified though a two byte value, the MIFARE application identifier (AID). Thus, if a card uses the MAD, an application can lookup its data sectors by browsing the MAD for its AID.

Mifare Ultralight C Lock

I'm attempting to lock a Mifare Ultralight C tag. I want to set NDEF pages 0x04 to 0x27 to read-only. I think this can be achieved via Ndef.makeReadonly().
Additionally, I would like to set pages 0x29 to 0x2F to be password protected (for read and write) so they can only be accessed after authentication was successful. I'm struggling to understand which bytes need to set in lock bytes (page 0x28, bytes 0, 1) and if sectors 0x2A and 0x2B need to be set as well.
I'm attempting to set NDEF pages 0x04 to 0x27 to readonly. I think this can be achieved via Ndef.makeReadonly().
Not necessarily, Ndef.makeReadonly() might only set the read-only flag in the capability container (according to the NFC Forum Type 2 Tag Operation specification).
If you want to make sure to set the actual lock bits, you would connect the tag as NfcA or MifareUltralight tag technology and issue a write command for the lock bits instead.
NfcA nfcA = NfcA.get(tag);
nfcA.connect();
byte[] result1 = nfcA.transceive(new byte[] {
(byte)0xA2, /* CMD = WRITE */
(byte)0x02, /* PAGE = 2 */
(byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF /* DATA = lock pages 3..15 */
});
byte[] result2 = nfcA.transceive(new byte[] {
(byte)0xA2, /* CMD = WRITE */
(byte)0x28, /* PAGE = 40 */
(byte)0x0F, (byte)0x00, (byte)0x00, (byte)0x00 /* DATA = lock pages 16..27 */
});
Also see Mifare Ultralight: lock specific pages for the coding of the lockbits.
I would like to set pages 0x29 to 0x2F to be password protected (for read and write) so they can only be accessed after authentication was successful.
Using the write command that I showed above, you would first write your authentication key into pages 44..47. You would then write AUTH1 (page 43) as all-zeros. Finally, you would write AUTH0 (page 42) as 0x29 0x00 0x00 0x00 to require authentication for pages 41 and up. Actually I would suggest to lock pages 40 and up so that nobody could set the lock bits for those pages. Alternatively, you could set the block locking bits (i.e. write 0x1F 0x0F 0x00 0x00 to page 40) so that the lock bits for the unlocked pages cannot be changed.

No response from MIFARE CLASSIC

I'm trying to authenticate any sector of a MIFARE classic card. I'm using a twinlinx mymax sticker (which makes almost any bluetooth device NFC enabled). It sends commands to a connected NFC tag. I've already made a connection and sent and recieved data with a Ultralight C tag, but so far I had no success on accessing a Mifare Classic. Here is my authentication code:
private boolean authenticate(int sector, byte[] key, boolean keyA) {
byte[] cmd = new byte[12];
// First byte is the command
if (keyA) {
cmd[0] = 0x60; // phHal_eMifareAuthentA
} else {
cmd[0] = 0x61; // phHal_eMifareAuthentB
}
// Second byte is block address
cmd[1] = (byte) 0x03;
// Next 6 bytes are the key
System.arraycopy(key, 0, cmd, 2, 6);
// Next 4 bytes is the UID
System.arraycopy(Answer, 3, cmd, 8,4);
byte[] test = null;
//this makes a connection to the NFC tag (and this works)
TR.ConnectToExternalCard(AUTH, (byte)0x00);
//checking if the tag is still connected
if (TR.isCardPresent() == true){
//sending command to authenticate
test = TR.SendCommandPropAndWaitResponse(cmd, (byte) 0x00);
}
try {
if (test != null) {
return true;
}
}
I'm using standard MIFARE Classic keys, the tags are fresh from the factory. The complete command (in bytes) which is sent to the tag is:
[0x60, 0x3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf4, 0xa9, 0xfb]
Any Ideas? The tag seems to be non-responsive... tried accessing other Classic tags but also had no success. Thanks!
It is hard to say what you are doing wrong using an SDK that is not publicly available. However, the API looks familiar enough, so I will give it a try anyway. I can think of a number of things you may try (in decreasing order of likeliness):
The UID bytes may be in the wrong order, so try reversing them.
Perhaps Answer does not only contain the UID, but other bytes, too (e.g. SAK), and you are copying the wrong bytes from it.
The MIFARE Classic tags you have may have a 7-byte UID and you are not using the correct 4 bytes from that.
May be TR.SendCommandPropAndWaitResponse() is the wrong method to use. Perhaps there is a dedicated method for MIFARE Classic.
The MyMax sticker may not support MIFARE Classic. I don't see explicit confirmation on their website that they do. However, indications are that their solution is based on NXP hardware, which always supports MIFARE Classic.

Categories

Resources