/** * test-key-manager-eoa.ts — Test EOA integration in EncryptIDKeyManager. * * Verifies that the key manager properly derives EOA keys from PRF output * and includes them in the DerivedKeys interface. * * Usage: * bun run scripts/test-key-manager-eoa.ts */ import { EncryptIDKeyManager } from '../src/encryptid/key-derivation'; let passed = 0; let failed = 0; function assert(condition: boolean, msg: string) { if (condition) { console.log(` ✓ ${msg}`); passed++; } else { console.error(` ✗ ${msg}`); failed++; } } function toHex(bytes: Uint8Array): string { return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); } async function main() { console.log('=== Key Manager EOA Integration Tests ===\n'); // Simulate PRF output (32 bytes) const prfOutput = new ArrayBuffer(32); const view = new Uint8Array(prfOutput); for (let i = 0; i < 32; i++) view[i] = i + 1; // Test 1: Initialize from PRF and derive keys console.log('[1] Derive keys from PRF (includes EOA)'); const km = new EncryptIDKeyManager(); assert(!km.isInitialized(), 'Not initialized before init'); await km.initFromPRF(prfOutput); assert(km.isInitialized(), 'Initialized after initFromPRF'); const keys = await km.getKeys(); assert(keys.fromPRF === true, 'Keys marked as from PRF'); assert(keys.encryptionKey !== undefined, 'Has encryption key'); assert(keys.signingKeyPair !== undefined, 'Has signing key pair'); assert(keys.didSeed !== undefined, 'Has DID seed'); assert(keys.did !== undefined, 'Has DID'); // EOA fields should be present (PRF path) assert(keys.eoaPrivateKey !== undefined, 'Has EOA private key'); assert(keys.eoaAddress !== undefined, 'Has EOA address'); assert(keys.eoaPrivateKey!.length === 32, 'EOA private key is 32 bytes'); assert(keys.eoaAddress!.startsWith('0x'), 'EOA address starts with 0x'); assert(keys.eoaAddress!.length === 42, 'EOA address is 42 chars'); console.log(` EOA Address: ${keys.eoaAddress}`); // Test 2: Keys are cached (same object returned) console.log('\n[2] Key caching'); const keys2 = await km.getKeys(); assert(keys2.eoaAddress === keys.eoaAddress, 'Cached keys return same EOA address'); // Test 3: Determinism — new manager with same PRF gives same EOA console.log('\n[3] Determinism across key manager instances'); const km2 = new EncryptIDKeyManager(); await km2.initFromPRF(prfOutput); const keys3 = await km2.getKeys(); assert(keys3.eoaAddress === keys.eoaAddress, 'Same PRF → same EOA across instances'); assert(toHex(keys3.eoaPrivateKey!) === toHex(keys.eoaPrivateKey!), 'Same private key too'); // Test 4: Clear wipes EOA key console.log('\n[4] Clear wipes sensitive material'); const eoaKeyRef = keys.eoaPrivateKey!; const eoaBefore = toHex(eoaKeyRef); assert(eoaBefore !== '00'.repeat(32), 'EOA key non-zero before clear'); km.clear(); assert(!km.isInitialized(), 'Not initialized after clear'); // The referenced Uint8Array should be zeroed const eoaAfter = toHex(eoaKeyRef); assert(eoaAfter === '00'.repeat(32), 'EOA private key zeroed after clear'); // Test 5: Passphrase path — no EOA keys console.log('\n[5] Passphrase path (no EOA)'); const km3 = new EncryptIDKeyManager(); const salt = new Uint8Array(32); crypto.getRandomValues(salt); await km3.initFromPassphrase('test-passphrase-123', salt); const passKeys = await km3.getKeys(); assert(passKeys.fromPRF === false, 'Keys marked as from passphrase'); assert(passKeys.eoaPrivateKey === undefined, 'No EOA private key from passphrase'); assert(passKeys.eoaAddress === undefined, 'No EOA address from passphrase'); assert(passKeys.encryptionKey !== undefined, 'Still has encryption key'); km3.clear(); // Test 6: Domain separation — EOA address differs from DID-derived values console.log('\n[6] Domain separation'); const km4 = new EncryptIDKeyManager(); await km4.initFromPRF(prfOutput); const keys4 = await km4.getKeys(); // EOA address should not appear in the DID assert(!keys4.did.includes(keys4.eoaAddress!.slice(2)), 'EOA address not in DID'); km4.clear(); // Cleanup km2 km2.clear(); console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`); process.exit(failed > 0 ? 1 : 0); } main().catch(err => { console.error('Fatal:', err); process.exit(1); });