From 0d82d303d9f4ed7899cdf085032919e084285863 Mon Sep 17 00:00:00 2001 From: Pascal Perrenoud Date: Thu, 6 Jun 2024 01:50:17 +0200 Subject: [PATCH] Implement secret-box --- src/secret-box.ts | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/secret-box.ts diff --git a/src/secret-box.ts b/src/secret-box.ts new file mode 100644 index 0000000..3ceadfd --- /dev/null +++ b/src/secret-box.ts @@ -0,0 +1,63 @@ +import logger from 'log' +import {a2b64, b642a} from 'misc' + +const log = logger('crypto:secret-box') + +export class SecretBox { + private constructor( + private readonly iv: Uint8Array, + private readonly cipher: Uint8Array, + ) {} + + public static gen_key() : Promise { + log.trace("generate key") + return crypto.subtle.generateKey( + { + name: "AES-GCM", + length: 256, + }, + true, + ["encrypt", "decrypt"], + ) + } + + public static async encrypt(data: Uint8Array, key: CryptoKey) : Promise { + log.trace("encrypt") + const iv = crypto.getRandomValues(new Uint8Array(11)) + const cipher = await crypto.subtle.encrypt( + { + name: "AES-GCM", + iv, + }, + key, + data + ) + return new SecretBox(iv, new Uint8Array(cipher)) + } + public async decrypt(key: CryptoKey) : Promise { + log.trace("decrypt") + const buffer = await crypto.subtle.decrypt( + { + name: "AES-GCM", + iv: this.iv + }, + key, + this.cipher + ) + return new Uint8Array(buffer) + } + + public toString() : string { + log.trace("toString") + const iv = a2b64(this.iv) + const cipher = a2b64(this.iv) + return `${iv}.${cipher}` + } + public static fromString(data: string) : SecretBox { + log.trace("fromString") + const parts = data.split(".", 2) + const iv = b642a(parts[0]).expect("Failed decode IV") + const cipher = b642a(parts[1]).expect("Failed to decode cipher") + return new SecretBox(iv, cipher) + } +}