Skip to content

Instantly share code, notes, and snippets.

@JosePedroDias
Last active October 31, 2023 11:55
Show Gist options
  • Save JosePedroDias/961d09c799439f34ef692b0b78eccafe to your computer and use it in GitHub Desktop.
Save JosePedroDias/961d09c799439f34ef692b0b78eccafe to your computer and use it in GitHub Desktop.
AES CBC friendly impl using subtle crypto for both nodejs and the browser
// import { webcrypto as crypto } from 'node:crypto'; // NODE
// eslint-disable-next-line no-undef
const crypto = globalThis.crypto; // BROWSER
const subtle = crypto.subtle;
const ALGO = 'AES-CBC';
// HELPER FUNCTIONS
const toHex = (bytes: Uint8Array): string =>
Array.from(bytes).reduce(
(str, byte) => str + byte.toString(16).padStart(2, '0'),
'',
);
const fromHex = (hexString): Uint8Array =>
Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
const stringToUint8Arr = (str: string): Uint8Array =>
new Uint8Array(str.split('').map((x) => x.charCodeAt(0)));
// LOWER LEVEL FUNCTIONS
function genIv(): Uint8Array {
return crypto.getRandomValues(new Uint8Array(16));
}
export async function genKey(secretString: string): Promise<CryptoKey> {
const rawKey = stringToUint8Arr(secretString);
return await subtle.importKey('raw', rawKey, ALGO, true, [
'encrypt',
'decrypt',
]);
}
async function _encode(
iv: Uint8Array,
key: CryptoKey,
message: string,
): Promise<Uint8Array> {
const enc = new TextEncoder();
const encoded = enc.encode(message);
return await subtle
.encrypt(
{
name: ALGO,
iv,
},
key,
encoded,
)
.then((arr) => new Uint8Array(arr));
}
async function _decode(iv: Uint8Array, key: CryptoKey, cypherText: Uint8Array) {
const decrypted = await subtle.decrypt(
{
name: ALGO,
iv,
},
key,
cypherText,
);
const dec = new TextDecoder();
const decoded = dec.decode(decrypted);
return decoded;
}
// HIGHER LEVEL FUNCTIONS
export async function encode(key: CryptoKey, message: string): Promise<string> {
const iv = genIv();
const o1 = await _encode(iv, key, message);
return `${toHex(iv)}:${toHex(o1)}`;
}
export async function decode(
key: CryptoKey,
cypherPair: string,
): Promise<string> {
const [ivHex, cypherHex] = cypherPair.split(':');
const iv = fromHex(ivHex);
const cypherText = fromHex(cypherHex);
return await _decode(iv, key, cypherText);
}
import { genKey, encode, decode } from './AesCbc';
async function example() {
const SECRET = 'random password 32 bytes length.';
const MESSAGE = 'hello world';
const key = await genKey(SECRET);
const o1 = await encode(key, MESSAGE);
console.log('o1', o1);
const o2 = await decode(key, o1);
console.log('o2', o2);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment