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

83 lines
2.6 KiB
Go

package omemo
import "encoding/xml"
// Device-list document parsing/encoding for all three OMEMO versions.
// Omemo1 and Omemo2 share an identical element shape (only the namespace
// differs - the XEP-0384 0.8.0 changelog only calls out SCE-related and
// namespace renames, nothing about the device-list/bundle schema itself,
// and 0.4.0/Omemo1's own changelog entry that introduced this schema
// doesn't mention it changing again later), so one xepDeviceList struct
// serves both: its XMLName tag omits the namespace, which makes Go's
// encoding/xml match on local name only during Unmarshal (so it accepts
// either namespace), while Marshal gets the right namespace by having the
// caller set XMLName explicitly before encoding.
// Namespace strings shared by both the device-list and bundle documents
// (see bundle.go) - both document kinds are just different local element
// names within the same OMEMO namespace.
const (
omemo0NS = "eu.siacs.conversations.axolotl"
omemo1NS = "urn:xmpp:omemo:1"
omemo2NS = "urn:xmpp:omemo:2"
)
type omemo0DeviceList struct {
XMLName xml.Name `xml:"eu.siacs.conversations.axolotl list"`
Devices []omemo0Device `xml:"device"`
}
type omemo0Device struct {
ID uint32 `xml:"id,attr"`
}
type xepDeviceList struct {
XMLName xml.Name `xml:"devices"`
Devices []xepDevice `xml:"device"`
}
type xepDevice struct {
ID uint32 `xml:"id,attr"`
Label string `xml:"label,attr,omitempty"`
LabelSig string `xml:"labelsig,attr,omitempty"`
}
// parseDeviceList extracts device ids from a device-list document, in
// whichever of the three known namespaces it's in.
func parseDeviceList(raw []byte) ([]uint32, error) {
var omemo0 omemo0DeviceList
if err := xml.Unmarshal(raw, &omemo0); err == nil {
ids := make([]uint32, len(omemo0.Devices))
for i, d := range omemo0.Devices {
ids[i] = d.ID
}
return ids, nil
}
var xep xepDeviceList
if err := xml.Unmarshal(raw, &xep); err != nil {
return nil, err
}
ids := make([]uint32, len(xep.Devices))
for i, d := range xep.Devices {
ids[i] = d.ID
}
return ids, nil
}
// encodeDeviceList produces a device-list document for this gateway's own
// published identity, in the namespace matching version.
func encodeDeviceList(version Version, deviceID uint32) ([]byte, error) {
if version == Omemo0 {
return xml.Marshal(omemo0DeviceList{Devices: []omemo0Device{{ID: deviceID}}})
}
ns := omemo1NS
if version == Omemo2 {
ns = omemo2NS
}
list := xepDeviceList{
XMLName: xml.Name{Space: ns, Local: "devices"},
Devices: []xepDevice{{ID: deviceID}},
}
return xml.Marshal(list)
}