test: audit AES-256-GCM crypto with 7 test cases
Verify random IV (same plaintext encrypts differently), ciphertext format (iv:authTag:encrypted), auth tag tamper detection, encrypted data tamper detection, empty string handling, and unicode support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
c5744f5984
commit
b1e86843a7
@@ -0,0 +1,64 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import { encrypt, decrypt } from './crypto.js'
|
||||||
|
|
||||||
|
describe('crypto — AES-256-GCM', () => {
|
||||||
|
it('encrypt then decrypt returns original plaintext', () => {
|
||||||
|
const plaintext = 'nostr+walletconnect://relay.example.com?secret=abc123'
|
||||||
|
const ciphertext = encrypt(plaintext)
|
||||||
|
const decrypted = decrypt(ciphertext)
|
||||||
|
expect(decrypted).toBe(plaintext)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('encrypting same plaintext twice produces different ciphertexts (random IV)', () => {
|
||||||
|
const plaintext = 'same-text-every-time'
|
||||||
|
const c1 = encrypt(plaintext)
|
||||||
|
const c2 = encrypt(plaintext)
|
||||||
|
expect(c1).not.toBe(c2)
|
||||||
|
// Both should still decrypt to the same value
|
||||||
|
expect(decrypt(c1)).toBe(plaintext)
|
||||||
|
expect(decrypt(c2)).toBe(plaintext)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ciphertext format is iv:authTag:encrypted (3 hex parts)', () => {
|
||||||
|
const ciphertext = encrypt('test data')
|
||||||
|
const parts = ciphertext.split(':')
|
||||||
|
expect(parts.length).toBe(3)
|
||||||
|
// IV should be 16 bytes = 32 hex chars
|
||||||
|
expect(parts[0].length).toBe(32)
|
||||||
|
expect(parts[0]).toMatch(/^[0-9a-f]+$/)
|
||||||
|
// Auth tag should be 16 bytes = 32 hex chars
|
||||||
|
expect(parts[1].length).toBe(32)
|
||||||
|
expect(parts[1]).toMatch(/^[0-9a-f]+$/)
|
||||||
|
// Encrypted data is hex
|
||||||
|
expect(parts[2]).toMatch(/^[0-9a-f]+$/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('decrypt throws on tampered ciphertext (auth tag verification)', () => {
|
||||||
|
const ciphertext = encrypt('sensitive wallet data')
|
||||||
|
const parts = ciphertext.split(':')
|
||||||
|
// Tamper with the encrypted data
|
||||||
|
const tampered = parts[0] + ':' + parts[1] + ':' + 'ff'.repeat(parts[2].length / 2)
|
||||||
|
expect(() => decrypt(tampered)).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('decrypt throws on tampered auth tag', () => {
|
||||||
|
const ciphertext = encrypt('sensitive wallet data')
|
||||||
|
const parts = ciphertext.split(':')
|
||||||
|
// Tamper with the auth tag
|
||||||
|
const tampered = parts[0] + ':' + '00'.repeat(16) + ':' + parts[2]
|
||||||
|
expect(() => decrypt(tampered)).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles empty string encryption', () => {
|
||||||
|
const ciphertext = encrypt('')
|
||||||
|
const decrypted = decrypt(ciphertext)
|
||||||
|
expect(decrypted).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles unicode content', () => {
|
||||||
|
const plaintext = '₿⚡ Bitcoin Lightning 🟠'
|
||||||
|
const ciphertext = encrypt(plaintext)
|
||||||
|
const decrypted = decrypt(ciphertext)
|
||||||
|
expect(decrypted).toBe(plaintext)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user