import {Result} from 'result' import * as misc from 'misc' export type Key = CryptoKey export class SecretBox { private readonly cipher: Uint8Array private readonly iv: Uint8Array private readonly phantom!: T private constructor(iv: Uint8Array, cipher: Uint8Array) { this.iv = iv this.cipher = cipher } public static async gen_key(extractable: boolean = false): Promise { return window.crypto.subtle.generateKey( { name: "AES-GCM", length: 256, }, extractable, ["encrypt", "decrypt"] ) } public static async encrypt(key: Key, data: Uint8Array): Promise>> { const iv = window.crypto.getRandomValues(new Uint8Array(12)) const algorithm = {name: "AES-GCM", iv} try { const cipher = await window.crypto.subtle.encrypt(algorithm, key, data) return Result.ok(new SecretBox(iv, new Uint8Array(cipher))) } catch (_) {} return Result.error([]) } public async decrypt(key: Key): Promise> { const algorithm = {name: "AES-GCM", iv: this.iv} try { const cipher = await window.crypto.subtle.decrypt(algorithm, key, this.cipher) const buffer = new Uint8Array(cipher) return Result.ok(buffer) } catch (_) {} return Result.error([]) } public toString() : string { const iv = misc.a2b64(this.iv) const cipher = misc.a2b64(this.cipher) return `${iv}.${cipher}` } public static fromString(data: string) : Result> { const parts = data.split(".") if (parts.length !== 2) return Result.error([]) const iv = misc.b642a(parts[0]) const cipher = misc.b642a(parts[1]) if (iv.error()) return Result.error([]) if (cipher.error()) return Result.error([]) return Result.ok(new SecretBox(iv.unwrap(), cipher.unwrap())) } }