import logger from 'log' import {hkdf, pbkdf, Usage} from './kdf' import SecretBox from './secret-box' import {a2b64, b642a} from 'misc' const log = logger('crypto:pwd-box') export default class PwdBox { private constructor( private readonly box: SecretBox, private readonly salt: Uint8Array ) {} private static async derive(pwd: string, salt: Uint8Array, context?: string): Promise { const k = await pbkdf(salt, pwd) if (k === null) return null return await hkdf(k, Usage.box, context) as CryptoKey } public static async encrypt(data: Uint8Array, pwd: string, context?: string): Promise { log.trace('encrypt') const salt = crypto.getRandomValues(new Uint8Array(16)) const k = await PwdBox.derive(pwd, salt, context) if (k === null) return null const box = await SecretBox.encrypt(data, k) if (box === null) return null return new PwdBox(box, salt) } public async decrypt(pwd: string, context?: string): Promise { log.trace('decrypt') const k = await PwdBox.derive(pwd, this.salt, context) if (k === null) return null return await this.box.decrypt(k) } public toString(): string { log.trace('toString') const salt = a2b64(this.salt) const box = this.box.toString() return `${salt}.${box}` } public static fromString(data: string): PwdBox | null { log.trace('fromString') const parts = data.split('.') if (parts.length < 2) return null const salt = b642a(parts[0]) if (salt === null) return null const box = SecretBox.fromString(parts.slice(1).join('.')) if (box === null) return null return new PwdBox(box, salt) } }