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") } }) }