fileverse/poc/crypto-eval/src/benchmark.ts

159 lines
5.4 KiB
TypeScript

/**
* Benchmark: AES-256-GCM (Web Crypto) vs NaCl SecretBox
* Tests encryption/decryption performance across payload sizes
*/
import {
aesEncrypt,
aesDecrypt,
generateSymmetricKey,
generateKeyPair,
encryptForRecipient,
decryptFromSender,
shareDocKey,
receiveDocKey,
} from './mit-crypto.js'
function generateTestData(size: number): Uint8Array {
const data = new Uint8Array(size)
// getRandomValues has 64KB limit, fill in chunks
for (let offset = 0; offset < size; offset += 65536) {
const chunk = Math.min(65536, size - offset)
crypto.getRandomValues(data.subarray(offset, offset + chunk))
}
return data
}
const encoder = new TextEncoder()
const decoder = new TextDecoder()
async function benchmark(name: string, fn: () => Promise<void>, iterations: number = 1000) {
// Warmup
for (let i = 0; i < 10; i++) await fn()
const start = performance.now()
for (let i = 0; i < iterations; i++) await fn()
const elapsed = performance.now() - start
console.log(`${name}: ${(elapsed / iterations).toFixed(3)}ms avg (${iterations} iterations)`)
}
async function main() {
console.log('=== rStack Crypto Primitives Benchmark ===\n')
// ─── Symmetric Encryption ───
console.log('--- Symmetric Encryption (AES-256-GCM) ---')
const key = generateSymmetricKey()
for (const size of [100, 1_000, 10_000, 100_000]) {
const data = generateTestData(size)
await benchmark(` Encrypt ${size.toLocaleString()}B`, async () => {
await aesEncrypt(key, data)
})
const encrypted = await aesEncrypt(key, data)
await benchmark(` Decrypt ${size.toLocaleString()}B`, async () => {
await aesDecrypt(key, encrypted)
})
}
// ─── Key Exchange ───
console.log('\n--- Key Exchange (x25519 ECDH) ---')
const alice = generateKeyPair()
const bob = generateKeyPair()
await benchmark(' Generate key pair', async () => {
generateKeyPair()
})
// ─── ECIES Encryption ───
console.log('\n--- ECIES Encryption (x25519 + AES-256-GCM) ---')
const message = encoder.encode('This is a secret document encryption key - 32 bytes!')
await benchmark(' Encrypt for recipient', async () => {
await encryptForRecipient(message, bob.publicKey, alice.privateKey)
})
const { ciphertext, ephemeralPublicKey } = await encryptForRecipient(
message, bob.publicKey, alice.privateKey
)
await benchmark(' Decrypt from sender', async () => {
await decryptFromSender(ciphertext, ephemeralPublicKey, bob.privateKey)
})
// ─── Document Key Sharing ───
console.log('\n--- Document Key Sharing ---')
const docKey = generateSymmetricKey()
await benchmark(' Share doc key', async () => {
await shareDocKey(docKey, bob.publicKey, alice.privateKey)
})
const shared = await shareDocKey(docKey, bob.publicKey, alice.privateKey)
await benchmark(' Receive doc key', async () => {
await receiveDocKey(shared.encryptedKey, shared.ephemeralPublicKey, bob.privateKey)
})
// ─── End-to-End Roundtrip ───
console.log('\n--- Full Roundtrip: Note Encryption + Key Sharing ---')
const noteContent = encoder.encode(JSON.stringify({
title: 'Secret Meeting Notes',
content: '<p>Discussion about token allocation...</p>'.repeat(100),
tags: ['dao', 'treasury', 'private'],
}))
await benchmark(' Full encrypt + share flow', async () => {
// 1. Generate doc key
const dk = generateSymmetricKey()
// 2. Encrypt content
const enc = await aesEncrypt(dk, noteContent)
// 3. Share key with collaborator
await shareDocKey(dk, bob.publicKey, alice.privateKey)
})
await benchmark(' Full receive + decrypt flow', async () => {
// Simulated received data
const dk = generateSymmetricKey()
const enc = await aesEncrypt(dk, noteContent)
const sk = await shareDocKey(dk, bob.publicKey, alice.privateKey)
// Actual measured flow
const receivedKey = await receiveDocKey(sk.encryptedKey, sk.ephemeralPublicKey, bob.privateKey)
await aesDecrypt(receivedKey, enc)
})
// ─── Correctness Check ───
console.log('\n--- Correctness Verification ---')
// Symmetric roundtrip
const testData = encoder.encode('Hello, encrypted world!')
const testKey = generateSymmetricKey()
const testEncrypted = await aesEncrypt(testKey, testData)
const testDecrypted = await aesDecrypt(testKey, testEncrypted)
const symOk = decoder.decode(testDecrypted) === 'Hello, encrypted world!'
console.log(` Symmetric roundtrip: ${symOk ? 'PASS' : 'FAIL'}`)
// ECIES roundtrip
const kp1 = generateKeyPair()
const kp2 = generateKeyPair()
const plaintext = encoder.encode('Secret message for key pair 2')
const { ciphertext: ct, ephemeralPublicKey: epk } = await encryptForRecipient(
plaintext, kp2.publicKey, kp1.privateKey
)
const decrypted = await decryptFromSender(ct, epk, kp2.privateKey)
const eciesOk = decoder.decode(decrypted) === 'Secret message for key pair 2'
console.log(` ECIES roundtrip: ${eciesOk ? 'PASS' : 'FAIL'}`)
// Doc key sharing roundtrip
const origKey = generateSymmetricKey()
const shareResult = await shareDocKey(origKey, kp2.publicKey, kp1.privateKey)
const recoveredKey = await receiveDocKey(
shareResult.encryptedKey, shareResult.ephemeralPublicKey, kp2.privateKey
)
const keyOk = origKey.every((b, i) => b === recoveredKey[i])
console.log(` Doc key sharing roundtrip: ${keyOk ? 'PASS' : 'FAIL'}`)
console.log('\nDone.')
}
main().catch(console.error)