Files
fn_registry/functions/cybersecurity/e2e_messaging_crypto_test.go
2026-06-04 23:44:39 +02:00

305 lines
8.6 KiB
Go

package cybersecurity
import (
"bytes"
"testing"
)
// --- GenerateIdentity ---
func TestGenerateIdentity(t *testing.T) {
t.Run("genera keypairs con longitudes correctas", func(t *testing.T) {
id, err := GenerateIdentity()
if err != nil {
t.Fatalf("GenerateIdentity() error = %v", err)
}
if len(id.SignPub) != 32 {
t.Errorf("SignPub len = %d, want 32", len(id.SignPub))
}
if len(id.SignPriv) != 64 {
t.Errorf("SignPriv len = %d, want 64", len(id.SignPriv))
}
if len(id.KexPub) != 32 {
t.Errorf("KexPub len = %d, want 32", len(id.KexPub))
}
if len(id.KexPriv) != 32 {
t.Errorf("KexPriv len = %d, want 32", len(id.KexPriv))
}
})
t.Run("dos llamadas producen identidades distintas", func(t *testing.T) {
id1, err1 := GenerateIdentity()
id2, err2 := GenerateIdentity()
if err1 != nil || err2 != nil {
t.Fatal("GenerateIdentity() error inesperado")
}
if bytes.Equal(id1.SignPub, id2.SignPub) {
t.Error("SignPub idénticos en dos identidades distintas")
}
if bytes.Equal(id1.KexPub, id2.KexPub) {
t.Error("KexPub idénticos en dos identidades distintas")
}
})
}
// --- SealAEAD / OpenAEAD ---
func TestSealOpenAEAD(t *testing.T) {
key := make([]byte, 32)
for i := range key {
key[i] = byte(i + 1)
}
plaintext := []byte("mensaje secreto del bus de mensajería")
aad := []byte("room:sala-general")
t.Run("round-trip con aad", func(t *testing.T) {
nonce, ct, err := SealAEAD(key, plaintext, aad)
if err != nil {
t.Fatalf("SealAEAD error = %v", err)
}
got, err := OpenAEAD(key, nonce, ct, aad)
if err != nil {
t.Fatalf("OpenAEAD error = %v", err)
}
if !bytes.Equal(got, plaintext) {
t.Errorf("got %q, want %q", got, plaintext)
}
})
t.Run("round-trip sin aad (nil)", func(t *testing.T) {
nonce, ct, err := SealAEAD(key, plaintext, nil)
if err != nil {
t.Fatalf("SealAEAD error = %v", err)
}
got, err := OpenAEAD(key, nonce, ct, nil)
if err != nil {
t.Fatalf("OpenAEAD error = %v", err)
}
if !bytes.Equal(got, plaintext) {
t.Errorf("got %q, want %q", got, plaintext)
}
})
t.Run("error con clave de longitud incorrecta", func(t *testing.T) {
_, _, err := SealAEAD(key[:16], plaintext, nil)
if err == nil {
t.Error("esperaba error con clave de 16 bytes, got nil")
}
})
t.Run("error de autenticacion con ciphertext modificado", func(t *testing.T) {
nonce, ct, err := SealAEAD(key, plaintext, aad)
if err != nil {
t.Fatalf("SealAEAD error = %v", err)
}
ct[0] ^= 0xFF // corromper el primer byte
_, err = OpenAEAD(key, nonce, ct, aad)
if err == nil {
t.Error("esperaba error de autenticación con ciphertext corrupto, got nil")
}
})
t.Run("error de autenticacion con aad distinto", func(t *testing.T) {
nonce, ct, err := SealAEAD(key, plaintext, aad)
if err != nil {
t.Fatalf("SealAEAD error = %v", err)
}
_, err = OpenAEAD(key, nonce, ct, []byte("room:otra-sala"))
if err == nil {
t.Error("esperaba error de autenticación con aad distinto, got nil")
}
})
t.Run("nonces distintos en llamadas sucesivas", func(t *testing.T) {
n1, _, err1 := SealAEAD(key, plaintext, nil)
n2, _, err2 := SealAEAD(key, plaintext, nil)
if err1 != nil || err2 != nil {
t.Fatal("SealAEAD error inesperado")
}
if bytes.Equal(n1, n2) {
t.Error("nonces iguales en dos llamadas sucesivas (no aleatorios)")
}
})
}
// --- SealKeyBox / OpenKeyBox ---
func TestSealOpenKeyBox(t *testing.T) {
t.Run("round-trip con identidad generada", func(t *testing.T) {
id, err := GenerateIdentity()
if err != nil {
t.Fatalf("GenerateIdentity error = %v", err)
}
roomKey := make([]byte, 32)
for i := range roomKey {
roomKey[i] = byte(i + 42)
}
sealed, err := SealKeyBox(id.KexPub, roomKey)
if err != nil {
t.Fatalf("SealKeyBox error = %v", err)
}
opened, err := OpenKeyBox(id.KexPub, id.KexPriv, sealed)
if err != nil {
t.Fatalf("OpenKeyBox error = %v", err)
}
if !bytes.Equal(opened, roomKey) {
t.Errorf("got %x, want %x", opened, roomKey)
}
})
t.Run("error con recipientKexPub de longitud incorrecta", func(t *testing.T) {
_, err := SealKeyBox(make([]byte, 16), []byte("secret"))
if err == nil {
t.Error("esperaba error con kexPub de 16 bytes, got nil")
}
})
t.Run("error al abrir con clave equivocada", func(t *testing.T) {
id, _ := GenerateIdentity()
other, _ := GenerateIdentity()
sealed, err := SealKeyBox(id.KexPub, []byte("roomkey"))
if err != nil {
t.Fatalf("SealKeyBox error = %v", err)
}
_, err = OpenKeyBox(other.KexPub, other.KexPriv, sealed)
if err == nil {
t.Error("esperaba error al abrir con keypair distinto, got nil")
}
})
t.Run("error con mensaje truncado", func(t *testing.T) {
id, _ := GenerateIdentity()
_, err := OpenKeyBox(id.KexPub, id.KexPriv, []byte("corto"))
if err == nil {
t.Error("esperaba error con sealedMsg truncado, got nil")
}
})
}
// --- SignEd25519 / VerifyEd25519 ---
func TestSignVerifyEd25519(t *testing.T) {
t.Run("firma y verificacion exitosa", func(t *testing.T) {
id, err := GenerateIdentity()
if err != nil {
t.Fatalf("GenerateIdentity error = %v", err)
}
msg := []byte("evento:room_key_rotation:v2")
sig := SignEd25519(id.SignPriv, msg)
if len(sig) != 64 {
t.Errorf("sig len = %d, want 64", len(sig))
}
if !VerifyEd25519(id.SignPub, msg, sig) {
t.Error("VerifyEd25519 devolvió false para firma válida")
}
})
t.Run("firma es determinista (misma entrada, misma firma)", func(t *testing.T) {
id, _ := GenerateIdentity()
msg := []byte("determinismo criptografico")
sig1 := SignEd25519(id.SignPriv, msg)
sig2 := SignEd25519(id.SignPriv, msg)
if !bytes.Equal(sig1, sig2) {
t.Error("Ed25519 debe ser determinista: mismas entradas deben producir misma firma")
}
})
t.Run("falla con mensaje modificado", func(t *testing.T) {
id, _ := GenerateIdentity()
msg := []byte("mensaje original")
sig := SignEd25519(id.SignPriv, msg)
modified := []byte("mensaje modificado")
if VerifyEd25519(id.SignPub, modified, sig) {
t.Error("VerifyEd25519 devolvió true para mensaje modificado")
}
})
t.Run("falla con clave publica incorrecta", func(t *testing.T) {
id1, _ := GenerateIdentity()
id2, _ := GenerateIdentity()
msg := []byte("autenticidad del remitente")
sig := SignEd25519(id1.SignPriv, msg)
if VerifyEd25519(id2.SignPub, msg, sig) {
t.Error("VerifyEd25519 devolvió true con clave pública de otra identidad")
}
})
t.Run("falla con firma corrupta", func(t *testing.T) {
id, _ := GenerateIdentity()
msg := []byte("integridad")
sig := SignEd25519(id.SignPriv, msg)
sig[0] ^= 0xFF
if VerifyEd25519(id.SignPub, msg, sig) {
t.Error("VerifyEd25519 devolvió true con firma corrupta")
}
})
}
// --- Integración: flujo completo megolm-reducido ---
func TestE2EMessagingFlow(t *testing.T) {
t.Run("flujo completo: generar identidad, distribuir clave de sala, cifrar y firmar mensaje", func(t *testing.T) {
// Servidor genera identidad
server, err := GenerateIdentity()
if err != nil {
t.Fatalf("GenerateIdentity server: %v", err)
}
// Usuario genera identidad
user, err := GenerateIdentity()
if err != nil {
t.Fatalf("GenerateIdentity user: %v", err)
}
// Servidor genera clave de sala y la distribuye al usuario cifrada con su KexPub
roomKey := make([]byte, 32)
for i := range roomKey {
roomKey[i] = byte(i)
}
sealedKey, err := SealKeyBox(user.KexPub, roomKey)
if err != nil {
t.Fatalf("SealKeyBox: %v", err)
}
// Usuario desella la clave de sala
gotRoomKey, err := OpenKeyBox(user.KexPub, user.KexPriv, sealedKey)
if err != nil {
t.Fatalf("OpenKeyBox: %v", err)
}
if !bytes.Equal(gotRoomKey, roomKey) {
t.Fatal("clave de sala distribuida no coincide")
}
// Usuario cifra un mensaje con la clave de sala
plainMsg := []byte("hola sala, este es mi primer mensaje cifrado e2e")
aad := []byte("room:sala-secreta:seq:1")
nonce, ct, err := SealAEAD(gotRoomKey, plainMsg, aad)
if err != nil {
t.Fatalf("SealAEAD: %v", err)
}
// Usuario firma el ciphertext para autenticación del remitente
sig := SignEd25519(user.SignPriv, ct)
// Receptor verifica firma del remitente
if !VerifyEd25519(user.SignPub, ct, sig) {
t.Fatal("verificación de firma del remitente falló")
}
// Receptor descifra el mensaje
decrypted, err := OpenAEAD(gotRoomKey, nonce, ct, aad)
if err != nil {
t.Fatalf("OpenAEAD: %v", err)
}
if !bytes.Equal(decrypted, plainMsg) {
t.Errorf("mensaje descifrado %q != original %q", decrypted, plainMsg)
}
// Servidor tiene distinta identidad que el usuario (las claves no se confunden)
if bytes.Equal(server.SignPub, user.SignPub) {
t.Error("server y user tienen la misma clave pública de firma")
}
})
}