Skip to content

Instantly share code, notes, and snippets.

@bradymholt
Created May 10, 2024 17:15
Show Gist options
  • Save bradymholt/73a78c8fefab8481a9e9b8f160d53eb7 to your computer and use it in GitHub Desktop.
Save bradymholt/73a78c8fefab8481a9e9b8f160d53eb7 to your computer and use it in GitHub Desktop.
Web Crypto API Example
// Cryptography utilities that utilize the Web Crypto API
// By default, the AES-GCM algorithm is used for encryption and decryption.
const DEFAULT_ENCRYPTION_ALGORITHM = "AES-GCM";
export function isCryptoSupported() {
return window.crypto && window.crypto.subtle && window.crypto.subtle.importKey;
}
async function buildEncryptionKey(
secretToken: string,
salt: Uint8Array,
keyUsages: KeyUsage[],
encryptionAlgorithm: string,
) {
const baseKey = await crypto.subtle.importKey("raw", new TextEncoder().encode(secretToken), "PBKDF2", false, [
"deriveKey",
]);
return await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 250000,
hash: "SHA-256",
},
baseKey,
{ name: encryptionAlgorithm, length: 256 },
false,
keyUsages,
);
}
export async function encrypt(
plainText: string,
secretToken: string,
encryptionAlgorithm = DEFAULT_ENCRYPTION_ALGORITHM,
) {
try {
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const aesKey = await buildEncryptionKey(secretToken, salt, ["encrypt"], encryptionAlgorithm);
const encryptedContent = await crypto.subtle.encrypt(
{
name: encryptionAlgorithm,
iv: iv,
},
aesKey,
new TextEncoder().encode(plainText),
);
const encryptedContentArr = new Uint8Array(encryptedContent);
const 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 = btoa(new Uint8Array(buff).reduce((d, byte) => d + String.fromCharCode(byte), ""));
return base64Buff;
} catch (err) {
console.error(`Error in encryptData: ${err}`);
return null;
}
}
export async function decrypt(
encryptedText: string,
secretToken: string,
encryptionAlgorithm = DEFAULT_ENCRYPTION_ALGORITHM,
): Promise<string | null> {
try {
const encryptedDataBuff = Uint8Array.from(atob(encryptedText), (c) => c.charCodeAt(null));
const salt = encryptedDataBuff.slice(0, 16);
const iv = encryptedDataBuff.slice(16, 16 + 12);
const bufferedData = encryptedDataBuff.slice(16 + 12);
const aesKey = await buildEncryptionKey(secretToken, salt, ["decrypt"], encryptionAlgorithm);
const decryptedContent = await crypto.subtle.decrypt(
{
name: encryptionAlgorithm,
iv: iv,
},
aesKey,
bufferedData,
);
return new TextDecoder().decode(decryptedContent);
} catch (err) {
console.error(`Error in decryptData: ${err}`);
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment