From 5d3d23ca4a374be23efd794746d1b3db1ed4c3ea Mon Sep 17 00:00:00 2001 From: Pascal Perrenoud Date: Wed, 4 Sep 2024 21:10:10 +0200 Subject: [PATCH] pwd-wrap : Implement --- index.ts | 1 + src/pwd-wrap.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/pwd-wrap.ts diff --git a/index.ts b/index.ts index 1548fd3..2225b2d 100644 --- a/index.ts +++ b/index.ts @@ -6,3 +6,4 @@ export * as JWT from "./src/jwt" export {SecretBox} from './src/secret-box' export {PrivateWrap} from './src/private-wrap' export {SecretWrap} from './src/secret-wrap' +export {PwdWrap} from './src/pwd-wrap' diff --git a/src/pwd-wrap.ts b/src/pwd-wrap.ts new file mode 100644 index 0000000..abd4c79 --- /dev/null +++ b/src/pwd-wrap.ts @@ -0,0 +1,49 @@ +import {hkdf, pbkdf, Usage} from './kdf' +import {SecretWrap} from './secret-wrap' +import logger from 'log' +import {a2b64, b642a} from "misc"; + +const log = logger('crypto:pwd-wrap') + +export class PwdWrap { + private constructor(private readonly box: SecretWrap, private readonly salt: Uint8Array) {} + + private static async derive(pwd: string, salt: Uint8Array) : Promise { + const k = await pbkdf(salt, pwd) + return await hkdf(k, Usage.wrap) as CryptoKey + } + + public static async encrypt(data: CryptoKey, pwd: string, salt?: Uint8Array) : Promise { + log.trace('encrypt') + salt = salt ?? crypto.getRandomValues(new Uint8Array(16)) + const k = await PwdWrap.derive(pwd, salt) + const box = await SecretWrap.encrypt(data, k) + return new PwdWrap(box, salt) + } + public async decrypt(pwd: string) : Promise { + log.trace('decrypt') + const k = await PwdWrap.derive(pwd, this.salt) + return this.box.decrypt(k) + } + + public toString(): String { + log.trace('toString') + const salt = a2b64(this.salt) + const box = this.box.toString() + return `${salt}.${box}` + } + + public fromString(data: string): PwdWrap | null { + log.trace('fromString') + + const parts = data.split('.', 2) + if (parts.length !== 2) return null + + const salt = b642a(parts[0]) + if (salt.is_err()) return null + const box = SecretWrap.fromString(parts[1]) + if (box === null) return null + + return new PwdWrap(box, salt.unwrap()) + } +}