109 lines
2.5 KiB
JavaScript
109 lines
2.5 KiB
JavaScript
|
const ALGORITHM_NAME = "AES-GCM";
|
||
|
const ALGORITHM_LENGTH = 256;
|
||
|
const IV_LENGTH = 16;
|
||
|
|
||
|
const TEXT_ENCODER = new TextEncoder();
|
||
|
const TEXT_DECODER = new TextDecoder();
|
||
|
|
||
|
/** Coverts an ArrayBuffer into a Base64 encoded string
|
||
|
* @param {ArrayBuffer} buffer
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function bufferToBase64(buffer) {
|
||
|
let binary = "";
|
||
|
const bytes = new Uint8Array(buffer);
|
||
|
const len = bytes.byteLength;
|
||
|
for (let i = 0; i < len; i++) {
|
||
|
binary += String.fromCharCode(bytes[i]);
|
||
|
}
|
||
|
|
||
|
return btoa(binary);
|
||
|
}
|
||
|
|
||
|
/** Converts a Base64 encoded string into an ArrayBuffer
|
||
|
* @param {string} base64
|
||
|
* @returns {ArrayBuffer}
|
||
|
*/
|
||
|
function base64ToBuffer(base64) {
|
||
|
const binaryString = atob(base64);
|
||
|
const len = binaryString.length;
|
||
|
const bytes = new Uint8Array(len);
|
||
|
for (let i = 0; i < len; i++) {
|
||
|
bytes[i] = binaryString.charCodeAt(i);
|
||
|
}
|
||
|
|
||
|
return bytes.buffer;
|
||
|
}
|
||
|
|
||
|
/** Generates an key and encrypts "password" using it
|
||
|
* @param {Promise<string>} password
|
||
|
*/
|
||
|
async function encryptPassword(password) {
|
||
|
const iv = new Uint8Array(IV_LENGTH);
|
||
|
window.crypto.getRandomValues(iv);
|
||
|
|
||
|
const key = await window.crypto.subtle.generateKey(
|
||
|
{
|
||
|
name: ALGORITHM_NAME,
|
||
|
length: ALGORITHM_LENGTH,
|
||
|
},
|
||
|
true,
|
||
|
["encrypt", "decrypt"],
|
||
|
);
|
||
|
|
||
|
const encryptedPassword = await window.crypto.subtle.encrypt(
|
||
|
{
|
||
|
name: ALGORITHM_NAME,
|
||
|
iv: iv,
|
||
|
},
|
||
|
key,
|
||
|
TEXT_ENCODER.encode(password),
|
||
|
);
|
||
|
|
||
|
const exportedKey = await window.crypto.subtle.exportKey("raw", key);
|
||
|
|
||
|
const encodedPassword = bufferToBase64(encryptedPassword);
|
||
|
const encodedKey = bufferToBase64(exportedKey);
|
||
|
const encodedIv = bufferToBase64(iv.buffer);
|
||
|
|
||
|
return {
|
||
|
password: encodedPassword,
|
||
|
key: encodedKey,
|
||
|
iv: encodedIv,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/** Decodes to key and iv and decryptes the password
|
||
|
* @param {string} password
|
||
|
* @param {string} key
|
||
|
* @param {string} iv
|
||
|
* @returns {Promise<string>}
|
||
|
*/
|
||
|
async function decryptPassword(password, key, iv) {
|
||
|
const decodedPassword = base64ToBuffer(password);
|
||
|
const decodedKey = base64ToBuffer(key);
|
||
|
const decodedIv = base64ToBuffer(iv);
|
||
|
|
||
|
const cryptoKey = await window.crypto.subtle.importKey(
|
||
|
"raw",
|
||
|
decodedKey,
|
||
|
{
|
||
|
name: ALGORITHM_NAME,
|
||
|
length: ALGORITHM_LENGTH,
|
||
|
},
|
||
|
true,
|
||
|
["encrypt", "decrypt"],
|
||
|
);
|
||
|
|
||
|
const decryptedPassword = await window.crypto.subtle.decrypt(
|
||
|
{
|
||
|
name: ALGORITHM_NAME,
|
||
|
iv: decodedIv,
|
||
|
},
|
||
|
cryptoKey,
|
||
|
decodedPassword,
|
||
|
);
|
||
|
|
||
|
return TEXT_DECODER.decode(decryptedPassword);
|
||
|
}
|