telegabber/e2ee/omemo/envelope_test.go
2026-07-28 20:32:56 -04:00

136 lines
3.4 KiB
Go

package omemo
import (
"bytes"
"reflect"
"testing"
)
func TestEncryptOuterRoundTrip(t *testing.T) {
for _, tc := range []struct {
name string
version Version
}{
{"omemo0", Omemo0},
{"omemo1", Omemo1},
{"omemo2", Omemo2},
} {
t.Run(tc.name, func(t *testing.T) {
plaintext := []byte("hello from telegram")
payload, err := EncryptOuter(tc.version, plaintext, "gateway@example.com")
if err != nil {
t.Fatalf("EncryptOuter: %v", err)
}
if len(payload.Ciphertext) == 0 {
t.Fatalf("expected non-empty ciphertext")
}
if tc.version == Omemo0 && len(payload.IV) != omemo0IVLen {
t.Fatalf("expected a %d-byte IV for omemo0, got %d", omemo0IVLen, len(payload.IV))
}
if tc.version != Omemo0 && payload.IV != nil {
t.Fatalf("expected no IV for %s (receiver re-derives it), got %d bytes", tc.name, len(payload.IV))
}
got, err := DecryptOuter(tc.version, payload)
if err != nil {
t.Fatalf("DecryptOuter: %v", err)
}
if !bytes.Equal(got, plaintext) {
t.Fatalf("round trip mismatch: got %q, want %q", got, plaintext)
}
})
}
}
func TestEncryptOuterTamperDetection(t *testing.T) {
for _, tc := range []struct {
name string
version Version
}{
{"omemo0", Omemo0},
{"omemo1", Omemo1},
{"omemo2", Omemo2},
} {
t.Run(tc.name, func(t *testing.T) {
payload, err := EncryptOuter(tc.version, []byte("secret"), "gateway@example.com")
if err != nil {
t.Fatalf("EncryptOuter: %v", err)
}
tampered := *payload
tampered.Ciphertext = append([]byte{}, payload.Ciphertext...)
tampered.Ciphertext[0] ^= 0xFF
if _, err := DecryptOuter(tc.version, &tampered); err == nil {
t.Fatalf("expected DecryptOuter to reject tampered ciphertext")
}
})
}
}
func TestSCERoundTrip(t *testing.T) {
plaintext := []byte("hello sce")
t.Run("v0", func(t *testing.T) {
xmlBytes, err := sceEncodeV0(plaintext, "alice@example.com")
if err != nil {
t.Fatalf("sceEncodeV0: %v", err)
}
got, err := sceDecodeV0(xmlBytes)
if err != nil {
t.Fatalf("sceDecodeV0: %v", err)
}
if !bytes.Equal(got, plaintext) {
t.Fatalf("got %q, want %q", got, plaintext)
}
})
t.Run("v1", func(t *testing.T) {
xmlBytes, err := sceEncodeV1(plaintext, "alice@example.com")
if err != nil {
t.Fatalf("sceEncodeV1: %v", err)
}
got, err := sceDecodeV1(xmlBytes)
if err != nil {
t.Fatalf("sceDecodeV1: %v", err)
}
if !bytes.Equal(got, plaintext) {
t.Fatalf("got %q, want %q", got, plaintext)
}
})
}
func TestWireEnvelopeEncodeDecode(t *testing.T) {
env := &WireEnvelope{
Version: Omemo2,
SenderSID: 12345,
Payload: []byte("ciphertext-bytes"),
Keys: []WireKey{
{RecipientJID: "bob@example.com", DeviceID: 1, IsPreKey: true, Ciphertext: []byte("key-ciphertext-1")},
{RecipientJID: "bob@example.com", DeviceID: 2, IsPreKey: false, Ciphertext: []byte("key-ciphertext-2")},
},
}
raw, err := env.Encode()
if err != nil {
t.Fatalf("Encode: %v", err)
}
got, err := DecodeWireEnvelope(raw)
if err != nil {
t.Fatalf("DecodeWireEnvelope: %v", err)
}
if got.Version != env.Version || got.SenderSID != env.SenderSID || !bytes.Equal(got.Payload, env.Payload) {
t.Fatalf("round trip mismatch: got %+v", got)
}
if len(got.Keys) != len(env.Keys) {
t.Fatalf("expected %d keys, got %d", len(env.Keys), len(got.Keys))
}
for i := range env.Keys {
if !reflect.DeepEqual(got.Keys[i], env.Keys[i]) {
t.Fatalf("key %d mismatch: got %+v, want %+v", i, got.Keys[i], env.Keys[i])
}
}
}