For the communication with a ACR1255U-J1 NFC Reader an authentication is needed. Connection is via bluetooth by using Hex-Strings.
These are my two methods for encryption and decryption:
encrypt(valueStringHex, keyStringHex) {
const CryptoJS = require('crypto-js');
const value = CryptoJS.enc.Hex.parse(valueStringHex);
const key = CryptoJS.enc.Hex.parse(keyStringHex);
const encryptedStringHex = CryptoJS.AES.encrypt(value, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding}).ciphertext.toString();
return encryptedStringHex;
}
decrypt(valueStringHex, keyStringHex) {
const CryptoJS = require('crypto-js');
const value = CryptoJS.enc.Hex.parse(valueStringHex);
const key = CryptoJS.enc.Hex.parse(keyStringHex);
const decryptedStringHex = CryptoJS.AES.decrypt({ciphertext: value}, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding});
return decryptedStringHex.toString();
}
With the reader comes a demo app from which I recorded the bluetooth traffic during the authentication. I checked this with Wireshark. Based on the communication seen in Wireshark I tried to proof my coding of the authentication process in my app.
ACR1255 documentation says:
In Step 1: I receive from the ACR reader a sequence of 16-byte random numbers encrypted using the Customer Master Key. I should decrypt it using the correct
Customer Master Key (41435231323535552d4a312041757468).
According to Wireshark this is what I received from the ACR1255 reader (key part in bold) in the first step: 83001500000021E1000045005ff58680541c5a5903f4833dfaa428bf1c0a
decrypt('5ff58680541c5a5903f4833dfaa428bf', '41435231323535552d4a312041757468')
=> Result is 6064a82b7edf62986b0a2ec79e922aad
In Step 2: According to the documentation I have to sent the following.
abAuthData[0:15] – 16 bytes of random number generated by me
abAuthData[16:31] – 16 bytes of decrypted random number received from ACR1255U-J1 (which would be the result from first step 6064a82b7edf62986b0a2ec79e922aad, I guess)
The overall 32-byte random numbers will be decrypted using the Customer Master Key and returned to ACR1255U-J1 reader.
According to Wireshark this is what the demo app now sends to the ACR1255 reader (key part in bold):
6B0025000000EAE0000046007088e66af57bf04e66a8b2e83614f288 c8ed5005b914b51e50285a93408e14922c0a
Of course, this is the already decrypted key. To proof my understanding of the workflow, I encrypted it and expected to see the result of step 1 as the second part of the key. But it is not.
encrypt('7088e66af57bf04e66a8b2e83614f288c8ed5005b914b51e50285a93408e1492', '41435231323535552d4a312041757468')
=> Result is 493aa0c5476f551d3b2bce664cfe4305*3b61bce6e4c0837be30453ddad165180*
What do I misunderstand in the workflow described in the documentation? Is one able to describe it differently to the documentation?
Documentation can also be found here. Page 32 to 35.
Ps. I know, the documentation says AES CBC mode. But a IV is not used and as far as I know the usage of ECB mode is than the same.
For the sake of completeness I would like to add the solution.
According to @Topaco's comment I was wrong with the ECB mode. I changed it to CBC and added the 0-IV and it is now working.
decrypt(valueStringHex, keyStringHex) {
const CryptoJS = require('crypto-js');
const value = CryptoJS.enc.Hex.parse(valueStringHex);
const key = CryptoJS.enc.Hex.parse(keyStringHex);
const ivvar = CryptoJS.enc.Hex.parse('00000000000000000000000000000000');
const decryptedStringHex = CryptoJS.AES.decrypt({ciphertext: value}, key, {iv: ivvar, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding});
return decryptedStringHex.toString();
}
// in Step 1:
// decrypt('5ff58680541c5a5903f4833dfaa428bf', '41435231323535552d4a312041757468')
// returns 6064a82b7edf62986b0a2ec79e922aad
encrypt(valueStringHex, keyStringHex) {
const CryptoJS = require('crypto-js');
const value = CryptoJS.enc.Hex.parse(valueStringHex);
const key = CryptoJS.enc.Hex.parse(keyStringHex);
const ivvar = CryptoJS.enc.Hex.parse('00000000000000000000000000000000');
const encryptedStringHex = CryptoJS.AES.encrypt(value, key, {iv: ivvar, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding}).ciphertext.toString();
return encryptedStringHex;
}
// in Step 2:
// encrypt('7088e66af57bf04e66a8b2e83614f288c8ed5005b914b51e50285a93408e1492', '41435231323535552d4a312041757468')
// this is the decrypted string sent to the reader, which has to be encrypted here to proof that the key contains the result of step 1 in second 16 Byte block
// returns => 493aa0c5476f551d3b2bce664cfe43056064a82b7edf62986b0a2ec79e922aad (which is the return of Step 1 (in the second half))