import { describe, expect, test } from 'bun:test'; import { SubtleCryptoProvider } from '@shade/crypto-web'; import { canonicalSthBytes, computeLogId, signSth, sthFromWire, sthToWire, verifySthSignature, } from '../src/index.js'; import { fromBase64, toBase64 } from '../src/util.js'; const crypto = new SubtleCryptoProvider(); describe('STH signing & verification', () => { test('signSth + verifySthSignature roundtrip', async () => { const kp = await crypto.generateEd25519KeyPair(); const sth = await signSth(crypto, kp.privateKey, { treeSize: 42, timestampMs: 1700000000000, rootHash: new Uint8Array(32).fill(0xaa), indexRoot: new Uint8Array(32).fill(0xbb), logId: computeLogId(kp.publicKey), }); expect(await verifySthSignature(crypto, sth, kp.publicKey)).toBe(true); }); test('verify fails with wrong public key', async () => { const kp = await crypto.generateEd25519KeyPair(); const other = await crypto.generateEd25519KeyPair(); const sth = await signSth(crypto, kp.privateKey, { treeSize: 1, timestampMs: 1700000000000, rootHash: new Uint8Array(32), indexRoot: new Uint8Array(32), logId: computeLogId(kp.publicKey), }); expect(await verifySthSignature(crypto, sth, other.publicKey)).toBe(false); }); test('verify fails when log_id is forged', async () => { const kp = await crypto.generateEd25519KeyPair(); const other = await crypto.generateEd25519KeyPair(); const sth = await signSth(crypto, kp.privateKey, { treeSize: 1, timestampMs: 1700000000000, rootHash: new Uint8Array(32), indexRoot: new Uint8Array(32), logId: computeLogId(other.publicKey), // mismatched }); // The signature was made over a log_id that doesn't match the supplied // public key — verifySthSignature should refuse. expect(await verifySthSignature(crypto, sth, kp.publicKey)).toBe(false); }); test('canonical bytes layout is stable', () => { const bytes = canonicalSthBytes({ treeSize: 0x0102030405, timestampMs: 0x06070809, rootHash: new Uint8Array(32).fill(0x11), indexRoot: new Uint8Array(32).fill(0x22), logId: new Uint8Array(32).fill(0x33), }); // 1 prefix + 8 treeSize + 8 timestamp + 32 + 32 + 32 expect(bytes.length).toBe(113); expect(bytes[0]).toBe(0x02); }); test('wire roundtrip', async () => { const kp = await crypto.generateEd25519KeyPair(); const sth = await signSth(crypto, kp.privateKey, { treeSize: 7, timestampMs: 1700000000000, rootHash: new Uint8Array(32).fill(0x77), indexRoot: new Uint8Array(32).fill(0x88), logId: computeLogId(kp.publicKey), }); const wire = sthToWire(sth, toBase64); const back = sthFromWire(wire, fromBase64); expect(back).toEqual(sth); expect(await verifySthSignature(crypto, back, kp.publicKey)).toBe(true); }); });