Skip to content

Instantly share code, notes, and snippets.

@qutek
Created June 13, 2023 02:33
Show Gist options
  • Save qutek/30ab9b0bfae7b2fd8011e8bbec5decf6 to your computer and use it in GitHub Desktop.
Save qutek/30ab9b0bfae7b2fd8011e8bbec5decf6 to your computer and use it in GitHub Desktop.
[javascript] Encrypt data in javascript browser as base64 code without padding
export const encode = (str) =>
btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
export const decode = (str) => {
if (str.length % 4 != 0) {
str += '==='.slice(0, 4 - (str.length % 4));
}
return atob(str.replace(/-/g, '+').replace(/_/g, '/'), 'base64');
};
export const encrypt = async (plaintext, password) => {
try {
const pwUtf8 = new TextEncoder().encode(password); // encode password as UTF-8
const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8); // hash the password
const iv = crypto.getRandomValues(new Uint8Array(12)); // get 96-bit random iv
const ivStr = Array.from(iv)
.map((b) => String.fromCharCode(b))
.join(''); // iv as utf-8 string
const alg = { name: 'AES-GCM', iv: iv }; // specify algorithm to use
const key = await crypto.subtle.importKey('raw', pwHash, alg, false, [
'encrypt',
]); // generate key from pw
const ptUint8 = new TextEncoder().encode(plaintext); // encode plaintext as UTF-8
const ctBuffer = await crypto.subtle.encrypt(alg, key, ptUint8); // encrypt plaintext using key
const ctArray = Array.from(new Uint8Array(ctBuffer)); // ciphertext as byte array
const ctStr = ctArray.map((byte) => String.fromCharCode(byte)).join(''); // ciphertext as string
return encode(ivStr + ctStr); // iv+ciphertext base64-encoded
} catch (error) {
throw error;
}
};
export const decrypt = async (ciphertext, password) => {
try {
const pwUtf8 = new TextEncoder().encode(password); // encode password as UTF-8
const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8); // hash the password
const ivStr = decode(ciphertext).slice(0, 12); // decode base64 iv
const iv = new Uint8Array(Array.from(ivStr).map((ch) => ch.charCodeAt(0))); // iv as Uint8Array
const alg = { name: 'AES-GCM', iv: iv }; // specify algorithm to use
const key = await crypto.subtle.importKey('raw', pwHash, alg, false, [
'decrypt',
]); // generate key from pw
const ctStr = decode(ciphertext).slice(12); // decode base64 ciphertext
const ctUint8 = new Uint8Array(
Array.from(ctStr).map((ch) => ch.charCodeAt(0))
); // ciphertext as Uint8Array
// note: why doesn't ctUint8 = new TextEncoder().encode(ctStr) work?
const plainBuffer = await crypto.subtle.decrypt(alg, key, ctUint8); // decrypt ciphertext using key
const plaintext = new TextDecoder().decode(plainBuffer); // plaintext from ArrayBuffer
return plaintext; // return the plaintext
} catch (e) {
throw e;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment