rspace-online/scripts/test-key-manager-eoa.ts

118 lines
4.3 KiB
TypeScript

/**
* 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);
});