Files
himbocrypt/pkg/engine/engine_test.go

893 lines
24 KiB
Go

package engine
import (
"bytes"
"crypto/ecdh"
"crypto/rand"
"encoding/hex"
"strings"
"testing"
)
func TestNewEngine(t *testing.T) {
e := NewEngine()
if e == nil {
t.Fatal("NewEngine() returned nil")
}
}
func TestGenerateKeyPair(t *testing.T) {
e := NewEngine()
kp, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
if kp.PrivateKey == nil {
t.Error("PrivateKey is nil")
}
if kp.PublicKey == nil {
t.Error("PublicKey is nil")
}
// Verify key lengths (X25519 keys are 32 bytes)
if len(kp.PrivateKey.Bytes()) != 32 {
t.Errorf("PrivateKey length = %d, want 32", len(kp.PrivateKey.Bytes()))
}
if len(kp.PublicKey.Bytes()) != 32 {
t.Errorf("PublicKey length = %d, want 32", len(kp.PublicKey.Bytes()))
}
}
func TestGenerateKeyPair_Uniqueness(t *testing.T) {
e := NewEngine()
kp1, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
kp2, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
if bytes.Equal(kp1.PrivateKey.Bytes(), kp2.PrivateKey.Bytes()) {
t.Error("Two generated key pairs have identical private keys")
}
if bytes.Equal(kp1.PublicKey.Bytes(), kp2.PublicKey.Bytes()) {
t.Error("Two generated key pairs have identical public keys")
}
}
func TestEncodePublicKey(t *testing.T) {
e := NewEngine()
kp, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
encoded := kp.EncodePublicKey()
// Should be 64 hex characters (32 bytes * 2)
if len(encoded) != 64 {
t.Errorf("EncodePublicKey() length = %d, want 64", len(encoded))
}
// Should be valid hex
_, err = hex.DecodeString(encoded)
if err != nil {
t.Errorf("EncodePublicKey() produced invalid hex: %v", err)
}
}
func TestEncodePrivateKey(t *testing.T) {
e := NewEngine()
kp, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
encoded := kp.EncodePrivateKey()
// Should be 64 hex characters (32 bytes * 2)
if len(encoded) != 64 {
t.Errorf("EncodePrivateKey() length = %d, want 64", len(encoded))
}
// Should be valid hex
_, err = hex.DecodeString(encoded)
if err != nil {
t.Errorf("EncodePrivateKey() produced invalid hex: %v", err)
}
}
func TestDecodePrivateKey(t *testing.T) {
e := NewEngine()
kp, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
encoded := kp.EncodePrivateKey()
decoded, err := e.DecodePrivateKey(encoded)
if err != nil {
t.Fatalf("DecodePrivateKey() error = %v", err)
}
if !bytes.Equal(decoded.Bytes(), kp.PrivateKey.Bytes()) {
t.Error("Decoded private key does not match original")
}
}
func TestDecodePublicKey(t *testing.T) {
e := NewEngine()
kp, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
encoded := kp.EncodePublicKey()
decoded, err := e.DecodePublicKey(encoded)
if err != nil {
t.Fatalf("DecodePublicKey() error = %v", err)
}
if !bytes.Equal(decoded.Bytes(), kp.PublicKey.Bytes()) {
t.Error("Decoded public key does not match original")
}
}
func TestDecodePrivateKey_InvalidHex(t *testing.T) {
e := NewEngine()
_, err := e.DecodePrivateKey("not-valid-hex!")
if err == nil {
t.Error("DecodePrivateKey() should error on invalid hex")
}
}
func TestDecodePublicKey_InvalidHex(t *testing.T) {
e := NewEngine()
_, err := e.DecodePublicKey("not-valid-hex!")
if err == nil {
t.Error("DecodePublicKey() should error on invalid hex")
}
}
func TestDecodePrivateKey_WrongLength(t *testing.T) {
e := NewEngine()
// 16 bytes instead of 32
shortKey := strings.Repeat("00", 16)
_, err := e.DecodePrivateKey(shortKey)
if err == nil {
t.Error("DecodePrivateKey() should error on wrong length key")
}
}
func TestDecodePublicKey_WrongLength(t *testing.T) {
e := NewEngine()
// 16 bytes instead of 32
shortKey := strings.Repeat("00", 16)
_, err := e.DecodePublicKey(shortKey)
if err == nil {
t.Error("DecodePublicKey() should error on wrong length key")
}
}
func TestEncryptDecrypt_RoundTrip(t *testing.T) {
e := NewEngine()
// Generate sender and recipient key pairs
sender, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() for sender error = %v", err)
}
recipient, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() for recipient error = %v", err)
}
plaintext := []byte("Hello, World! This is a secret message.")
// Encrypt
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
// Decrypt
decrypted, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("Decrypted message = %q, want %q", decrypted, plaintext)
}
}
func TestEncryptDecrypt_EmptyMessage(t *testing.T) {
e := NewEngine()
sender, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() for sender error = %v", err)
}
recipient, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() for recipient error = %v", err)
}
plaintext := []byte("")
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
decrypted, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("Decrypted message = %q, want empty string", decrypted)
}
}
func TestEncryptDecrypt_LargeMessage(t *testing.T) {
e := NewEngine()
sender, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() for sender error = %v", err)
}
recipient, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() for recipient error = %v", err)
}
// Create a 1MB message
plaintext := make([]byte, 1024*1024)
if _, err := rand.Read(plaintext); err != nil {
t.Fatalf("Failed to generate random plaintext: %v", err)
}
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
decrypted, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Error("Decrypted large message does not match original")
}
}
func TestEncryptDecrypt_BinaryData(t *testing.T) {
e := NewEngine()
sender, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() for sender error = %v", err)
}
recipient, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() for recipient error = %v", err)
}
// Binary data with null bytes and special characters
plaintext := []byte{0x00, 0x01, 0x02, 0xff, 0xfe, 0x00, 0x00, 0x10, 0x20}
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
decrypted, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("Decrypted binary data = %v, want %v", decrypted, plaintext)
}
}
func TestDecrypt_WrongRecipientKey(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
wrongRecipient, _ := e.GenerateKeyPair()
plaintext := []byte("Secret message")
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
// Try to decrypt with wrong recipient's private key
_, err = e.Decrypt(wrongRecipient.PrivateKey, sender.PublicKey, ciphertext)
if err == nil {
t.Error("Decrypt() should fail with wrong recipient key")
}
}
func TestDecrypt_WrongSenderPublicKey(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
wrongSender, _ := e.GenerateKeyPair()
plaintext := []byte("Secret message")
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
// Try to decrypt with wrong sender's public key
_, err = e.Decrypt(recipient.PrivateKey, wrongSender.PublicKey, ciphertext)
if err == nil {
t.Error("Decrypt() should fail with wrong sender public key")
}
}
func TestDecrypt_TamperedCiphertext(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
plaintext := []byte("Secret message")
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
// Tamper with the ciphertext (flip a bit in the actual ciphertext portion)
if len(ciphertext) > 60 {
ciphertext[60] ^= 0x01
}
_, err = e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err == nil {
t.Error("Decrypt() should fail with tampered ciphertext")
}
}
func TestDecrypt_TruncatedMessage(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
plaintext := []byte("Secret message")
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
// Truncate the message
truncated := ciphertext[:len(ciphertext)-10]
_, err = e.Decrypt(recipient.PrivateKey, sender.PublicKey, truncated)
if err == nil {
t.Error("Decrypt() should fail with truncated ciphertext")
}
}
func TestDecrypt_MessageTooShort(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
// Message shorter than pubKeySize + nonceSize (32 + 24 = 56 bytes)
shortMessage := make([]byte, 50)
_, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, shortMessage)
if err == nil {
t.Error("Decrypt() should fail with message too short")
}
if err.Error() != "message too short" {
t.Errorf("Decrypt() error = %v, want 'message too short'", err)
}
}
func TestDecrypt_InvalidEphemeralPublicKey(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
// Create a message with invalid ephemeral public key (all zeros)
invalidMessage := make([]byte, 100)
_, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, invalidMessage)
// This should fail during decryption due to authentication failure
if err == nil {
t.Error("Decrypt() should fail with invalid ephemeral public key")
}
}
func TestEncrypt_DifferentCiphertextEachTime(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
plaintext := []byte("Same message")
ciphertext1, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("First Encrypt() error = %v", err)
}
ciphertext2, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Second Encrypt() error = %v", err)
}
// Due to ephemeral keys and random nonce, ciphertexts should be different
if bytes.Equal(ciphertext1, ciphertext2) {
t.Error("Encrypting same message twice should produce different ciphertexts (due to ephemeral keys)")
}
// But both should decrypt to the same plaintext
decrypted1, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext1)
if err != nil {
t.Fatalf("Decrypt first error = %v", err)
}
decrypted2, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext2)
if err != nil {
t.Fatalf("Decrypt second error = %v", err)
}
if !bytes.Equal(decrypted1, plaintext) || !bytes.Equal(decrypted2, plaintext) {
t.Error("Both ciphertexts should decrypt to the same plaintext")
}
}
func TestCiphertextFormat(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
plaintext := []byte("Test message")
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
// Ciphertext format: [EphemeralPub (32)] [Nonce (24)] [Ciphertext + Tag (len(plaintext) + 16)]
expectedMinLength := 32 + 24 + len(plaintext) + 16 // 16 is Poly1305 tag size
if len(ciphertext) != expectedMinLength {
t.Errorf("Ciphertext length = %d, want %d", len(ciphertext), expectedMinLength)
}
}
func TestKeyPairConsistency(t *testing.T) {
e := NewEngine()
kp, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
// Public key should be derivable from private key
derivedPub := kp.PrivateKey.PublicKey()
if !bytes.Equal(derivedPub.Bytes(), kp.PublicKey.Bytes()) {
t.Error("Public key should be consistent with private key")
}
}
func TestEncodeDecodeRoundTrip(t *testing.T) {
e := NewEngine()
kp, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
// Test private key round trip
privEncoded := kp.EncodePrivateKey()
privDecoded, err := e.DecodePrivateKey(privEncoded)
if err != nil {
t.Fatalf("DecodePrivateKey() error = %v", err)
}
// Test public key round trip
pubEncoded := kp.EncodePublicKey()
pubDecoded, err := e.DecodePublicKey(pubEncoded)
if err != nil {
t.Fatalf("DecodePublicKey() error = %v", err)
}
// Use decoded keys for encryption/decryption
recipient, _ := e.GenerateKeyPair()
plaintext := []byte("Round trip test")
ciphertext, err := e.Encrypt(privDecoded, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt with decoded key error = %v", err)
}
decrypted, err := e.Decrypt(recipient.PrivateKey, pubDecoded, ciphertext)
if err != nil {
t.Fatalf("Decrypt with decoded key error = %v", err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Error("Decoded keys should work correctly for encryption/decryption")
}
}
func TestMultipleRecipients(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient1, _ := e.GenerateKeyPair()
recipient2, _ := e.GenerateKeyPair()
plaintext := []byte("Message for multiple recipients")
// Encrypt for recipient1
ciphertext1, err := e.Encrypt(sender.PrivateKey, recipient1.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt for recipient1 error = %v", err)
}
// Encrypt for recipient2
ciphertext2, err := e.Encrypt(sender.PrivateKey, recipient2.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt for recipient2 error = %v", err)
}
// Both should be able to decrypt their own messages
decrypted1, err := e.Decrypt(recipient1.PrivateKey, sender.PublicKey, ciphertext1)
if err != nil {
t.Fatalf("Decrypt for recipient1 error = %v", err)
}
if !bytes.Equal(decrypted1, plaintext) {
t.Error("Recipient1 should be able to decrypt their message")
}
decrypted2, err := e.Decrypt(recipient2.PrivateKey, sender.PublicKey, ciphertext2)
if err != nil {
t.Fatalf("Decrypt for recipient2 error = %v", err)
}
if !bytes.Equal(decrypted2, plaintext) {
t.Error("Recipient2 should be able to decrypt their message")
}
// Cross decryption should fail
_, err = e.Decrypt(recipient1.PrivateKey, sender.PublicKey, ciphertext2)
if err == nil {
t.Error("Recipient1 should NOT be able to decrypt recipient2's message")
}
_, err = e.Decrypt(recipient2.PrivateKey, sender.PublicKey, ciphertext1)
if err == nil {
t.Error("Recipient2 should NOT be able to decrypt recipient1's message")
}
}
func TestUnicodeMessage(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
// Unicode message with various scripts
plaintext := []byte("Hello 世界! Привет мир! 🔐🔑")
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
decrypted, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("Decrypted unicode message = %q, want %q", decrypted, plaintext)
}
}
// Benchmark tests
func BenchmarkGenerateKeyPair(b *testing.B) {
e := NewEngine()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := e.GenerateKeyPair()
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkEncrypt(b *testing.B) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
plaintext := []byte("Benchmark encryption test message")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkDecrypt(b *testing.B) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
plaintext := []byte("Benchmark decryption test message")
ciphertext, _ := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkEncryptLargeMessage(b *testing.B) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
plaintext := make([]byte, 1024*1024) // 1MB
rand.Read(plaintext)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
b.Fatal(err)
}
}
}
// Test that verifies the authentication property - message must come from correct sender
func TestSenderAuthentication(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
imposter, _ := e.GenerateKeyPair()
plaintext := []byte("Authenticated message")
// Sender encrypts
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
// Recipient tries to decrypt thinking it's from imposter - should fail
_, err = e.Decrypt(recipient.PrivateKey, imposter.PublicKey, ciphertext)
if err == nil {
t.Error("Decryption should fail when wrong sender public key is provided")
}
// Recipient decrypts with correct sender public key - should succeed
decrypted, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
t.Fatalf("Decrypt() with correct sender should succeed: %v", err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Error("Decrypted message should match plaintext")
}
}
// Test forward secrecy by verifying ephemeral keys are different each encryption
func TestForwardSecrecy(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
plaintext := []byte("Forward secrecy test")
ciphertext1, _ := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
ciphertext2, _ := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
// Extract ephemeral public keys (first 32 bytes)
ephemeralPub1 := ciphertext1[:32]
ephemeralPub2 := ciphertext2[:32]
// Ephemeral keys should be different for each encryption
if bytes.Equal(ephemeralPub1, ephemeralPub2) {
t.Error("Ephemeral public keys should be different for each encryption (forward secrecy)")
}
}
// Test with known test vectors (useful for cross-implementation testing)
func TestDecodeKnownKeys(t *testing.T) {
e := NewEngine()
// Generate a key pair and verify it can be encoded and decoded
kp, err := e.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair() error = %v", err)
}
privHex := kp.EncodePrivateKey()
pubHex := kp.EncodePublicKey()
// Verify the hex strings are lowercase
if privHex != strings.ToLower(privHex) {
t.Error("Private key hex should be lowercase")
}
if pubHex != strings.ToLower(pubHex) {
t.Error("Public key hex should be lowercase")
}
// Verify decoding
priv, err := e.DecodePrivateKey(privHex)
if err != nil {
t.Fatalf("DecodePrivateKey() error = %v", err)
}
pub, err := e.DecodePublicKey(pubHex)
if err != nil {
t.Fatalf("DecodePublicKey() error = %v", err)
}
// Verify the decoded public key matches derived public key
if !bytes.Equal(priv.PublicKey().Bytes(), pub.Bytes()) {
t.Error("Decoded public key should match public key derived from decoded private key")
}
}
// Test edge case: decode with uppercase hex (should work since hex.DecodeString handles both)
func TestDecodeUppercaseHex(t *testing.T) {
e := NewEngine()
kp, _ := e.GenerateKeyPair()
privHex := strings.ToUpper(kp.EncodePrivateKey())
pubHex := strings.ToUpper(kp.EncodePublicKey())
priv, err := e.DecodePrivateKey(privHex)
if err != nil {
t.Fatalf("DecodePrivateKey() should handle uppercase hex: %v", err)
}
pub, err := e.DecodePublicKey(pubHex)
if err != nil {
t.Fatalf("DecodePublicKey() should handle uppercase hex: %v", err)
}
if !bytes.Equal(priv.Bytes(), kp.PrivateKey.Bytes()) {
t.Error("Decoded private key should match original")
}
if !bytes.Equal(pub.Bytes(), kp.PublicKey.Bytes()) {
t.Error("Decoded public key should match original")
}
}
// Fuzz-like test with random message sizes
func TestRandomMessageSizes(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
sizes := []int{0, 1, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128, 256, 512, 1000, 4096}
for _, size := range sizes {
t.Run(string(rune(size)), func(t *testing.T) {
plaintext := make([]byte, size)
if size > 0 {
rand.Read(plaintext)
}
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() with size %d error = %v", size, err)
}
decrypted, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
t.Fatalf("Decrypt() with size %d error = %v", size, err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("Round trip failed for size %d", size)
}
})
}
}
// Test concurrent encryption/decryption
func TestConcurrentOperations(t *testing.T) {
e := NewEngine()
sender, _ := e.GenerateKeyPair()
recipient, _ := e.GenerateKeyPair()
const numGoroutines = 100
done := make(chan bool, numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func(id int) {
plaintext := []byte("Concurrent message " + string(rune('A'+id%26)))
ciphertext, err := e.Encrypt(sender.PrivateKey, recipient.PublicKey, plaintext)
if err != nil {
t.Errorf("Goroutine %d: Encrypt() error = %v", id, err)
done <- false
return
}
decrypted, err := e.Decrypt(recipient.PrivateKey, sender.PublicKey, ciphertext)
if err != nil {
t.Errorf("Goroutine %d: Decrypt() error = %v", id, err)
done <- false
return
}
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("Goroutine %d: decrypted != plaintext", id)
done <- false
return
}
done <- true
}(i)
}
for i := 0; i < numGoroutines; i++ {
<-done
}
}
// Test self-encryption (sender encrypts to themselves)
func TestSelfEncryption(t *testing.T) {
e := NewEngine()
user, _ := e.GenerateKeyPair()
plaintext := []byte("Message to myself")
ciphertext, err := e.Encrypt(user.PrivateKey, user.PublicKey, plaintext)
if err != nil {
t.Fatalf("Encrypt() error = %v", err)
}
decrypted, err := e.Decrypt(user.PrivateKey, user.PublicKey, ciphertext)
if err != nil {
t.Fatalf("Decrypt() error = %v", err)
}
if !bytes.Equal(decrypted, plaintext) {
t.Error("Self-encryption should work correctly")
}
}
// Ensure interface compatibility (Engine implements expected methods)
func TestEngineInterface(t *testing.T) {
e := NewEngine()
// Test that all public methods exist and have correct signatures
var _ func() (*KeyPair, error) = e.GenerateKeyPair
var _ func(string) (*ecdh.PrivateKey, error) = e.DecodePrivateKey
var _ func(string) (*ecdh.PublicKey, error) = e.DecodePublicKey
var _ func(*ecdh.PrivateKey, *ecdh.PublicKey, []byte) ([]byte, error) = e.Encrypt
var _ func(*ecdh.PrivateKey, *ecdh.PublicKey, []byte) ([]byte, error) = e.Decrypt
}