From 37cd6b1800f971ec486bb78934de6bcc7132e95c Mon Sep 17 00:00:00 2001 From: Pascal Perrenoud Date: Thu, 12 Sep 2024 00:05:31 +0200 Subject: [PATCH] Test KDF --- test/kdf.test.ts | 177 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 test/kdf.test.ts diff --git a/test/kdf.test.ts b/test/kdf.test.ts new file mode 100644 index 0000000..939714b --- /dev/null +++ b/test/kdf.test.ts @@ -0,0 +1,177 @@ +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) + }) +})