В настоящее время я работаю над проектом Arduino / ESP8266, который требует шифрования / дешифрования определенных данных (небольших строк) AES128, библиотека поддерживает AES256, но сейчас я получу 128 рабочих. ESP будет взаимодействовать с приложением Electron / React, которое будет расшифровывать зашифрованные полезные данные, передаваемые из ESP. Я проанализировал данные в шестнадцатеричные буферы и разделил их соответствующим образом (iv, ключ шифрования, hmac, зашифрованные данные).
У меня возникли две проблемы:
1- Мой HMAC никогда не соответствует оригиналу
2- Я не могу расшифровать зашифрованные данные, всегда пустая строка «»
У меня такое ощущение, что обе эти проблемы связаны с форматом содержимого (шестнадцатеричным), поскольку мне удалось зашифровать «hello123» и снова расшифровать его с текущей конфигурацией.
Кто-нибудь может увидеть, что я делаю неправильно?
Код расшифровки Javascript:
// Libraries
import CryptoJS from 'crypto-js';
import { slice } from 'lodash';
testDecryption() {
const input_aesMode128 = '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8F,19,E3,E6,AE,26,93,9F,71,EB,3E,44,17,7C,CE,53,79,15,34,4D,70,C8,6A,E7,7,4B,4F,F6,88,44,ED,E4,2A,40,17,2E,91,DE,D9,3,A2,1B,EF,41,BB,FA,DD,E3,96,1,34,36,E0,60,48,F1,BB,C8,91,A3,4B,91,96,E5';
const input = input_aesMode128;
const parsed = this.splitStringAndParse({ payload: input, delimiter: ',', parseMode: "DECIMAL" });
const aes128hmacPayload = Buffer.from(parsed);
// Extract, parse and store specific data
const ekParsed = this.splitStringAndParse({ payload: '1C,3E,4B,AF,13,4A,89,C3,F3,87,4F,BC,D7,F3,31,31', delimiter: ',', parseMode: 'DECIMAL' });
const ivParsed = this.extractValue({ payload: aes128hmacPayload, startIndex: 0, lengthToExtract: 16 });
const encryptedParsed = this.extractValue({ payload: aes128hmacPayload, startIndex: 16, lengthToExtract: aes128hmacPayload.length - 32 - 16 });
const hmacParsed = this.extractValue({ payload: aes128hmacPayload, startIndex: aes128hmacPayload.length - 32, lengthToExtract: 32 });
// Convert specific data into buffers
const aes128hmacStr = aes128hmacPayload.toString('hex');
const ek = Buffer.from(ekParsed);
const iv = Buffer.from(ivParsed);
const encrypted = Buffer.from(encryptedParsed);
const hmac = Buffer.from(hmacParsed);
// SHA256 of Encryption Key
const shaOfKey = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(ek.toString('hex'))).toString()
console.log({
'Parsed String As Decimal': parsed,
'AES Payload Buffer': aes128hmacPayload,
'AES Payload String': aes128hmacStr,
'Encryption Key': ek.toString('hex'),
'Initialisation Vector': iv.toString('hex'),
'Encrypted Data': encrypted.toString('hex'),
'HMAC': hmac.toString('hex'),
'Encryption Key - keyHash (actually used as the key)': shaOfKey
});
// Calculate HMAC to verify the original
const calculatedHMAC = CryptoJS.HmacSHA256(iv.toString('hex') + encrypted.toString('hex'), shaOfKey).toString();
if (calculatedHMAC === hmac.toString('hex')) {
console.log('HMAC MATCHED!', { original: hmac.toString('hex'), computed: calculatedHMAC });
} else {
console.log('HMAC DOES NOT MATCH!', { original: hmac.toString('hex'), computed: calculatedHMAC });
}
const ivAsHex = CryptoJS.enc.Hex.parse(iv.toString('hex'));
const keyAsHex = CryptoJS.enc.Hex.parse(shaOfKey.toString('hex'));
const options = {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: ivAsHex
};
// const encryptedPayload = CryptoJS.AES.encrypt("hello123", keyAsHex, options);
// console.log({ encrypted: encryptedPayload });
const decryptedPayload = CryptoJS.AES.decrypt(encrypted.toString('hex'), keyAsHex, options);
console.log({ decrypted: decryptedPayload.toString(CryptoJS.enc.Hex) });
},
splitStringAndParse({ payload, delimiter, parseMode }) {
if (parseMode !== 'HEX' && parseMode !== 'DECIMAL' && parseMode !== 'ASCII') {
return new Error('parseMode must either be HEX, ASCII or DECIMAL');
}
const tmp = payload.split(delimiter);
const final = [];
tmp.map(curr => {
const currTmp = parseInt(curr, 16);
switch (parseMode) {
case 'HEX': {
final.push(currTmp.toString(16));
break;
}
case 'DECIMAL': {
final.push(parseInt(currTmp, 10));
break;
}
case 'ASCII': {
final.push(String.fromCharCode(currTmp));
break;
}
default: {
final.push(currTmp);
break;
}
}
});
// return Buffer.from(final);
return final;
},
convertBufferToString({ payload, parseMode }) {
if (parseMode !== 'HEX' && parseMode !== 'DECIMAL' && parseMode !== 'ASCII') {
return new Error('parseMode must either be HEX, ASCII or DECIMAL');
}
let final = '';
payload.map(curr => {
const currTmp = parseInt(curr, 16);
switch (parseMode) {
case 'HEX': {
if (curr < 10) {
final = final.concat('0' + currTmp.toString(16));
} else {
final = final.concat(currTmp.toString(16));
}
break;
}
case 'DECIMAL': {
final = final.concat(currTmp.toString());
break;
}
case 'ASCII': {
final = final.concat(String.fromCharCode(currTmp));
break;
}
default: {
final = final.concat(currTmp.toString());
break;
}
}
});
return final;
},
extractValue({ payload, startIndex, lengthToExtract }) {
// return payload.substring(startIndex, (startIndex + lengthToExtract));
return slice(payload, startIndex, (startIndex + lengthToExtract));
}
ESP8266 Код эскиза Arduino:
#include <Crypto.h> // AES 128 CBC with pkcs7, RNG, SHA256 and SHA256HMAC
#include <base64.hpp> // Base64 encode and decode without line breaks https://github.com/Densaugeo/base64_arduino
/*
* AES encryption with SHA256HMAC on an ESP8266
*/
#define HMAC_KEY_LENGTH 16
#define AES_KEY_LENGTH 16
uint8_t* keyEncrypt;
uint8_t* keyHmac;
uint8_t keyHash[SHA256_SIZE];
uint8_t key[AES_KEY_LENGTH] = { 0x1C,0x3E,0x4B,0xAF,0x13,0x4A,0x89,0xC3,0xF3,0x87,0x4F,0xBC,0xD7,0xF3, 0x31, 0x31 };
uint8_t iv[AES_KEY_LENGTH] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
SHA256 sha256;
// prints given block of given length in HEX
void printBlock(uint8_t* block, int length) {
Serial.print(" { ");
for (int i=0; i<length; i++) {
Serial.print(block[i], HEX);
Serial.print(",");
}
Serial.println(" } ");
}
void setup() {
Serial.begin(115200);
while (!Serial) {
; //wait
}
Serial.printf("\nAES Mode: 128-Bit + SHA256 + HMAC");
Serial.printf("\n\n");
// get SHA-256 hash of our secret key to create 256 bits of "key material"
sha256.doUpdate(key, AES_KEY_LENGTH);
sha256.doFinal(keyHash);
// keyEncrypt is a pointer pointing to the first 128 bits bits of "key material" stored in keyHash
// keyHmac is a pointer poinging to the second 128 bits of "key material" stored in keyHashMAC
keyEncrypt = keyHash;
keyHmac = keyHash + AES_KEY_LENGTH;
Serial.printf("\nkey - (%d bytes)\n", AES_KEY_LENGTH);
printBlock(key, AES_KEY_LENGTH);
Serial.printf("\nkeyHash - sha256 of key\n");
printBlock(keyHash, SHA256_SIZE);
Serial.printf("\nkeyEncrypt - keyHash \n");
printBlock(keyEncrypt, SHA256_SIZE);
Serial.printf("\n");
// maximum packet length for this example is 350 bytes. A crash occurs on larger packets.
char packet[] = "0123456789abcdef\0";
// char packet[] = "1234567890 abcdefghijklmnopqrstuvwxyz !@#$%^&*()_+{|\\:\"<>?-=[];'./,";
// char packet[] = "0123456789abcdef";
Serial.println("\n*** Starting Encryption ***");
int packetSize = strlen(packet);
Serial.printf("\n=> Packet (%d bytes):\n", packetSize);
Serial.println(packet);
Serial.print("\n=> Packet HEX");
printBlock((uint8_t*)packet, packetSize+1); //+1 to add null termination
// random initialization vector
// RNG::fill(iv, AES_KEY_LENGTH);
Serial.printf("\n=> Random IV (%d bytes)", AES_KEY_LENGTH);
printBlock(iv, AES_KEY_LENGTH);
AES aes(keyEncrypt, iv, AES::AES_MODE_128, AES::CIPHER_ENCRYPT);
// create buffer for final message which will contain IV, encrypted message, and HMAC
int encryptedSize = aes.calcSizeAndPad(packetSize);
int ivEncryptedSize = encryptedSize + AES_KEY_LENGTH;
int ivEncryptedHmacSize = ivEncryptedSize + SHA256HMAC_SIZE;
uint8_t ivEncryptedHmac[ivEncryptedHmacSize];
// copy IV to our final message buffer
memcpy(ivEncryptedHmac, iv, AES_KEY_LENGTH);
// encrypted is a pointer that points to the encypted messages position in our final message buffer
uint8_t* encrypted = ivEncryptedHmac + AES_KEY_LENGTH;
// AES 128 CBC and pkcs7 padding
aes.process((uint8_t*)packet, encrypted, packetSize);
Serial.printf("\n=> Encrypted (%d bytes)\n", encryptedSize);
printBlock(encrypted, encryptedSize);
// computedHmac is a pointer which points to the HMAC position in our final message buffer
uint8_t* computedHmac = encrypted + encryptedSize;
// compute HMAC/SHA-256 with keyHmac
SHA256HMAC hmac(keyHmac, HMAC_KEY_LENGTH);
Serial.printf("\n=> ivEncryptedHmac (size %d bytes)\n", ivEncryptedHmacSize);
printBlock(ivEncryptedHmac, ivEncryptedHmacSize);
hmac.doUpdate(ivEncryptedHmac, ivEncryptedSize-32);
hmac.doFinal(computedHmac);
Serial.printf("\n=> Computed HMAC (%d bytes)", SHA256HMAC_SIZE);
printBlock(computedHmac, SHA256HMAC_SIZE);
Serial.printf("\n=> IV | encrypted | HMAC (%d bytes)", ivEncryptedHmacSize);
printBlock(ivEncryptedHmac, ivEncryptedHmacSize);
// base64 encode
int encodedSize = encode_base64_length(ivEncryptedHmacSize); // get size needed for base64 encoded output
uint8_t encoded[encodedSize];
encode_base64(ivEncryptedHmac, ivEncryptedHmacSize, encoded);
Serial.printf("\n=> Base64 encoded to %d bytes\n", encodedSize);
printBlock(encoded, encodedSize);
}
void loop() {
delay(1);
}