mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 12:17:06 +00:00
146 lines
4.5 KiB
Go
146 lines
4.5 KiB
Go
package extensions_test
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gosrc.io/xmpp/stanza"
|
|
|
|
"dev.narayana.im/narayana/telegabber/xmpp/extensions"
|
|
)
|
|
|
|
// parseMessage decodes a full stanza through the exact path telegabber's
|
|
// component actually uses (stanza.NextPacket, requiring a top-level
|
|
// jabber:component:accept namespace - confirmed by reading go-xmpp's
|
|
// parser.go directly), not just a raw xml.Unmarshal of an isolated
|
|
// struct - this is what actually proves the TypeRegistry dispatch works,
|
|
// not just that my own struct tags happen to parse in isolation.
|
|
func parseMessage(t *testing.T, xmlStr string) stanza.Message {
|
|
t.Helper()
|
|
dec := xml.NewDecoder(strings.NewReader(xmlStr))
|
|
pkt, err := stanza.NextPacket(dec)
|
|
if err != nil {
|
|
t.Fatalf("NextPacket: %v", err)
|
|
}
|
|
msg, ok := pkt.(stanza.Message)
|
|
if !ok {
|
|
t.Fatalf("expected stanza.Message, got %T", pkt)
|
|
}
|
|
return msg
|
|
}
|
|
|
|
func TestOMEMO0EncryptedRoundTrip(t *testing.T) {
|
|
msg := parseMessage(t, `<message xmlns='jabber:component:accept' from='a@b' to='c@d' type='chat'>
|
|
<encrypted xmlns='eu.siacs.conversations.axolotl'>
|
|
<header sid='27183'>
|
|
<key rid='31415' prekey='true'>a2V5MQ==</key>
|
|
<key rid='12321'>a2V5Mg==</key>
|
|
<iv>aXY=</iv>
|
|
</header>
|
|
<payload>cGF5bG9hZA==</payload>
|
|
</encrypted>
|
|
</message>`)
|
|
|
|
var enc extensions.OMEMO0Encrypted
|
|
if !msg.Get(&enc) {
|
|
t.Fatalf("expected to find OMEMO0Encrypted extension")
|
|
}
|
|
if enc.Namespace() != "eu.siacs.conversations.axolotl" {
|
|
t.Fatalf("got namespace %q", enc.Namespace())
|
|
}
|
|
if enc.Header.SID != 27183 {
|
|
t.Fatalf("got sid %d, want 27183", enc.Header.SID)
|
|
}
|
|
if len(enc.Header.Keys) != 2 {
|
|
t.Fatalf("expected 2 keys, got %d", len(enc.Header.Keys))
|
|
}
|
|
if !enc.Header.Keys[0].PreKey {
|
|
t.Fatalf("expected first key to be a prekey")
|
|
}
|
|
if enc.Header.Keys[1].PreKey {
|
|
t.Fatalf("expected second key to not be a prekey")
|
|
}
|
|
if enc.Header.Keys[0].Text != "a2V5MQ==" {
|
|
t.Fatalf("got key text %q", enc.Header.Keys[0].Text)
|
|
}
|
|
if enc.Header.IV != "aXY=" {
|
|
t.Fatalf("got iv %q", enc.Header.IV)
|
|
}
|
|
if enc.Payload != "cGF5bG9hZA==" {
|
|
t.Fatalf("got payload %q", enc.Payload)
|
|
}
|
|
}
|
|
|
|
// TestOMEMOEncryptedNamespaceRecovered confirms the central design point
|
|
// for OMEMOEncrypted: it is registered under BOTH urn:xmpp:omemo:1 and
|
|
// urn:xmpp:omemo:2 (identical shape), and after decoding, XMLName.Space
|
|
// correctly reflects whichever one an incoming stanza actually used - this
|
|
// is what lets the decrypt hook later distinguish Omemo1 from Omemo2
|
|
// without needing two separate Go types.
|
|
func TestOMEMOEncryptedNamespaceRecovered(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
ns string
|
|
}{
|
|
{"omemo1", "urn:xmpp:omemo:1"},
|
|
{"omemo2", "urn:xmpp:omemo:2"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
msg := parseMessage(t, `<message xmlns='jabber:component:accept' from='a@b' to='c@d' type='chat'>
|
|
<encrypted xmlns='`+tc.ns+`'>
|
|
<header sid='27183'>
|
|
<keys jid='juliet@capulet.lit'>
|
|
<key rid='31415' kex='true'>a2V5</key>
|
|
</keys>
|
|
</header>
|
|
<payload>cGF5bG9hZA==</payload>
|
|
</encrypted>
|
|
</message>`)
|
|
|
|
var enc extensions.OMEMOEncrypted
|
|
if !msg.Get(&enc) {
|
|
t.Fatalf("expected to find OMEMOEncrypted extension")
|
|
}
|
|
if enc.Namespace() != tc.ns {
|
|
t.Fatalf("got namespace %q, want %q", enc.Namespace(), tc.ns)
|
|
}
|
|
if enc.Header.SID != 27183 {
|
|
t.Fatalf("got sid %d, want 27183", enc.Header.SID)
|
|
}
|
|
if len(enc.Header.Keys) != 1 || enc.Header.Keys[0].JID != "juliet@capulet.lit" {
|
|
t.Fatalf("got keys %+v", enc.Header.Keys)
|
|
}
|
|
if len(enc.Header.Keys[0].Keys) != 1 || !enc.Header.Keys[0].Keys[0].Kex {
|
|
t.Fatalf("got inner keys %+v", enc.Header.Keys[0].Keys)
|
|
}
|
|
if enc.Header.Keys[0].Keys[0].Text != "a2V5" {
|
|
t.Fatalf("got key text %q", enc.Header.Keys[0].Keys[0].Text)
|
|
}
|
|
if enc.Payload != "cGF5bG9hZA==" {
|
|
t.Fatalf("got payload %q", enc.Payload)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEMERoundTrip(t *testing.T) {
|
|
msg := parseMessage(t, `<message xmlns='jabber:component:accept' from='a@b' to='c@d' type='chat'>
|
|
<body>[This message is OMEMO end-to-end encrypted]</body>
|
|
<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:omemo:2' name='OMEMO'/>
|
|
</message>`)
|
|
|
|
var eme extensions.EME
|
|
if !msg.Get(&eme) {
|
|
t.Fatalf("expected to find EME extension")
|
|
}
|
|
if eme.Namespace() != "urn:xmpp:eme:0" {
|
|
t.Fatalf("got namespace %q", eme.Namespace())
|
|
}
|
|
if eme.EncryptionNamespace != "urn:xmpp:omemo:2" {
|
|
t.Fatalf("got encryption namespace %q", eme.EncryptionNamespace)
|
|
}
|
|
if eme.Name != "OMEMO" {
|
|
t.Fatalf("got name %q", eme.Name)
|
|
}
|
|
}
|