import { describe, expect, it } from 'vitest' import { readFileSync } from 'node:fs' import { join } from 'node:path' import { computeEnrollFpr, generateIdentity, identityFromPrivatePem, verifySignature, } from '../src/keys/identity.js' describe('AgentIdentity (INV4)', () => { it('generates distinct keypairs', () => { const a = generateIdentity() const b = generateIdentity() expect(Buffer.from(a.publicKey).equals(Buffer.from(b.publicKey))).toBe(false) expect(a.publicKey.length).toBe(32) }) it('sign/verify round-trips', () => { const id = generateIdentity() const msg = new TextEncoder().encode('transcript-bytes') const sig = id.sign(msg) expect(verifySignature(id.publicKey, msg, sig)).toBe(true) expect(verifySignature(id.publicKey, new TextEncoder().encode('tampered'), sig)).toBe(false) }) it('enrollFpr is deterministic base64url(SHA-256(pubkey))', () => { const id = generateIdentity() expect(computeEnrollFpr(id.publicKey)).toBe(id.enrollFpr) expect(id.enrollFpr).not.toMatch(/[+/=]/) // base64url alphabet only }) it('reloads the same identity from PEM', () => { const id = generateIdentity() const pem = id.exportPrivatePkcs8Pem() const reloaded = identityFromPrivatePem(pem) expect(Buffer.from(reloaded.publicKey).equals(Buffer.from(id.publicKey))).toBe(true) expect(reloaded.enrollFpr).toBe(id.enrollFpr) }) it('security: no API returns raw private-key bytes', () => { const id = generateIdentity() // The only private-key surface is a PEM export for the 0600 keystore and an in-process // KeyObject; there is NO Uint8Array/raw-bytes getter for the private key. expect('privateKey' in id).toBe(false) const src = readFileSync( join(import.meta.dirname, '..', 'src', 'keys', 'identity.ts'), 'utf8', ) // No function exports raw private key bytes onto the network surface. expect(src).not.toMatch(/exportPrivateRaw|privateKeyBytes|toRawPrivate/) }) })