-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Sometimes we'll want to encrypt data before inserting it into Postgres.
Other times we'll want to be able to decrypt data outside of Postgres.
Sometimes we won't want IV because we want the "known text attack" - we want to search.
This is all perfectly doable.
Postgres' encrypt functions wrap OpenSSL's envelope functions for which the NULL IV is the same as a full IV with all 0s.
Here's a simple test to demonstrate this with webcrypto which works with the examples linked to in #10 and https://github.com/coolaj86/home-sweet-home/pull/4/commits.
ef9e1219cfdea4f63f6267d59a4bbc5f7906d995b5a1330d3889b6c5f1caea52
node ./scripts/pg-encrypt.js"use strict";
let ivNull = new Uint8Array(16); // 16 zeros is the same as a 0-length `null` iv in C
/** @typedef {String} Hex */
/**
* Encrypts data using AES-CBC with a 128-bit key.
* @param {String} data - The plaintext data to encrypt.
* @param {Uint8Array} key - The 128-bit encryption key (16 bytes).
* @returns {Promise<Hex>} - A promise that resolves to the base64-encoded encrypted data.
* @throws {Error} If the key is not 128 bits (16 bytes).
*/
async function pgEncryptAesCbc128(data, key) {
if (key.length !== 16) {
throw new Error("key must be 128 bits (16 bytes)");
}
let cryptoKey = await crypto.subtle.importKey(
"raw",
key,
{ name: "AES-CBC" },
false,
["encrypt"],
);
let bytes = new TextEncoder().encode(data);
let encryptedAb = await crypto.subtle.encrypt(
{ name: "AES-CBC", iv: ivNull },
cryptoKey,
bytes,
);
let encryptedBytes = new Uint8Array(encryptedAb);
return encryptedBytes;
}
function bytesToHex(bytes) {
let hs = [];
for (let b of bytes) {
let h = b.toString(16);
h = h.padStart(2, "0");
hs.push(h);
}
let hex = hs.join("");
return hex;
}
function hexToBytes(hex) {
let bufLen = hex.length / 2;
let bytes = new Uint8Array(bufLen);
let i = 0;
let index = 0;
let lastIndex = hex.length - 2;
for (;;) {
if (i > lastIndex) {
break;
}
let h = hex.slice(i, i + 2);
let b = parseInt(h, 16);
bytes[index] = b;
i += 2;
index += 1;
}
return bytes;
}
async function main() {
let keyBytes = hexToBytes("deadbeefbadc0ffee0ddf00dcafebabe");
let data = "sensitive data (raw)";
const encryptedBytes = await pgEncryptAesCbc128(data, keyBytes);
let hex = bytesToHex(encryptedBytes);
console.log(hex);
}
main();I'm not 100% sure, but I believe the NULL IV and all-0s IV work the same because the IV is simply preloading the first block (usually with random data) and I believe that matrix won't change from its initial state if the first block is all 0s (and perhaps any block of all 0s is an identity that produces the same block as the prior block).