import logger from 'log' import {a2b64, b642a} from 'misc' const log = logger('crypto:secret-wrap') export class SecretWrap { constructor( private readonly cipher: Uint8Array, private readonly algorithm: KeyAlgorithm, private readonly usages: KeyUsage[], private readonly type: "raw" | "pkcs8", private readonly iv: Uint8Array, ) {} public static gen_key() : Promise { log.trace("generate key") return crypto.subtle.generateKey( { name: "AES-GCM", length: 256, }, true, ["wrapKey", "unwrapKey"], ) } private static format(type: KeyType) : "raw" | "pkcs8" { switch (type) { case "private": return "pkcs8" case "secret": return "raw" default: throw "Don't wrap public keys please..." } } public static async encrypt(data: CryptoKey, key: CryptoKey) : Promise { log.trace("encrypt") const format = this.format(data.type) const iv = crypto.getRandomValues(new Uint8Array(12)) const box = await crypto.subtle.wrapKey( format, data, key, { name: "AES-GCM", iv, }, ) return new SecretWrap(new Uint8Array(box), data.algorithm, data.usages, format, iv) } public async decrypt(key: CryptoKey) : Promise { log.trace("decrypt") return crypto.subtle.unwrapKey( this.type, this.cipher, key, { name: "AES-GCM", iv: this.iv, }, this.algorithm, true, this.usages ) } public toString() : string { log.trace("toString") return JSON.stringify({ cipher: a2b64(this.cipher), iv: a2b64(this.iv), algorithm: this.algorithm, usages: this.usages, type: this.type, }) } public static fromString(data: string) : SecretWrap { log.trace("fromString") const obj = JSON.parse(data) const cipher = b642a(obj.cipher).expect("Failed to decode cipher") const iv = b642a(obj.iv).expect("Failed to decode IV") return new SecretWrap(cipher, obj.algorithm, obj.usages, obj.type, iv) } }