passed/static/js/crypto.js
2025-05-23 22:22:18 +02:00

112 lines
2.4 KiB
JavaScript

class PasswordCrypto {
#encoder
#decoder
#algorithmName
#algorithmLength
#ivLength
constructor() {
this.#encoder = new TextEncoder()
this.#decoder = new TextDecoder()
this.#algorithmName = "AES-GCM"
this.#algorithmLength = 256
this.#ivLength = 16
}
/**
* @param {string} buffer
* @returns {string}
*/
#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)
}
/**
* @param {string} base64
* @returns {string}
*/
#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
}
/**
* @param {string} password Plain text password
* @returns {Promise<{ password: string, key: string, iv: string }>} Encrypted password
*/
async encryptPassword(password) {
const iv = new Uint8Array(this.#ivLength)
window.crypto.getRandomValues(iv)
const key = await window.crypto.subtle.generateKey(
{
name: this.#algorithmName,
length: this.#algorithmLength,
},
true,
["encrypt", "decrypt"],
)
const rawKey = await window.crypto.subtle.exportKey("raw", key)
const encrypted = await window.crypto.subtle.encrypt(
{
name: this.#algorithmName,
iv: iv,
},
key,
this.#encoder.encode(password),
)
return {
password: this.#bufferToBase64(encrypted),
key: this.#bufferToBase64(rawKey),
iv: this.#bufferToBase64(iv.buffer),
}
}
/**
* @param {string} password Encrypted password
* @param {string} key
* @param {string} iv
* @returns {Promise<string>} Plain text password
*/
async decryptPassword(password, key, iv) {
const ckey = await window.crypto.subtle.importKey(
"raw",
this.#base64ToBuffer(key),
{
name: this.#algorithmName,
length: this.#algorithmLength,
},
true,
["encrypt", "decrypt"],
)
const dpassword = await window.crypto.subtle.decrypt(
{
name: this.#algorithmName,
iv: this.#base64ToBuffer(iv),
},
ckey,
this.#base64ToBuffer(password),
)
return this.#decoder.decode(dpassword)
}
}