elpamplina_mastodon-threading/encrypt.ts
elpamplina 7df2101754 Completed missing german translation with english replacements (help wanted!).
Some type tweaks to deal with TypeScript 5 requirements.
2025-12-14 00:50:32 +01:00

94 lines
2.2 KiB
TypeScript

// Thanks to: https://davidmyers.dev/blog/a-practical-guide-to-the-web-cryptography-api
const importKey = async (rawKey: ArrayBuffer) => {
return window.crypto.subtle.importKey(
"raw",
rawKey,
"AES-GCM",
true,
["encrypt", "decrypt"]
);
}
const encode = (text: string) => {
const encoder = new TextEncoder()
return encoder.encode(text)
}
const generateIv = () => {
return window.crypto.getRandomValues(new Uint8Array(12))
}
const encrypt = async (text: string, key: CryptoKey) => {
const encoded = encode(text)
const iv = generateIv()
const cipher = await window.crypto.subtle.encrypt({
name: 'AES-GCM',
iv: iv,
}, key, encoded)
return {
cipher,
iv,
}
}
const pack = (buffer: Uint8Array<ArrayBuffer>) => {
return window.btoa(
String.fromCharCode.apply(null, new Uint8Array(buffer))
)
}
function string_to_buffer(string: string) {
const buffer = new ArrayBuffer(string.length)
const bufferView = new Uint8Array(buffer)
for (let i = 0; i < string.length; i++) {
bufferView[i] = string.charCodeAt(i)
}
return buffer;
}
const unpack = (packed: string) => {
return string_to_buffer(window.atob(packed));
}
const decode = (bytestream: Uint8Array<ArrayBuffer>) => {
const decoder = new TextDecoder()
return decoder.decode(bytestream)
}
const decrypt = async (cipher: ArrayBuffer, key: CryptoKey, iv: ArrayBuffer) => {
const encoded = new Uint8Array(await window.crypto.subtle.decrypt({
name: 'AES-GCM',
iv: iv,
}, key, cipher))
return decode(encoded)
}
const generateKey = (seed: string) => window.crypto.subtle.digest('SHA-256', string_to_buffer(encodeURIComponent(seed)));
const encryptText = async (key: ArrayBuffer, text: string) => {
const cryptoKey = await importKey(key);
const {cipher, iv} = await encrypt(text, cryptoKey)
return pack(iv) + ':' + pack(new Uint8Array(cipher));
}
const decryptText = async (key: ArrayBuffer, text: string) => {
if (text) {
const cryptoKey = await importKey(key);
let textParts = text.split(":");
let iv = textParts.shift();
if (iv) {
let encryptedText = textParts.join(":");
return await decrypt(unpack(encryptedText), cryptoKey, unpack(iv));
} else {
return '';
}
}
else {
return '';
}
}
export {generateKey, encryptText, decryptText}