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(extractable: boolean = true): Promise { log.trace('generate key') return crypto.subtle.generateKey( { name: 'AES-GCM', length: 256 }, extractable, ['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 | null { log.trace('fromString') const obj = this.parseJSON(data) if (obj === null) return null const cipher = b642a(obj.cipher) if (cipher.is_err()) return null const iv = b642a(obj.iv) if (iv.is_err()) return null return new SecretWrap(cipher.unwrap(), obj.algorithm, obj.usages, obj.type, iv.unwrap()) } private static parseJSON(data: string): {cipher: string, iv: string, algorithm: KeyAlgorithm, usages: KeyUsage[], type: 'raw' | 'pkcs8'} | null { log.trace('parseJSON') try { // TODO : Check the fields return JSON.parse(data) } catch (e) { log.warn('Failed to parse JSON') return null } } }