From c45f14c240216612df146350e4fdc907d560bbe0 Mon Sep 17 00:00:00 2001 From: marlon-erler Date: Sun, 21 Jul 2024 02:58:59 +0200 Subject: [PATCH] restore crypto functions from --- src/Model/Utility/crypto.ts | 115 ++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/Model/Utility/crypto.ts diff --git a/src/Model/Utility/crypto.ts b/src/Model/Utility/crypto.ts new file mode 100644 index 00000000..f839e0a3 --- /dev/null +++ b/src/Model/Utility/crypto.ts @@ -0,0 +1,115 @@ +const IV_SIZE = 12; +const ENCRYPTION_ALG = "AES-GCM"; + +// minimal +export async function encryptString( + plaintext: string, + passphrase: string +): Promise { + if (!window.crypto.subtle) return plaintext; + + const iv = generateIV(); + const key = await importKey(passphrase, "encrypt"); + const encryptedArray = await encrypt(iv, key, plaintext); + + const encryptionData = { + iv: uInt8ToArray(iv), + encryptedArray: uInt8ToArray(encryptedArray), + }; + return btoa(JSON.stringify(encryptionData)); +} + +export async function decryptString( + cyphertext: string, + passphrase: string +): Promise { + try { + const encrypionData = JSON.parse(atob(cyphertext)); + const iv = arrayToUint8(encrypionData.iv); + const encryptedArray = arrayToUint8(encrypionData.encryptedArray); + + const key = await importKey(passphrase, "decrypt"); + return await decrypt(iv, key, encryptedArray); + } catch { + return cyphertext; + } +} + +// process +export function encode(string: string): Uint8Array { + return new TextEncoder().encode(string); +} + +export function decode(array: Uint8Array): string { + return new TextDecoder("utf-8").decode(array); +} + +export async function encrypt( + iv: Uint8Array, + key: CryptoKey, + message: string +): Promise { + const arrayBuffer = await window.crypto.subtle.encrypt( + { name: ENCRYPTION_ALG, iv }, + key, + encode(message) + ); + return new Uint8Array(arrayBuffer); +} + +export async function decrypt( + iv: Uint8Array, + key: CryptoKey, + cyphertext: Uint8Array +): Promise { + const arrayBuffer = await crypto.subtle.decrypt( + { name: ENCRYPTION_ALG, iv }, + key, + cyphertext + ); + return arrayBufferToString(arrayBuffer); +} + +export async function hash(encoded: Uint8Array): Promise { + return await crypto.subtle.digest("SHA-256", encoded); +} + +// iv +export function generateIV(): Uint8Array { + return crypto.getRandomValues(new Uint8Array(IV_SIZE)); +} + +export function splitMergedString(mergedString: string): [Uint8Array, string] { + const iv = mergedString.slice(0, IV_SIZE); + const encryptedString = mergedString.slice(IV_SIZE); + + return [JSON.parse(iv), encryptedString]; +} + +// key +export async function importKey( + passphrase: string, + purpose: "encrypt" | "decrypt" +): Promise { + return await crypto.subtle.importKey( + "raw", + await hash(encode(passphrase)), + { name: ENCRYPTION_ALG }, + false, + [purpose] + ); +} + +// conversion +export function arrayBufferToString(arrayBuffer: ArrayBuffer): string { + const uInt8Array = new Uint8Array(arrayBuffer); + return decode(uInt8Array); +} + +export function uInt8ToArray(uInt8Array: Uint8Array): number[] { + return Array.from(uInt8Array); +} + +export function arrayToUint8(array: number[]): Uint8Array { + return new Uint8Array(array); +}