Created
February 9, 2022 07:59
-
-
Save siddsarkar/4fddd70b04235a442ea239177d5fc1c4 to your computer and use it in GitHub Desktop.
A WebCrypto class that can be used to encrypt/decrypt data with password.
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
function WebCrypto() { | |
const buffToBase64 = (buff) => btoa(String.fromCharCode.apply(null, buff)); | |
const base64ToBuff = (b64) => | |
Uint8Array.from(atob(b64), (c) => c.charCodeAt(null)); | |
const enc = new TextEncoder(); | |
const dec = new TextDecoder(); | |
const bytes = { salt: 16, iv: 12 }; | |
/** | |
* Returns a key generated from password, | |
* use it as input to the deriveKey method. | |
* | |
* @param {string|number} password password for encryption/decryption | |
* @returns a key | |
*/ | |
function getKeyFromPassword(password) { | |
return window.crypto.subtle.importKey( | |
"raw", | |
enc.encode(password), | |
{ name: "PBKDF2" }, | |
false, | |
["deriveBits", "deriveKey"] | |
); | |
} | |
/** | |
* Given some key from password and some random salt, | |
* returns a derived AES-GCM key using PBKDF2. | |
* | |
* @param {CryptoKey} keyFromPassword Key generated from password | |
* @param {Uint8Array} salt random generated salt | |
* @returns derived key | |
*/ | |
function getKey(keyFromPassword, salt) { | |
return window.crypto.subtle.deriveKey( | |
{ | |
name: "PBKDF2", | |
salt: salt, | |
iterations: 100000, | |
hash: "SHA-256", | |
}, | |
keyFromPassword, | |
{ name: "AES-GCM", length: 256 }, | |
true, | |
["encrypt", "decrypt"] | |
); | |
} | |
/** | |
* Derive a key from a password supplied by the user, | |
* use the key to encrypt the secret data, | |
* return the combined encrypted data as string. | |
* | |
* @param {string|number} secret secret data to encrypt | |
* @param {string|number} password password for encryption | |
* @returns encrypted string | |
*/ | |
this.encrypt = async function (secret, password) { | |
let keyFromPassword = await getKeyFromPassword(password); | |
let salt = window.crypto.getRandomValues(new Uint8Array(bytes.salt)); | |
let key = await getKey(keyFromPassword, salt); | |
let iv = window.crypto.getRandomValues(new Uint8Array(bytes.iv)); | |
let encoded = enc.encode(secret); | |
let ciphertext = await window.crypto.subtle.encrypt( | |
{ | |
name: "AES-GCM", | |
iv: iv, | |
}, | |
key, | |
encoded | |
); | |
let cipher = new Uint8Array(ciphertext); | |
let buffer = new Uint8Array( | |
salt.byteLength + iv.byteLength + cipher.byteLength | |
); | |
buffer.set(salt, 0); | |
buffer.set(iv, salt.byteLength); | |
buffer.set(cipher, salt.byteLength + iv.byteLength); | |
let encrypted = buffToBase64(buffer); | |
return encrypted; | |
}; | |
/** | |
* Derive a key from a password supplied by the user, | |
* use the key to decrypt the ciphertext. | |
* if the ciphertext was decrypted successfully, | |
* return the decrypted value. | |
* if there was an error decrypting, | |
* throw an error message. | |
* | |
* @param {string} encrypted encrypted base64 string | |
* @param {string|number} password password for the encrypted data | |
* @returns decrypted data as string | |
*/ | |
this.decrypt = async function (encrypted, password) { | |
const encryptedBuffer = base64ToBuff(encrypted); | |
const salt = encryptedBuffer.slice(0, bytes.salt); | |
const iv = encryptedBuffer.slice(bytes.salt, bytes.salt + bytes.iv); | |
const ciphertext = encryptedBuffer.slice(bytes.salt + bytes.iv); | |
let keyFromPassword = await getKeyFromPassword(password); | |
let key = await getKey(keyFromPassword, salt); | |
try { | |
let decryptedEncoded = await window.crypto.subtle.decrypt( | |
{ | |
name: "AES-GCM", | |
iv: iv, | |
}, | |
key, | |
ciphertext | |
); | |
let decrypted = dec.decode(decryptedEncoded); | |
return decrypted; | |
} catch (e) { | |
throw new Error(e); | |
} | |
}; | |
} | |
/** | |
* Usage | |
*/ | |
let c = new WebCrypto(); | |
let someEncryptedString = await c.encrypt("This is my secret", "iamkey"); | |
console.log(await c.decrypt(someEncryptedString, "iamkey")); // "This is my secret" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment