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