PN532 emulated card not read by an Android phone - android

I am using Arduino UNO with elechouse's library and a PN532 module connected through SPI.
I am trying to emulate a card using the emulate_tag_ndef example in that library, but when I try to read the emulated card with the NFC Tools app on my Samsung Galaxy S7, I get an empty serial number and I don't get the Ndef message similarly to this.
When I try to change the command array in the library according to the post lower on the linked issue on GitHub, then the emulated card cannot be detected by my phone at all.
The PN532 works alright in all other NFC modes (read/write, peer-to-peer) for me.

After trying a lot of possible workarounds, I can bravely claim that Android only works in HCE mode with PN532 and iOS only works with NDEF.

Give a look to this issue on Seeeds studio:
https://github.com/Seeed-Studio/PN532/issues/88
Android phones will only accept Felica Cards. If you put some random IDs, it will not be detected as NDEF card.
To emulate it right, you have to set the card ID in this way:
uint8_t command[] = {
PN532_COMMAND_TGINITASTARGET,
0x05, // MODE: 0x04 = PICC only, 0x01 = Passive only (0x02 = DEP only)
// MIFARE PARAMS
0x04, 0x00, // SENS_RES (seeeds studio set it to 0x04, nxp to 0x08)
0x00, 0x00, 0x00, // NFCID1t (is set over sketch with setUID())
0x20, // SEL_RES (0x20=Mifare DelFire, 0x60=custom)
// FELICA PARAMS
0x01, 0xFE, // NFCID2t (8 bytes) https://github.com/adafruit/Adafruit-PN532/blob/master/Adafruit_PN532.cpp FeliCa NEEDS TO BEGIN WITH 0x01 0xFE!
0x05, 0x01, 0x86,
0x04, 0x02, 0x02,
0x03, 0x00, // PAD (8 bytes)
0x4B, 0x02, 0x4F,
0x49, 0x8A, 0x00,
0xFF, 0xFF, // System code (2 bytes)
0x01, 0x01, 0x66, // NFCID3t (10 bytes)
0x6D, 0x01, 0x01, 0x10,
0x02, 0x00, 0x00,
0x00, // length of general bytes
0x00 // length of historical bytes
};
NFCID1t is the card UID, you can set something on your sketch.
NFCID2t has to be exactly how I wrote in the code above => Felica card
NFCID3t can be some random numbers.
You will see that there will be other issues with your sketch and sometimes it is impossible to debug or see why it does not work (for example if you have multiple NDEF Tags).
So, the most important tool to test your PN532 is this one from NXP:
https://play.google.com/store/apps/details?id=com.nxp.taginfolite
This will give you a lot of info. This app can also read your card, even if it is not formatted in the right way.
Edit: This is how I updated the emulatetag.cpp from PN532 library:
bool EmulateTag::emulate(const uint16_t tgInitAsTargetTimeout){
// https://www.nxp.com/docs/en/nxp/application-notes/AN133910.pdf
// Doc:
// Mode: 0x00 any command is accepted. 0x02 only ATR_REQ. 0x04 only RATS (ISO1443-4)
// Mifare: SENS_RES => bit 6 and 7 must be 0!
// NFCID1t => first byte must be 0x08 according to the ISO
// SEL_RES => bit 6 must be 1, to enable NFC protocol (example 0x40)
// FeliCa: NFCID2t => first 2 bytes must be 0x01 and 0xFE
//
uint8_t command[] = {
PN532_COMMAND_TGINITASTARGET,
0x05, // MODE: 0x04 = PICC only, 0x01 = Passive only (0x02 = DEP only)
// MIFARE PARAMS
0x04, 0x00, // SENS_RES (seeeds studio set it to 0x04, nxp to 0x08)
0x00, 0x00, 0x00, // NFCID1t (is set over sketch with setUID())
0x20, // SEL_RES (0x20=Mifare DelFire, 0x60=custom)
// FELICA PARAMS
0x01, 0xFE, // NFCID2t (8 bytes) https://github.com/adafruit/Adafruit-PN532/blob/master/Adafruit_PN532.cpp FeliCa NEEDS TO BEGIN WITH 0x01 0xFE!
0x05, 0x01, 0x86,
0x04, 0x02, 0x02,
0x03, 0x00, // PAD (8 bytes)
0x4B, 0x02, 0x4F,
0x49, 0x8A, 0x00,
0xFF, 0xFF, // System code (2 bytes)
0x01, 0x01, 0x66, // NFCID3t (10 bytes)
0x6D, 0x01, 0x01, 0x10,
0x02, 0x00, 0x00,
0x00, // length of general bytes
0x00 // length of historical bytes
};
if(uidPtr != 0){ // if uid is set copy 3 bytes to nfcid1
memcpy(command + 4, uidPtr, 3);
}
switch(pn532.tgInitAsTarget(command,sizeof(command), tgInitAsTargetTimeout))
{
case 1: break;
case 0: DMSG("tgInitAsTarget timed out!"); return false; break;
case -2: DMSG("tgInitAsTarget failed!"); return false; break;
}
uint8_t compatibility_container[] = {
0, 0x0F,
0x20,
0, 0x54,
0, 0xFF,
0x04, // T
0x06, // L
0xE1, 0x04, // File identifier
((NDEF_MAX_LENGTH & 0xFF00) >> 8), (NDEF_MAX_LENGTH & 0xFF), // maximum NDEF file size
0x00, // read access 0x0 = granted
0x00 // write access 0x0 = granted | 0xFF = deny
};
if(tagWriteable == false){
compatibility_container[14] = 0xFF;
}
tagWrittenByInitiator = false;
uint8_t rwbuf[128];
uint8_t sendlen;
int16_t status;
int16_t totalReads = 0;
tag_file currentFile = NONE;
uint16_t cc_size = sizeof(compatibility_container);
bool runLoop = true;
while(runLoop){
status = pn532.tgGetData(rwbuf, sizeof(rwbuf));
if(status < 0){
if (status == -2)
{
if (totalReads == 0)
{
if (pn532.tgInitAsTarget(command, sizeof(command), 1000) == 1) continue;
}
else
{
DMSG("Transmission over.\n");
pn532.inRelease();
return true;
}
}
DMSG("tgGetData failed!\n");
pn532.inRelease();
return false;
}
totalReads++;
uint8_t p1 = rwbuf[C_APDU_P1];
uint8_t p2 = rwbuf[C_APDU_P2];
uint8_t lc = rwbuf[C_APDU_LC];
uint16_t p1p2_length = ((int16_t) p1 << 8) + p2;
switch(rwbuf[C_APDU_INS]){
case ISO7816_SELECT_FILE:
switch(p1){
case C_APDU_P1_SELECT_BY_ID:
if(p2 != 0x0c){
DMSG("C_APDU_P2 != 0x0c\n");
setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
} else if(lc == 2 && rwbuf[C_APDU_DATA] == 0xE1 && (rwbuf[C_APDU_DATA+1] == 0x03 || rwbuf[C_APDU_DATA+1] == 0x04)){
setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
if(rwbuf[C_APDU_DATA+1] == 0x03){
currentFile = CC;
} else if(rwbuf[C_APDU_DATA+1] == 0x04){
currentFile = NDEF;
}
} else {
setResponse(TAG_NOT_FOUND, rwbuf, &sendlen);
}
break;
case C_APDU_P1_SELECT_BY_NAME:
const uint8_t ndef_tag_application_name_v2[] = {0, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 };
if(0 == memcmp(ndef_tag_application_name_v2, rwbuf + C_APDU_P2, sizeof(ndef_tag_application_name_v2))){
setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
} else{
DMSG("function not supported\n");
setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
}
break;
}
break;
case ISO7816_READ_BINARY:
switch(currentFile){
case NONE:
setResponse(TAG_NOT_FOUND, rwbuf, &sendlen);
break;
case CC:
if( p1p2_length > NDEF_MAX_LENGTH){
setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen);
}else {
memcpy(rwbuf,compatibility_container + p1p2_length, lc);
setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc);
}
break;
case NDEF:
if( p1p2_length > NDEF_MAX_LENGTH){
setResponse(END_OF_FILE_BEFORE_REACHED_LE_BYTES, rwbuf, &sendlen);
}else {
memcpy(rwbuf, ndef_file + p1p2_length, lc);
setResponse(COMMAND_COMPLETE, rwbuf + lc, &sendlen, lc);
}
break;
}
break;
case ISO7816_UPDATE_BINARY:
if(!tagWriteable){
setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
} else{
if( p1p2_length > NDEF_MAX_LENGTH){
setResponse(MEMORY_FAILURE, rwbuf, &sendlen);
}
else{
memcpy(ndef_file + p1p2_length, rwbuf + C_APDU_DATA, lc);
setResponse(COMMAND_COMPLETE, rwbuf, &sendlen);
tagWrittenByInitiator = true;
uint16_t ndef_length = (ndef_file[0] << 8) + ndef_file[1];
if ((ndef_length > 0) && (updateNdefCallback != 0)) {
updateNdefCallback(ndef_file + 2, ndef_length);
}
}
}
break;
default:
DMSG("Command not supported!");
DMSG_HEX(rwbuf[C_APDU_INS]);
DMSG("\n");
setResponse(FUNCTION_NOT_SUPPORTED, rwbuf, &sendlen);
}
status = pn532.tgSetData(rwbuf, sendlen);
if(status < 0){
DMSG("tgSetData failed\n!");
pn532.inRelease();
return true;
}
}
pn532.inRelease();
return true;
}

just uncomment some stuff like I did and it should work.
void setup()
{
Serial.begin(115200);
Serial.println("------- Emulate Tag --------");
message = NdefMessage();
message.addUriRecord("http://www.elechouse.com");
messageSize = message.getEncodedSize();
if (messageSize > sizeof(ndefBuf)) {
Serial.println("ndefBuf is too small");
while (1) { }
}
Serial.print("Ndef encoded message size: ");
Serial.println(messageSize);
message.encode(ndefBuf);
// comment out this command for no ndef message
nfc.setNdefFile(ndefBuf, messageSize);
// uid must be 3 bytes!
nfc.setUid(uid);
nfc.init();
}
void loop(){
// uncomment for overriding ndef in case a write to this tag occured
nfc.setNdefFile(ndefBuf, messageSize);
// start emulation (blocks)
nfc.emulate();
// or start emulation with timeout
if(!nfc.emulate(1000)){ // timeout 1 second
Serial.println("timed out");
}
// deny writing to the tag
// nfc.setTagWriteable(false);
if(nfc.writeOccured()){
Serial.println("\nWrite occured !");
uint8_t* tag_buf;
uint16_t length;
nfc.getContent(&tag_buf, &length);
NdefMessage msg = NdefMessage(tag_buf, length);
msg.print();
}

Related

How to define an APDU for STORE DATA for Host Card Emulation?

I have been looking in the Global Platform Spec on how to define an APDU for my app which will use Host Card Emulation (HCE). My app is supposed to have one phone behaving like an NFC tag through HCE and another phone acting as the NFC reader. The arbitrary data that i am trying to transfer between the phones is just a simple string containing an ID number, but I'm not really sure how to apply it in the code. I have looked at what the different byte commands mean but I'm really not sure how to apply it.
I think I need to use the STORE DATA command but I'm not sure how to intuitively do it and don't really understand. I am currently looking at the HCE side rather than the reader side.
This is my code so far for the HCE side
public class SecondaryActivity extends HostApduService {
#Override
public void onDeactivated(int reason) {
}
#Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
String inboundApduDescription;
byte[] responseApdu;
if (Arrays.equals(AID_SELECT_APDU, commandApdu)) {
inboundApduDescription = "Application selected";
Log.i("HCEDEMO", inboundApduDescription);
byte[] answer = new byte[2];
answer[0] = (byte) 0x90;
answer[1] = (byte) 0x00;
responseApdu = answer;
return responseApdu;
}
return commandApdu;
}
private static final byte[] AID_SELECT_APDU = {
(byte) 0x00,
(byte) 0xA4,
(byte) 0x04,
(byte) 0x00,
(byte) 0x07,
(byte) 0xF0, (byte) 0x39, (byte) 0x41, (byte) 0x48, (byte) 0x14, (byte) 0x81, (byte) 0x00,
(byte) 0x00
};
private static final byte[] STORE_DATA = {
(byte) 0x00,
(byte) 0xA4,
(byte) 0x04,
(byte) 0xA5, // forproprietary data according to the spec
(byte) 0xE2,
(byte) 0x66, (byte) 0x39, (byte) 0x41, (byte) 0x48, (byte) 0x14, (byte) 0x81, (byte) 0x00,
(byte) 0x00
};
private static final byte[] INSTALL = {
(byte) 0x00,
(byte) 0x00,
};
}
How do I send the data from the HCE phone to the reader phone?
What am I missing?
What needs to be done?
You can define virtually any APDU command for HCE. Only the initial SELECT (by AID) command is required. After that, you can create your own command set (or try to follow ISO/IEC 7816-4 commands) as long as you obey the rules of ISO/IEC 7816 for command/response APDU structure, and stick to valid CLA, INS, and status word values.
Since you only want to transfer an ID, you could send this ID directly in response to the SELECT command:
private static final String ID = "1234567890"
#Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
byte[] responseApdu = new byte[] { (byte)0x6F, (byte)0x00 };
if ((commandApdu != null) && (commandApdu.length >= 4)) {
if ((commandApdu[0] == (byte)0x00) && (commandApdu[1] == (byte)0xA4) && (commandApdu[2] == (byte)0x04) && (commandApdu[3] == (byte)0x00)) {
Log.i("HCEDEMO", "Application selected");
byte[] id = ID.getBytes(Charset.forName("UTF-8"));
responseApdu = new byte[id.length + 2];
System.arraycopy(id, 0, responseApdu, 0, id.length);
responseApdu[id.length] = (byte)0x90;
responseApdu[id.length + 1] = (byte)0x00;
}
}
return responseApdu;
}

Control relay over USB from Android

I have this USB Relay and I'd like to control from an Android phone.
(There is a similar post here but it explains how do it from a Linux shell. By looking at that code i figured i'd be able to solve it - apparently not.)
The device lists in lsusb:
Bus 002 Device 011: ID 16c0:05df VOTI
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 0 (Defined at Interface level)
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x16c0 VOTI
idProduct 0x05df
bcdDevice 1.00
iManufacturer 1
iProduct 2
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 34
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 20mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 0 No Subclass
bInterfaceProtocol 0 None
iInterface 0
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.01
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 22
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 20
I find it strange that the end point is defined as "EP IN". In my mind it should be "EP OUT" since the direction should be "host->device".
Anyway, I use the Android USB manager to create a connection and then initialize the USbRequest for an interrupt based end point. Android permissions and all that is handled so the device is connected successfully.
Snippet for sending data. It runs in a separate thread following the Android guidelines:
UsbRequest request = new UsbRequest();
synchronized (mUsbLock) {
if (mUsbConnection != null && mUsbEndPointIn != null) {
if (!request.initialize(mUsbConnection, mUsbEndPointIn)) {
Log.e(TAG, "Unable to initialize UsbRequest. Thread exits");
return;
} else {
if (DEBUG) {
Log.d(TAG, String.format("Usb request is initialized"));
}
}
} else {
Log.e(TAG, "Usb communication is not up and running. The worker thread should never be started.");
return;
}
}
mRunning.set(true);
while (mRunning.get()) {
if (DEBUG) {
Log.d(TAG, String.format("Waiting for data to be sent to end point"));
}
WorkPackage wp = mWorkQueue.take();
// send the package.
byte[] data = wp.getData();
if (!request.queue(ByteBuffer.wrap(data), data.length)) {
Log.e(TAG, "Unable to queue to send data on UsbRequest.");
continue;
} else {
if (DEBUG) {
Log.d(TAG, String.format("Usb request is queued on end point, ep=%s", printUsbEndpoint(mUsbEndPointIn)));
}
}
}
It seems everything is fine, no errors occur and the request is queued on the end point but then nothing happens at all. I don't get any message back that the request has been handled.
Since the supplier won't release the on/off commands I tried variants based on the Linux post (above). None seem to work though. Supplier only release Windows binary lib.
Sending 8 byte packages (according to max package):
public static byte[] SET_RELAY_ON = {(byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
public static byte[] SET_RELAY_OFF = {(byte) 0xfd, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
Help appreciated.
So, i figured out how to do this. I will write the solution here in case someone else is having a similar problem.
I had to snoop the USB traffic to understand what to actually send. It turned out that the defined interrupt end point was not used at all. So, the communication with the device was not interrupt based but instead using the control transfer type on end point 0.
So, using the Android USB API this translates into:
Open device (device->host direction):
int r = mUsbConnection.controlTransfer(0xa1, 0x01, 0x0300, 0x00, buffer, buffer.length, 500);
Open relay '1' (host->device direction).
byte[] buffer = {(byte) 0xff, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
int r = mUsbConnection.controlTransfer(0x21, 0x09, 0x0300, 0x00, buffer, buffer.length, 500);
Note, the second parameter in the buffer (0x01) is the relay number (in case you have >1 relay on the board). I only had one.
close relay '1' (host->device direction):
byte[] buffer = {(byte) 0xfd, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
int r = mUsbConnection.controlTransfer(0x21, 0x09, 0x0300, 0x00, buffer, buffer.length, 500);
I have found the project in Github which is similar with this it (for who is looking it)
https://github.com/gigacycle/AndroidHidUsbRelayControl
I have tested this code and I can confirm that it works well with the USB relay board.

ACR122 - Android / How to get the UID's [duplicate]

I try to integrate an ACR122 to my android app. I'm using the ANDROID Library (http://www.acs.com.hk/en/products/3/acr122u-usb-nfc-reader/) available from ACS.
Everything work, I can detect the presence of a card but I want to extract the UID/ID of the card. Someone know the function to do that?
Do you have an example of this type of integration?
In case of Mifare card you need to send this APDU byte array to the card: (byte) 0xFF, (byte) 0xCA, (byte) 0x00, (byte) 0x00, (byte) 0x00 . I'm not sure about ACR122 API but probably you need to wrap this APDU into specific API method like transmit()
UPDATE
Sample code:
byte[] command = new byte[] { (byte) 0xFF, (byte) 0xCA, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
byte[] response = new byte[300];
int responseLength;
responseLength = reader.transmit(slotNum, command, command.length, response,response.length);
System.out.println(new String(response));
Reader is com.acs.smartcard.Reader object
and slotNum is a the slot number. I’m not sure how to find it because I don’t have ACR to test. But if you told that you was able to establish basic communication with reader probably you know slotNum.
In order to prevent this error when trying to read the UID:
com.acs.smartcard.InvalidDeviceStateException: The current state is not equal to specific.
This should rather be:
int slotNum = 0;
byte[] payload = new byte[] { (byte) 0xFF, (byte) 0xCA, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
byte[] response = new byte[7]; // 7 bytes + 90 00 (9 bytes)
try {
reader.power(slotNum, Reader.CARD_WARM_RESET);
reader.setProtocol(slotNum, Reader.PROTOCOL_T0 | Reader.PROTOCOL_T1);
reader.transmit(slotNum, payload, payload.length, response, response.length);
logBuffer(response, response.length);
} catch (ReaderException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}

Arduino to Android secure bluetooth connection

I'm using an Arduino Uno and a nRF8001 board from Adafruit to connect to an Android phone over bluetooth. I will be using it to lock and unlock a lock and I need to make sure only verified devices are able to initiate the locking and unlocking. I've searched around a bunch and I'm having trouble finding a clear example of what I should do to verify the connecting device. Currently I have the Arduino hooked up to the lock and whenever the Android phone connects, it is allowed to lock and unlock.
The process I had in mind is as follows.
Android tries to connect to the Arduino
Arduino sees the request and sends a random string to the Android device
The Android encrypts the string with a shared secret key and sends it back to the Arduino
The Arduino decrypts the encrypted string and verifies it matches the original it sent if it does it it goes ahead and connects/continues with locking or unlocking.
[EDIT]
I've done a lot more research and work and from advice from security.stackexchange I decided to use AES to do the encryption. I am using this Arduino library and this Android library I'm a little confused on what configuration I should use on the two libraries I am using from programming an Arduino and Android.
My Arduino code I set up to test encrypting an decrypting:
#include <AES.h>
AES aes ;
byte key[] =
{
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
} ;
byte plain[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
} ;
byte my_iv[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
} ;
char PassString[] = "This is hard to believe but true";
char Message[] = "We, the Fairies, blithe and antic Of dimensions not gigantic, Th";
byte cipher [4*N_BLOCK] ;
byte check [4*N_BLOCK] ;
int bits = 128;
int blocks = 4;
void loop ()
{}
void setup ()
{
Serial.begin (9600) ;
Serial.println ("Starting encryption...") ;
byte iv [N_BLOCK] ;
// Pass the key into the byte array
for (int i = 0; i < 32; i++) {
key[i] = PassString[i];
}
print_value ("KEY = ", key, 256) ;
for (int i = 0; i < 64; i++) {
plain[i] = Message[i];
}
// Set Key
byte succ = aes.set_key (key, bits) ;
// Encrypt
for (byte i = 0 ; i < 16 ; i++)
iv[i] = my_iv[i] ;
succ = aes.cbc_encrypt (plain, cipher, blocks, iv) ;
// Decrypt
for (byte i = 0 ; i < 16 ; i++)
iv[i] = my_iv[i] ;
succ = aes.cbc_decrypt (cipher, check, blocks, iv) ;
// Prints the plain, ciper, decrypted, and IV
for (byte ph = 0 ; ph < (blocks == 1 ? 3 : 4) ; ph++)
{
for (byte i = 0 ; i < (ph < 3 ? blocks*N_BLOCK : N_BLOCK) ; i++)
{
byte val = ph == 0 ? plain[i] : ph == 1 ? cipher[i] : ph == 2 ? check[i] : iv[i] ;
Serial.print (val>>4, HEX) ; Serial.print (val&15, HEX) ; Serial.print (" ") ;
}
Serial.println () ;
}
}
char * hex = "0123456789abcdef" ;
void print_value (char * str, byte * a, int bits)
{
Serial.print (str) ;
bits >>= 3 ; //bits goes from decimal 128 to decimal 16
for (int i = 0 ; i < bits ; i++)
{
// of ex. 0xb9 prints b then 9
byte b = a[i] ;
Serial.print (hex [b >> 4]) ;
Serial.print (hex [b & 15]) ;
}
Serial.println () ;
}
My Android program is almost verbatim from the link above.
I can get both of them to encrypt and decrypt their own messages, but their set ups seem very different and haven't been able to encrypt each others. The Android code seems to have quite a bit more involved like creating keys and salts. Both libraries are pretty versatile and I'm not sure how to make them encrypt the same way.
I have a few questions that hopefully will help me get this solved:
How do the "blocks" in the Arduino code relate to the Android
implementation?
From what I've read, to decrypt the cipher text on the Arduino from
Android I will need to send, the cipher text, the IV, and the PBE
iteration count. Is that correct and everything?
Android code takes my key and uses a bunch of SecretKeys functions
on it to I think randomize it and make it more secure. If I'm having
to store the key on the Arduino, do I need to even bother with this?
Where is the PBE iteration count in the Arduino code? I don't really
see anything in the library. Do I need to implement that myself? I
saw in the "test_vectors" example of the library a section that had
the following bit of code. Is this the iteration?
for (int j = 0 ; j < 1000 ; j++)
{
succ = aes.encrypt (plain, cipher) ;
aes.copy_n_bytes (plain, cipher, 16) ;
}
If you want to send encrypted messages from Android to Arduino, you have to make sure that you use the same parameters in both ends.
I took some test vectors from Vladimir Klykov and then encrypted and decrypted with Arduino (using AES library) and Java.
Both libraries can be set to CBC, use 16 bytes hex vectors in both ends and you won't have padding issues.
Arduino
#include <AES.h>
AES aes ;
//2b7e151628aed2a6abf7158809cf4f3c
byte key[] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
};
//6bc1bee22e409f96e93d7e117393172a
byte plain[] = {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
};
//000102030405060708090A0B0C0D0E0F
byte my_iv[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
};
byte cipher [N_BLOCK] ;
byte check [N_BLOCK] ;
void loop ()
{
}
void setup ()
{
Serial.begin (115200) ;
while (!Serial) ;
Serial.println ("Ready") ;
Serial.print("N_BLOCK: ") ;
Serial.println (N_BLOCK) ;
prekey (128, 2) ;
}
void prekey (int bits, int blocks)
{
byte iv [N_BLOCK] ;
long t0 = micros () ;
byte succ = aes.set_key (key, bits) ;
long t1 = micros()-t0 ;
Serial.print ("set_key ") ; Serial.print (bits) ; Serial.print (" ->") ; Serial.print ((int) succ) ;
Serial.print (" took ") ; Serial.print (t1) ; Serial.println ("us") ;
t0 = micros () ;
if (blocks == 1)
succ = aes.encrypt (plain, cipher) ;
else
{
for (byte i = 0 ; i < 16 ; i++)
iv[i] = my_iv[i] ;
succ = aes.cbc_encrypt (plain, cipher, blocks, iv) ;
}
t1 = micros () - t0 ;
Serial.print ("encrypt") ; Serial.print (" ->") ; Serial.print ((int) succ) ;
Serial.print (" took ") ; Serial.print (t1) ; Serial.println ("us") ;
t0 = micros () ;
if (blocks == 1)
succ = aes.decrypt (cipher, plain) ;
else
{
for (byte i = 0 ; i < 16 ; i++)
iv[i] = my_iv[i] ;
succ = aes.cbc_decrypt (cipher, check, blocks, iv) ;
}
t1 = micros () - t0 ;
Serial.print ("decrypt") ; Serial.print (" ->") ; Serial.print ((int) succ) ;
Serial.print (" took ") ; Serial.print (t1) ; Serial.println ("us") ;
for (byte ph = 0 ; ph < 5 ; ph++)
{
Serial.print(ph == 0 ? "plain: " : ph == 1 ? "key: " : ph == 2 ? "iv: " : ph == 3 ? "enc: " : "dec: ") ;
for (byte i = 0 ; i < (blocks-1)*N_BLOCK ; i++)
{
byte val = ph == 0 ? plain[i] : ph == 1 ? key[i] : ph == 2 ? my_iv[i] : ph == 3 ? cipher[i] : check[i] ;
Serial.print (val>>4, HEX) ; Serial.print (val&15, HEX) ;
}
Serial.println () ;
}
}
Java
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.*;
public class Encryptor3 {
public static byte[] hexStringToByteArray(String hexInputString){
byte[] bts = new byte[hexInputString.length() / 2];
for (int i = 0; i < bts.length; i++) {
bts[i] = (byte) Integer.parseInt(hexInputString.substring(2*i, 2*i+2), 16);
}
return bts;
}
public static String byteArrayToString(byte[] byteArray) {
StringBuilder str = new StringBuilder();
for (int i = 0; i < byteArray.length; i++) {
str.append((char) byteArray[i]);
}
return str.toString();
}
public static String byteArrayToHexString(byte[] arg) {
int l = arg.length * 2;
return String.format("%0"+l+"x", new BigInteger(1, arg));
}
public static byte[] encrypt(byte[] key1, byte[] key2, byte[] value) {
try {
IvParameterSpec iv = new IvParameterSpec(key2);
SecretKeySpec skeySpec = new SecretKeySpec(key1, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value);
return encrypted;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] key1, byte[] key2, byte[] encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(key2);
SecretKeySpec skeySpec = new SecretKeySpec(key1, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(encrypted);
return original;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String toHex(String arg) {
int l = arg.length() * 2;
return String.format("%0"+l+"x", new BigInteger(1, arg.getBytes()));
}
public static String HexStringToString (String arg) {
StringBuilder output = new StringBuilder();
for (int i = 0; i < arg.length(); i+=2) {
String str = arg.substring(i, i+2);
output.append((char)Integer.parseInt(str, 16));
}
return output.toString();
}
public static void main(String[] args) {
// source: http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors#aes-cbc-128
String message = "6bc1bee22e409f96e93d7e117393172a"; // 16 byte = 128 bit key
//String message = toHex("Hello00000000000");
String key1 = "2b7e151628aed2a6abf7158809cf4f3c";
String iv = "000102030405060708090A0B0C0D0E0F";
String match = "7649abac8119b246cee98e9b12e9197d";
System.out.print("message (hex): "); System.out.println(message);
System.out.print("key (hex): "); System.out.println(key1);
System.out.print("iv (hex): "); System.out.println(iv);
System.out.print("match (hex): "); System.out.println(match);
System.out.println();
byte[] enc_message_ba = encrypt(hexStringToByteArray(key1), hexStringToByteArray(iv), hexStringToByteArray(message));
System.out.print("Encrypted (hex): "); System.out.println(byteArrayToHexString(enc_message_ba));
System.out.println();
byte[] dec_message_ba = decrypt(hexStringToByteArray(key1), hexStringToByteArray(iv), enc_message_ba);
System.out.print("Decrypted (hex): "); System.out.println(byteArrayToHexString(dec_message_ba));
}
}
Arduino output
Install AES library in Arduino and run as usual
Ready
N_BLOCK: 16
set_key 128 ->0 took 596us
encrypt ->0 took 1136us
decrypt ->0 took 1548us
plain: 6BC1BEE22E409F96E93D7E117393172A
key: 2B7E151628AED2A6ABF7158809CF4F3C
iv: 000102030405060708090A0B0C0D0E0F
enc: 7649ABAC8119B246CEE98E9B12E9197D
dec: 6BC1BEE22E409F96E93D7E117393172A
Java output
Put the code in Encryptor.java, then from command line
javac Encryptor.java
java Encryptor
message (hex): 6bc1bee22e409f96e93d7e117393172a
key (hex): 2b7e151628aed2a6abf7158809cf4f3c
iv (hex): 000102030405060708090A0B0C0D0E0F
match (hex): 7649abac8119b246cee98e9b12e9197d
Encrypted (hex): 7649abac8119b246cee98e9b12e9197d
Decrypted (hex): 6bc1bee22e409f96e93d7e117393172a

NFC Smartposter to Dial a number

i want to create a NFC SmartPoster which dial a number with Action Record type "act".
Can anyone tell how to get Action Record type "act" in android from packet and check whether packet contains Action Record type "act" or not.
Below is packet i have created.
/**
* Smart Poster containing a Telephone number and Action record type.
*/
public static final byte[] SMART_POSTER_Dial_Number =
new byte[] {
// SP type record
(byte) 0xd1, (byte) 0x02, (byte) 0x26, (byte) 0x53, (byte) 0x70,
// Call type record
(byte) 0xd1, (byte) 0x01, (byte) 0x0e, (byte) 0x55, (byte) 0x05, (byte) 0x2b,
(byte) 0x39, (byte) 0x31, (byte) 0x38, (byte) 0x38, (byte) 0x37, (byte) 0x32,
(byte) 0x37, (byte) 0x34, (byte) 0x33, (byte) 0x39, (byte) 0x33, (byte) 0x39,
// Action type record
(byte) 0x11, (byte) 0x03, (byte) 0x01, (byte) 0x61, (byte) 0x63, (byte) 0x74,
(byte) 0x00,
// Text type record with 'T'
(byte) 0x91, (byte) 0x01, (byte) 0x09, (byte) 0x54, (byte) 0x02, (byte) 'C',
(byte) 'a', (byte) 'l', (byte) 'l', (byte) 'i', (byte) 'n', (byte) 'g', (byte) '.'
};
Please help..
When you receive an NDEF message in your Activity via an ACTION_NDEF_DISCOVERED intent, you can parse and check the contents for a SmartPoster record with an embedded 'act' record as follows:
Intent intent = getIntent();
final Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage mesg = (NdefMessage) rawMsgs[0]; // in theory there can be more messages
// let's inspect the first record only
NdefRecord[] record = mesg.getRecords()[0];
byte[] type = record.getType();
// check if it is a SmartPoster
byte[] smartPoster = { 'S', 'p'};
if (Arrays.equals(smartPoster, type) {
byte[] payload = record.getPayload();
// try to parse the payload as NDEF message
NdefMessage n;
try {
n = new NdefMessage(payload);
} catch (FormatException e) {
return; // not an NDEF message, we're done
}
// try to find the 'act' record
NdefRecord[] recs = n.getRecords();
byte[] act = { 'a', 'c', 't' };
for (NdefRecord r : recs) {
if (Arrays.equals(act, r.getType()) {
... // found it; do your thing!
return;
}
}
}
return; // nothing found
BTW: You will find that there are a couple of format errors in the example message in your question: the first byte of the Uri record should be 0x81 and the first byte of the Text record should be 0x51.

Categories

Resources