import {describe, expect, test} from 'bun:test' import {kdf, PrivateWrap, SecretBox, SecretWrap} from '..' async function test_keys(k1: CryptoKey, k2: CryptoKey, usage: kdf.Usage | kdf.DHusage, should_box: boolean, should_unbox: boolean) { if (usage === kdf.Usage.wrap || usage == kdf.DHusage.wrap) { const payload = await SecretBox.gen(true) const box = await SecretWrap.wrap(payload, k1) if (should_box) expect(box).not.toBeNull() else return const unbox = await box!.unwrap(k2) if (should_unbox) expect(unbox).not.toBeNull() } else { const payload = crypto.getRandomValues(new Uint8Array(16)) const box = await SecretBox.encrypt(payload, k1) if (should_box) expect(box).not.toBeNull() else return const unbox = await box!.decrypt(k2) if (should_unbox) { expect(unbox).not.toBeNull() expect(unbox).toEqual(payload) } } } describe('HKDF', () => { function test_key(k: CryptoKey, usage: kdf.Usage) { expect(k.extractable).toBeFalse() expect(k.type).toEqual('secret') expect(k.usages).toContainAllValues(usage === kdf.Usage.wrap ? ['wrapKey', 'unwrapKey'] : ['encrypt', 'decrypt']) } const seed1 = crypto.getRandomValues(new Uint8Array(32)) const seed2 = crypto.getRandomValues(new Uint8Array(32)) expect(seed1).not.toEqual(seed2) const usage1 = kdf.Usage.wrap const usage2 = kdf.Usage.box test('Base case', async () => { const k1 = await kdf.hkdf(seed1, usage1) test_key(k1!, usage1) const k2 = await kdf.hkdf(seed1, usage1) test_key(k2!, usage1) expect(k1!.usages).toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, true) }) test('Different seed', async () => { const k1 = await kdf.hkdf(seed1, usage1) test_key(k1!, usage1) const k2 = await kdf.hkdf(seed2, usage1) test_key(k2!, usage1) expect(k1!.usages).toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, false) }) test('Different usage', async () => { const k1 = await kdf.hkdf(seed1, usage1) test_key(k1!, usage1) const k2 = await kdf.hkdf(seed1, usage2) test_key(k2!, usage2) expect(k1!.usages).not.toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, false) await test_keys(k1!, k2!, usage2, false, false) }) test('Different context', async () => { const k1 = await kdf.hkdf(seed1, usage1, 'abc') test_key(k1!, usage1) const k2 = await kdf.hkdf(seed1, usage1, 'def') test_key(k2!, usage1) expect(k1!.usages).toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, false) }) test('Minimum size', async () => { const k = await kdf.hkdf(new Uint8Array(31), usage1) expect(k).toBeNull() }) }) describe('PBKDF', () => { const pwd1 = 'abc' const pwd2 = 'def' expect(pwd1).not.toEqual(pwd2) const salt1 = crypto.getRandomValues(new Uint8Array(16)) const salt2 = crypto.getRandomValues(new Uint8Array(16)) expect(salt1).not.toEqual(salt2) test('Base case', async () => { const k1 = await kdf.pbkdf(salt1, pwd1) const k2 = await kdf.pbkdf(salt1, pwd1) expect(k1).toEqual(k2) }) test('Different salt', async () => { const k1 = await kdf.pbkdf(salt1, pwd1) const k2 = await kdf.pbkdf(salt2, pwd1) expect(k1).not.toEqual(k2) }) test('Different password', async () => { const k1 = await kdf.pbkdf(salt1, pwd1) const k2 = await kdf.pbkdf(salt1, pwd2) expect(k1).not.toEqual(k2) }) // TODO : Different strength => different keys test('Minimum salt size', async () => { const k = await kdf.pbkdf(new Uint8Array(15), pwd1) expect(k).toBeNull() }) }) describe('ECDH', async () => { const p1 = await PrivateWrap.gen(false) const p2 = await PrivateWrap.gen(false) const p3 = await PrivateWrap.gen(false) const usage1 = kdf.DHusage.wrap const usage2 = kdf.DHusage.box function test_key(k: CryptoKey, usage: kdf.DHusage) { expect(k.usages).toContainAllValues(usage === kdf.DHusage.wrap ? ['wrapKey', 'unwrapKey'] : ['encrypt', 'decrypt']) expect(k.type).toEqual('secret') expect(k.extractable).toBeFalse() } test('base case', async () => { const k1 = await kdf.ecdh(p1.privateKey, p2.publicKey, usage1) test_key(k1!, usage1) const k2 = await kdf.ecdh(p2.privateKey, p1.publicKey, usage1) test_key(k2!, usage1) expect(k1!.usages).toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, true) }) test('Different pubkeys', async () => { const k1 = await kdf.ecdh(p1.privateKey, p2.publicKey, usage1) test_key(k1!, usage1) const k2 = await kdf.ecdh(p2.privateKey, p3.publicKey, usage1) test_key(k2!, usage1) expect(k1!.usages).toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, false) }) test('Different privkeys', async () => { const k1 = await kdf.ecdh(p1.privateKey, p2.publicKey, usage1) test_key(k1!, usage1) const k2 = await kdf.ecdh(p3.privateKey, p1.publicKey, usage1) test_key(k2!, usage1) expect(k1!.usages).toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, false) }) test('Different usage', async () => { const k1 = await kdf.ecdh(p1.privateKey, p2.publicKey, usage1) test_key(k1!, usage1) const k2 = await kdf.ecdh(p2.privateKey, p1.publicKey, usage2) test_key(k2!, usage2) expect(k1!.usages).not.toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, false) await test_keys(k1!, k2!, usage2, false, false) }) test('Different context', async () => { const k1 = await kdf.ecdh(p1.privateKey, p2.publicKey, usage1, 'abc') test_key(k1!, usage1) const k2 = await kdf.ecdh(p2.privateKey, p1.publicKey, usage1, 'def') test_key(k2!, usage1) expect(k1!.usages).toContainAllValues(k2!.usages) await test_keys(k1!, k2!, usage1, true, false) }) })