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
Related
As part of a project, I am supposed to demonstrate 128-bit AES encryption in Arduino. Unfortunately, there is not much information on how to implement this in Arduino compared to for example, Android, which gives me my desired encrypted output.
Here is the sample code (https://gist.github.com/EC12-Code/73b19a98c759c777e303a75e94d6ed21) which I have referred to to implement AES, unfortunately when I compare the encrypted output to online tools, the encrypted output for Arduino is different but for Android it is the same. Somehow, the Arduino code still manages to give the desired decrypted output however, which confuses me. Is it a format issue? I am a newbie at this and I have no idea what is causing this difference, shouldn't the same key and IV used give the same encrypted output anyways?
Code on Arduino's side (with ref to the link above):
#include <string.h>
#include <stdio.h>
#include <hwcrypto/aes.h>
#include <stdlib.h>
#include <base64.h>
static inline int32_t _getCycleCount(void) {
int32_t ccount;
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
return ccount;
}
char plaintext[16384];
char encrypted[16384];
int encodetest()
{
//uint8_t key[32];
//uint8_t iv[16];
unsigned char iv[16] = {0x57, 0x6E, 0x5A, 0x72, 0x34, 0x75, 0x37, 0x78, 0x21, 0x7A, 0x25, 0x43, 0x2A, 0x46, 0x2D, 0x4A};
unsigned char key[32] ={0x66 ,0x54 ,0x6A ,0x57 ,0x6E ,0x5A ,0x72 ,0x34 ,0x75 ,0x37 ,0x78 ,0x21 ,0x41 ,0x25 ,0x44 ,0x2A ,0x47 ,0x2D ,0x4A ,0x61 ,0x4E ,0x64 ,0x52 ,0x67 ,0x55 ,0x6B ,0x58 ,0x70 ,0x32 ,0x73 ,0x35 ,0x76};
strcpy( plaintext, "Hello" );
Serial.printf( "\n================================Encode================================\nIV HEX:" );
for(int d = 0; d < sizeof(iv); d++) {
Serial.printf( "%02x", iv[d] );
}
Serial.printf( "\nKey HEX:" );
for(int f = 0; f < sizeof(key); f++) {
Serial.printf( "%02x", key[f] );
}
Serial.printf("\nWill Encode \n%s",plaintext);
//Just FYI - you must be encrypting/decrypting data that is in BLOCKSIZE chunks!!!
esp_aes_context ctx;
esp_aes_init( &ctx );
esp_aes_setkey( &ctx, key, 256 );
int32_t start = _getCycleCount();
esp_aes_crypt_cbc( &ctx, ESP_AES_ENCRYPT, sizeof(plaintext), iv, (uint8_t*)plaintext, (uint8_t*)encrypted );
int32_t end = _getCycleCount();
float enctime = (end-start)/240.0;
Serial.printf( "Encryption time: %.2fus (%f MB/s)\n", enctime, (sizeof(plaintext)*1.0)/enctime );
//See encrypted payload, and wipe out plaintext.
memset( plaintext, 0, sizeof( plaintext ) );
int i;
Serial.printf( "Hex" );
for( i = 0; i < 128; i++ )
{
if (i % 8 == 0) {Serial.printf( " " );};
Serial.printf( "%02x", encrypted[i] );
}
Serial.printf( "\n================================Decode================================ \n" );
//Must reset IV. Otherwise first block will fail
unsigned char iva[16] = {0x57, 0x6E, 0x5A, 0x72, 0x34, 0x75, 0x37, 0x78, 0x21, 0x7A, 0x25, 0x43, 0x2A, 0x46, 0x2D, 0x4A};
for(int c = 0; c < sizeof(iva); c++) {
Serial.printf( "%02x ", iva[c] );
}
memset( iv, 0, sizeof( iv ) );
//Use the ESP32 to decrypt the CBC block.
esp_aes_crypt_cbc( &ctx, ESP_AES_DECRYPT, sizeof(encrypted), iva, (uint8_t*)encrypted, (uint8_t*)plaintext );
Serial.printf( "\nHuman Readable\n" );
//Verify output
for( i = 0; i < sizeof( plaintext ); i++ )
{
// printf( "%c", (plaintext[i]>31)?plaintext[i]:' ' );
Serial.printf( "%c", plaintext[i]);
}
Serial.printf( "\n" );
esp_aes_free( &ctx );
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Execute Begin");
int a = encodetest();
}
Code on Android's side:
public class MainActivity extends AppCompatActivity {
EditText inputText, inputPassword;
TextView outputText;
Button encBtn, decBtn;
String outputString;
String AES = "AES";
private static final String SECRET_KEY = "fTjWnZr4u7x!A%D*G-JaNdRgUkXp2s5v";
private static final String SALT = "ssshhhhhhhhhhh!!!!";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inputText = findViewById(R.id.inputText);
inputPassword = findViewById(R.id.password);
outputText =findViewById(R.id.outputText);
encBtn = findViewById(R.id.encBtn);
decBtn = findViewById(R.id.decBtn);
encBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
outputString = encrypt(inputText.getText().toString());
outputText.setText(outputString);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public static String encrypt(String strToEncrypt) {
try {
byte[] iv = {0x57, 0x6E, 0x5A, 0x72, 0x34, 0x75, 0x37, 0x78, 0x21, 0x7A, 0x25, 0x43, 0x2A, 0x46, 0x2D, 0x4A};
IvParameterSpec ivspec = new IvParameterSpec(iv);
//SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
//KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT.getBytes(), 65536, 256);
//SecretKey tmp = factory.generateSecret(spec);
//SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
byte[] key = SECRET_KEY.getBytes("UTF-8");
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
byte[] encVal = cipher.doFinal(strToEncrypt.getBytes());
String encryptedValue = Base64.encodeToString(encVal, Base64.DEFAULT);
return encryptedValue;
} catch (Exception e) {
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
}
Plaintext: Hello
Encrypted output from Arduino:
7c6e13cc7e943313 ee111a1c90387c04 ac1fa7a7c491d53e be6eea1feaf01815 d7733389f23dd8f4 ed6dae3e78f5388e 1eda7bbbaf3bad5f 1b5b0d01281805d4 2c38139fcda0a05b 7eded259247e105b 16b228aacefc89ee 9dd2db2392537168 63adf145feb2b33a 287c5a5623212a0d c8760ed78ffc6508 a1e95d6235895ef5
Encrypted output from Android, which is the same as the one generated from online tools:
Any help would be much appreciated in helping me understanding why the Arduino output is different, thanks!
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();
}
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;
}
I am trying to write an android application to interface with iBeacons, but I need to find the start of the UUID / major / minor characteristics in the byte array. Through look at a couple of resources including this question, it seems like ALL iBeacons transmit some pattern in the byte array that is the same that identifies them as iBeacons.
I asked another question recently and got a useful answer, link here but 1) I haven't been able to test it's functionality (waiting on my device) and 2) I want to know how it is working.
So my questions: What is that pattern? Can I find it just by searching that array for the pattern? And is the UUID / Major / Minor always a predefined number of spots in the array from that identifying pattern?
Thanks!
So after looking around on a few different sites, I have found that 3 people list their patterns as
02 01 06 1A FF 4C 00 02 15
or
02 01 1A 1A FF 4C 00 02 15
which isn't super helpful, since the 4C 00 appears to just be the Apple identifier, which I'm assuming could switch based on the manufacturer (Estimote, GE, Apple, whatever). However, it appears that the
02 15
is static, so I'm using that. My solution for finding it basically is just searching the byte array that I get for the first occurrence of that sequence of 2 bytes, and starting right after that I grab the next 16 as the UUID, the next 2 after that as the Major, and the next 2 after that as the Minor. So to clarify with code:
//adPacket is an array I created to represent an iBeacon ad for testing
byte[] adPacket = {(byte) 0xd6, (byte) 0xbe, (byte) 0x89, (byte) 0x8e, 0x40, 0x24, 0x05, (byte) 0xa2,
0x17, 0x6e, 0x3d, 0x71, 0x02, 0x01, 0x06, 0x1A, (byte) 0xFF, 0x4C, 0x00, 0x02, 0x15, (byte) 0xe2,
(byte) 0xc5, 0x6d, (byte) 0xb5, (byte) 0xdf, (byte) 0xfb, 0x48, (byte) 0xd2, (byte) 0xb0, 0x60, (byte) 0xd0,
(byte) 0xf5, (byte) 0xa7, 0x10, (byte) 0x96, (byte) 0xe0, 0x00, 0x00, 0x00, 0x00, (byte) 0xc5, 0x52, (byte) 0xab, (byte) 0x8d, 0x38, (byte) 0xa5};
byte[] pattern = {0x02, 0x15};
int startIndex = findAdPacketEnd(adPacket, pattern) + 1;
if(startIndex == 0){ System.out.println("Pattern not found"); return;}
int endIndex = startIndex + 21;
ArrayList<Byte> tempArray = new ArrayList<Byte>();
for(int i = startIndex; i<endIndex; i++){
tempArray.add(adPacket[i]);
}
byte[] proxUUID = new byte[16];
for(int i = 0; i<16; i++){
proxUUID[i] = tempArray.get(i);
}
byte[] major = new byte[2];
major[0] = tempArray.get(16);
major[1] = tempArray.get(17);
byte[] minor = new byte[2];
minor[0] = tempArray.get(18);
minor[1] = tempArray.get(19);
...
where my findAdPacketEnd function is (not the most efficient but it works):
private static int findAdPacketEnd(byte[] adPacket, byte[] pattern){
//return -1 if pattern is too long
if(adPacket.length < pattern.length)
return -1;
//iterate over adPacket
for(int i = 0; i<adPacket.length - pattern.length; i++){
System.out.println("Searching Ad Packet");
//iterate over pattern
for(int j = 0; j<pattern.length; j++){
//compare wherever you are in the adpacket to the pattern, break if it doesn't match
if(adPacket[i+j] != pattern[j])
break;
// if you get to the end of the pattern and there wasn't a mismatch, return the index
if(j == pattern.length-1)
return i+pattern.length-1;
}
}
//pattern not found
return -1;
}
I'm trying to develop an Android app that does the same as a function written in C++ to communicate between the mobile phone and a remote ethernet interface (I don't know what it exactly is…).
I know that I have to pass to the interface 15 bytes, retrieved from hex values, to pilot some loads connected to the interface.
I've tried a few things, the closest to the correct solution seems to be this one:
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://192.168.2.100:1001");
try {
//String message "AA10A0F0F0FFFF0102FF00010091A5";
byte[] messaggioInBytes={(byte)10101010, //AA
(byte)00010000, //10
(byte)10100000, //A0
(byte)11110000, //F0
(byte)11110000, //F0
(byte)11111111, //FF
(byte)11111111, //FF
(byte)00000001, //01
(byte)00000010, //02
(byte)11111111, //FF
(byte)00000000, //00
(byte)00000001, //01
(byte)00000000, //00
(byte)10010001, //91
(byte)10100101}; //A5
httppost.setEntity(new ByteArrayEntity(messaggioInBytes));
// Execute HTTP Post Request
httpclient.execute(httppost);
} catch(Exception e){
Log.i("ConnectionHandler", "Error", e);
}
With this code the ethernet interface stops blinking and put itself to listening but it doesn't seem to receive the correct command string of bytes.
My doubts are connected to:
1. the format of the command to send
2. the type of connection to use
The only thing I know about the system is that it works well with this C++ function, so I would like to know if there is a way to translate it in Java code for Android instead of using Android NDK to import it.
The C++ function is the following:
void __fastcall TForm1::SendClick(TObject *Sender)
{
unsigned char Buffer[15];
AnsiString Line;
Buffer[0] = 0xAA;
...
Buffer[13] = 0x91;
Buffer[14] = 0xA5;
ClientSocket1->Socket->SendBuf(Buffer,15);
}
Is there anyone who can point me to the right direction?
Not sure if you have other issues but when you are trying to fill the array with bytes you are trying to get the byte value of the wrong numbers.
(byte) 00010000
is trying to stuff 10000 (ten thousand) into a byte.
Try putting in the hex values instead,
(byte)0xAA, (byte)0x10, etc.
Wrote a little test class just to verify that trying to cast a bit representation of a byte was the issue:
public class TestByteArray {
static byte[] myTestArray = {(byte) 0xAA,
(byte) 0x10,
(byte) 0xA0,
(byte) 0xF0,
(byte) 0xF0,
(byte) 0xFF,
(byte) 0xFF,
(byte) 0x01,
(byte) 0x02,
(byte) 0xFF,
(byte) 0x00,
(byte) 0x01,
(byte) 0x00,
(byte) 0x91,
(byte) 0xA5};
static byte[] myBitTestArray = {(byte)10101010, //AA
(byte)00010000, //10
(byte)10100000, //A0
(byte)11110000, //F0
(byte)11110000, //F0
(byte)11111111, //FF
(byte)11111111, //FF
(byte)00000001, //01
(byte)00000010, //02
(byte)11111111, //FF
(byte)00000000, //00
(byte)00000001, //01
(byte)00000000, //00
(byte)10010001, //91
(byte)10100101}; //A5
public static void main(String[] args) {
for(int i = 0;i < myTestArray.length; i++)
{
if(myTestArray[i] != myBitTestArray[i])
{
System.out.println("The value in myTestArray at index " + i + " is " + Integer.toHexString(myTestArray[i] & 0xFF).toUpperCase());
System.out.println("The value in myBitTestArray at index " + i + " is " + Integer.toHexString(myBitTestArray[i] & 0xFF).toUpperCase());
}
else
System.out.println("Arrays are equal at index " + i);
}
}
}