3

I'm using an react native app to communicate with my miband 3, and part of its authentication process is encrypt a array of bytes using AES/ECB/NoPadding algorithm. I'm currently using this code as reference

Since I'm new to iot and encryption, I suppose that the lest step of its authentication is incorrect because and after writing to the miband 3 the encrypted byte array, I'm not getting authenticated properly.

Currently the last step looks like this:

const base_key = [0x01,0x23,0x45,0x67,0x89,0x01,0x22,0x23,0x34,0x45,0x56,0x67,0x78,0x89,0x90,0x02]
console.warn('Getting random number...')
const random_number = bytesToString(notification.filter((byte, index) => index > 3))

// Encrypting the key
const key = bytesToString(base_key)
const cipher = CryptoJS.AES.encrypt(random_number,key).toString()

// Step 5) Sending encrypted random number
console.warn('sending encrypted random number...')
const request_send_encrypted_key = [0x03,0x00, ...stringToBytes(cipher)]
await BleManager.writeWithoutResponse(miband3, service_uuid, characteristic_uuid, request_send_encrypted_key)

Notification is filtered because the first 3 bytes are used to tell which notification is happening, so I do not need them.

I must send the following to the miband 3 in order to authenticate properly:

const byteArrayToSend = [0x03,0x00, ...encryptedByteArray]

encryptedByteArray being my random number returned from miband notification (without the first 3 bytes) and properly encrypted .

I'm using 'crypto-js' and 'react-native-ble-manager' in the code.

How do I properly encrypt this bytearray using AES algorithm in order to send it?

Ry-
  • 218,210
  • 55
  • 464
  • 476
Kanagawa Marcos
  • 100
  • 1
  • 9

1 Answers1

5

The following must be taken into account for AES-encryption using CryptoJS:

  • In the posted code, keys and data to be encrypted (random_number) are obviously specified as arrays. CryptoJS uses WordArray-objects, so a conversion is necessary. WordArray-objects can easily be converted over hexadecimal strings into arrays and vice versa using these functions and the CryptoJS-Encoders. Another possibility is the direct conversion using these functions (not tested).

  • Since the arrays contain arbitrary byte-sequences (in the sense that they generally do not correspond to any readable characters), only suitable encodings (Base64 or hexadecimal) may be used here. The methods bytesToString and stringToBytes were not posted, so it is unclear if there is a problem. CryptoJS allows to pass the data as string or WordArray, where the latter is used in the following.

  • If the second parameter in CryptoJS.AES.encrypt is passed as a string, it is interpreted as a passphrase from which the actual key is generated according to a defined algorithm, here. If the second parameter is to be interpreted as a key (and this seems to be the case in the reference code), then it must be passed as a WordArray.

  • AES/ECB/NoPadding is used for encryption according to the linked Medium-article. The disabling of padding is only possible because the data to be encrypted (random_number with a length of 16 bytes according to the article) corresponds to an integer multiple of the AES block size (16 bytes) (if this were not the case, padding would have to be mandatory).

  • The result of the encryption is a CipherParams-object whose ciphertext-property contains the encrypted data as a WordArray, here.

Taking these points into account, encryption can be performed as follows:

var CryptoJS = require("crypto-js");

// Key and random number as arrays, e.g.:
var random_numberBA = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f];
var keyBA = [0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x22, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x90, 0x02];

// Conversion to WordArrays
var random_numberWA = CryptoJS.enc.Hex.parse(bytesToHex(random_numberBA));
var keyWA = CryptoJS.enc.Hex.parse(bytesToHex(keyBA));

// Encryption
// - random_number as WordArray
// - Key as WordArray
// - ECB-Mode (default: CBC), No-Padding (default: Pkcs7-Padding)
var encryptedCP = CryptoJS.AES.encrypt(random_numberWA, keyWA, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding });

// Encrypted data as WordArray
var encryptedWA = encryptedCP.ciphertext;

// Conversion to array
var encryptedBA = hexToBytes(CryptoJS.enc.Hex.stringify(encryptedWA));

// Consume encryptedBA 
// ...

// Helper: from https://stackoverflow.com/a/34356351/9014097
function hexToBytes(hex) {
    for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
    return bytes;
}

function bytesToHex(bytes) {
    for (var hex = [], i = 0; i < bytes.length; i++) {
        var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
        hex.push((current >>> 4).toString(16));
        hex.push((current & 0xF).toString(16));
    }
    return hex.join("");
}

The result can be verified e.g. here.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • 1
    Great tip. Could you put an example how we can decrypt using these method? should we use encryptedCP result or encryptedBA in decrypt method? – mastervv Nov 27 '19 at 10:54
  • It is difficult to give an answer to a question with so little information (what is your decryption-library/method, what types of data do you use, how were the data encrypted? etc.). [Here](https://cryptojs.gitbook.io/docs/) you can find a good explanation of CryptoJS (if it is to be used at all). Try to implement the decryption-part by yourself and when you get stuck, post a question with your code and the necessary informations on SO. Thanks. – Topaco Nov 27 '19 at 13:59