62 lines
1.7 KiB
TypeScript
62 lines
1.7 KiB
TypeScript
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<CryptoKey | null> {
|
|
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<PwdBox | null> {
|
|
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<Uint8Array | null> {
|
|
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)
|
|
}
|
|
}
|