Skip to content

Instantly share code, notes, and snippets.

@mryhryki
Last active June 27, 2022 08:31
Show Gist options
  • Save mryhryki/d1df696da09560a226fb0c9bc50efe8f to your computer and use it in GitHub Desktop.
Save mryhryki/d1df696da09560a226fb0c9bc50efe8f to your computer and use it in GitHub Desktop.
AES CBC encrypt / decrypt example on Deno

AES CBC encrypt / decrypt example on Deno

How to Use

Encrypt data

$ cat {PLAIN_FILE} | deno run --allow-env=SECRET_KEY,IV ./script.ts encrypt > {ENCRIYPT_FILE}

Decrypt data

$ cat {ENCRIYPT_FILE} | deno run --allow-env=SECRET_KEY,IV ./script.ts decrypt > {PLAIN_FILE}

Test

$ bash ./test.sh
const TextCharacters = "0123456789abcdef";
const Radix = TextCharacters.length;
const BufferSize = 1024 ** 3; // 1MB
const SecretKeyLength = 32;
const IvSize = 16;
const checkBuf = (buf: Uint8Array): Uint8Array => {
if (buf.length <= 0) {
throw new Error(`ERROR: Data is empty`);
} else if (buf.length > BufferSize) {
throw new Error(`ERROR: Data size too large. Max: ${BufferSize} bytes`);
}
return buf;
};
const convBinToText = (arr: Uint8Array): string =>
Array.from(arr)
.map((n: number): string =>
[TextCharacters.at(Math.floor(n / Radix)), TextCharacters.at(n % Radix)]
.join(""),
)
.join("");
const convTextToBin = (text: string): Uint8Array => {
const list: number[] = [];
for (let p = 0; p < text.length; p += 2) {
list.push(parseInt(text.substring(p, p + 2), Radix));
}
return new Uint8Array(list);
};
const getSecretKey = async (): Promise<CryptoKey> => {
const secret = Deno.env.get("SECRET_KEY");
if (secret == null) throw new Error("SECRET_KEY value not exists");
if (secret.length !== SecretKeyLength * 2) throw new Error(`SECRET_KEY length must be ${SecretKeyLength * 2}.`)
const rawKey = convTextToBin(secret);
if (rawKey.length !== SecretKeyLength) throw new Error(`SECRET_KEY binary length must be ${SecretKeyLength}.`)
return await crypto.subtle.importKey("raw", rawKey, "AES-CBC", true, [
"encrypt",
"decrypt",
]);
};
const encrypt = async (secretKey: CryptoKey): Promise<void> => {
let plainBuf = new Uint8Array(BufferSize + 1);
const readBytes = await Deno.stdin.read(plainBuf);
plainBuf = checkBuf(plainBuf.slice(0, readBytes ?? 0));
const ivByEnvVar = Deno.env.get("IV");
const iv = ivByEnvVar != null ? convTextToBin(ivByEnvVar) : window.crypto.getRandomValues(new Uint8Array(16));
const encryptedBuf = checkBuf(
new Uint8Array(
await crypto.subtle.encrypt(
{ name: "AES-CBC", iv },
secretKey,
plainBuf,
),
),
);
const mergeBuf = checkBuf(new Uint8Array(iv.length + encryptedBuf.length));
mergeBuf.set(iv, 0);
mergeBuf.set(encryptedBuf, iv.length);
const encryptedText = convBinToText(mergeBuf);
console.log(encryptedText);
};
const decrypt = async (secretKey: CryptoKey): Promise<void> => {
let encryptedBuf = new Uint8Array(BufferSize + 1);
const readBytes = await Deno.stdin.read(encryptedBuf);
encryptedBuf = convTextToBin(new TextDecoder().decode(encryptedBuf.slice(0, readBytes ?? 0)));
const iv = encryptedBuf.slice(0, IvSize);
encryptedBuf = checkBuf(encryptedBuf.slice(IvSize, IvSize + (readBytes ?? 0)));
const plainBuf = await window.crypto.subtle.decrypt(
{ name: "AES-CBC", iv },
secretKey,
encryptedBuf,
);
console.log(new TextDecoder().decode(new Uint8Array(plainBuf)));
};
const command = Deno.args[0] ?? "(empty)";
if (command === "encrypt") {
await encrypt(await getSecretKey());
} else if (command === "decrypt") {
await decrypt(await getSecretKey());
} else {
throw new Error(`Unknown command: ${Deno.args[0]}`);
}
#!/usr/bin/env bash
# These are TEST values. DON'T USE in production encryption.
export SECRET_KEY="e06c1543168a5d746fdc9b021dc8c5f8a5c901bbbc05012013dbeb9728f9e184"
export IV="df862bac4b7e81dfa11394ec61a1938b" # Optional
echo "SECRET_KEY: ${SECRET_KEY}"
echo "IV : ${IV}"
PLAIN="69FAFBC0E42E7E206C1E3"
echo "PLAIN : ${PLAIN}"
ENCRYPTED="$(printf "${PLAIN}" | deno run --allow-env=SECRET_KEY,IV ./script.ts encrypt)"
echo "ENCRYPTED : ${ENCRYPTED}"
DECRYPTED="$(printf "${ENCRYPTED}" | deno run --allow-env=SECRET_KEY,IV ./script.ts decrypt)"
echo "DECRYPTED : ${DECRYPTED}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment