Skip to content

Instantly share code, notes, and snippets.

@arvati
Last active August 9, 2024 00:25
Show Gist options
  • Save arvati/9a06e80e421c62dee530a428a9817f10 to your computer and use it in GitHub Desktop.
Save arvati/9a06e80e421c62dee530a428a9817f10 to your computer and use it in GitHub Desktop.
This module provides BCrypt hashing support for deno and the web by providing simple bindings using bcrypt compiled to webassembly.
//https://jsr.io/@blackberry/bcrypt
import { hash, verify } from "jsr:@blackberry/bcrypt";
const encoder = new TextEncoder();
const password = encoder.encode("very-cool-password");
const salt = encoder.encode("salty salt woo!!");
const hashed = hash(password, salt);
console.log(hashed);
// $2b$12$a0DqbFiea0DqbA/1Z06fGOZcRMnat359MTbqezYI1qXvLuOL16Eve
const verified = verify(password, hashed);
console.log(verified);
// true
// https://github.com/JamesBroadberry/deno-bcrypt/issues/26#issuecomment-1268090590
const buff_to_base64 = (buff: number[] | Uint8Array) =>
btoa(String.fromCharCode.apply(null, buff));
const base64_to_buf = (b64: string) =>
Uint8Array.from(atob(b64), (c) => c.charCodeAt(null));
const enc = new TextEncoder();
const dec = new TextDecoder();
const getPasswordKey = async (password: any) =>
await window.crypto.subtle.importKey(
"raw",
enc.encode(password),
"PBKDF2",
false,
['deriveBits', "deriveKey"]
);
const deriveKey = async (passwordKey: any, salt: any, keyUsage: any[]) =>
await window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: salt,
iterations: 250000,
hash: "SHA-256",
},
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
keyUsage
);
export async function encryptData(secretData: string | undefined, password: any) {
try {
const salt = window.crypto.getRandomValues(new Uint8Array(16));
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const passwordKey = await getPasswordKey(password);
const aesKey = await deriveKey(passwordKey, salt, ["encrypt"]);
const encryptedContent = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv,
},
aesKey,
enc.encode(secretData)
);
const encryptedContentArr = new Uint8Array(encryptedContent);
let buff = new Uint8Array(
salt.byteLength + iv.byteLength + encryptedContentArr.byteLength
);
buff.set(salt, 0);
buff.set(iv, salt.byteLength);
buff.set(encryptedContentArr, salt.byteLength + iv.byteLength);
const base64Buff = buff_to_base64(buff);
return base64Buff;
} catch (e) {
console.log(`Error - ${e}`);
return "";
}
}
export async function decryptData(
encryptedData: string,
password: string | undefined
) {
try {
const encryptedDataBuff = base64_to_buf(encryptedData);
const salt = encryptedDataBuff.slice(0, 16);
const iv = encryptedDataBuff.slice(16, 16 + 12);
const data = encryptedDataBuff.slice(16 + 12);
const passwordKey = await getPasswordKey(password);
const aesKey = await deriveKey(passwordKey, salt, ["decrypt"]);
const decryptedContent = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv,
},
aesKey,
data
);
return dec.decode(decryptedContent);
} catch (e) {
console.log(`Error - ${e}`);
return "";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment