import { describe, test, expect } from 'bun:test'; import { readFileSync } from 'fs'; import { join } from 'path'; import { deriveBlobSlotId, deriveBlobKey, deriveBlobSigningSeed, } from '../src/crypto/kdf.js'; import { ed25519PublicKeyFromSeed } from '@shade/crypto-web'; function fromHex(s: string): Uint8Array { const out = new Uint8Array(s.length / 2); for (let i = 0; i < out.length; i++) { out[i] = parseInt(s.substr(i * 2, 2), 16); } return out; } function toHex(b: Uint8Array): string { let s = ''; for (const x of b) s += x.toString(16).padStart(2, '0'); return s; } describe('V4.9 blob-storage KDF vectors', () => { // Resolve relative to this file, not to cwd, so the test passes // regardless of which directory `bun test` is invoked from. const vectorPath = join(import.meta.dir, '..', '..', '..', 'test-vectors', 'blob-storage.json'); const vectors = JSON.parse(readFileSync(vectorPath, 'utf-8')) as { kdf: Array<{ masterKey: string; app: string; slotId: string; blobKey: string; signingSeed: string; ownerPubkey: string; }>; }; for (const v of vectors.kdf) { test(`(master=${v.masterKey.slice(0, 8)}…, app=${v.app})`, () => { const km = fromHex(v.masterKey); expect(toHex(deriveBlobSlotId(km, v.app))).toBe(v.slotId); expect(toHex(deriveBlobKey(km, v.app))).toBe(v.blobKey); const seed = deriveBlobSigningSeed(km, v.app); expect(toHex(seed)).toBe(v.signingSeed); expect(toHex(ed25519PublicKeyFromSeed(seed))).toBe(v.ownerPubkey); }); } });