Search code examples
authenticationencryptionacr

Authentication to ACR1255U-J1 reader via bluetooth


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.


Solution

  • 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))