Last active
October 31, 2023 11:55
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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