118 lines
4.3 KiB
TypeScript
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);
|
|
});
|