I'm trying encrypt files and upload them to the cloud and decrypt them locally with Node.js v21.6.2. Unfortunately, I'm having trouble getting the decrypt to work. I'm able to generate a key and encrypt, but decryption fails on the iv which allegedly isn't an ArrayBuffer, but should be. Is there a better way to make sure the iv is a buffer so I can properly decrypt the file? I've noticed that the encrypted data object is undefined, I'm not sure if that's the problem. Anyone have any ideas as to what I'm doing wrong? Here's the error I get:
node:internal/webidl:181
const err = new TypeError(message);
^
TypeError: Failed to execute 'decrypt' on 'SubtleCrypto': 3rd argument is not instance of ArrayBuffer, Buffer, TypedArray, or DataView.
at codedTypeError (node:internal/webidl:181:15)
at makeException (node:internal/webidl:190:10)
at converters.BufferSource (node:internal/crypto/webidl:206:11)
at SubtleCrypto.decrypt (node:internal/crypto/webcrypto:961:28)
at decrypt (/Users/me/code/encryptiontest/aes/aes.js:31:43)
at testEncryptionDecryption (/Users/me/code/encryptiontest/aes/aes.js:52:29) {
code: 'ERR_INVALID_ARG_TYPE'
}
Here is my code:
const { subtle } = globalThis.crypto;
async function generateAesKey(length = 256) {
const key = await subtle.generateKey({
name: 'AES-CBC',
length,
}, true, ['encrypt', 'decrypt']);
return key;
}
async function aesEncrypt(plaintext) {
const ec = new TextEncoder();
const key = await generateAesKey();
const iv = crypto.getRandomValues(new Uint8Array(16));
const ciphertext = await crypto.subtle.encrypt({
name: 'AES-CBC',
iv,
}, key, ec.encode(plaintext));
return {
key,
iv,
ciphertext,
};
}
async function decrypt(ciphertext, key, iv) {
const dec = new TextDecoder();
const plaintext = await crypto.subtle.decrypt({
name: 'AES-CBC',
iv,
}, key, ciphertext);
return dec.decode(plaintext);
}
// takes Uint8Array and returns ArrayBuffer
function typedArrayToBuffer(array) {
return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset);
}
async function testEncryptionDecryption() {
const data = "Hello, world!";
console.log("ENCRYPTING DATA");
const { key, iv, encrypted } = await aesEncrypt(data);
console.log(iv);
console.log("DECRYPTING DATA");
console.log(typeof iv.buffer);
const decrypted = await decrypt(encrypted, key, typedArrayToBuffer(iv));
console.log("Original:", data);
console.log("Decrypted:", decrypted);
}
testEncryptionDecryption();
I tried using iv, iv.buffer, and iv.buffer.slice in the decrypt, but instead I get an error about iv not being an instance of ArrayBuffer, Buffer, TypedArray, or DataView