// Copyright Martin Dosch. // Use of this source code is governed by the BSD-2-clause // license that can be found in the LICENSE file. package main import ( "encoding/base64" "fmt" "log" "os" "runtime" "strings" "time" "github.com/ProtonMail/gopenpgp/v2/crypto" // MIT License "github.com/beevik/etree" // BSD-2-clause "github.com/xmppo/go-xmpp" // BSD-3-Clause ) func oxDeleteNodes(jid string, client *xmpp.Client, iqc chan xmpp.IQ) error { nodeListRequest := etree.NewDocument() nodeListRequest.WriteSettings.AttrSingleQuote = true query := nodeListRequest.CreateElement("query") query.CreateAttr("xmlns", nsDiscoItems) nlr, err := nodeListRequest.WriteToString() if err != nil { return fmt.Errorf("oxDeleteNodes: failed to create node list request %w", err) } iqReply, err := sendIQ(client, iqc, jid, "get", nlr) if err != nil { return fmt.Errorf("oxDeleteNodes: failure with node list request iq: %w", err) } nodeListReply := etree.NewDocument() err = nodeListReply.ReadFromBytes(iqReply.Query) if err != nil { return fmt.Errorf("oxDeleteNodes: failed to read node list reply iq: %w", err) } query = nodeListReply.SelectElement("query") if query == nil { return fmt.Errorf("error parsing iq reply") } items := query.SelectElements("item") if items == nil { return fmt.Errorf("error parsing iq reply") } for _, item := range items { node := item.SelectAttr("node") if node == nil { continue } if !strings.Contains(node.Value, nsOx) { continue } deleteNodeRequest := etree.NewDocument() deleteNodeRequest.WriteSettings.AttrSingleQuote = true pubsub := deleteNodeRequest.CreateElement("pubsub") pubsub.CreateAttr("xmlns", nsPubsubOwner) del := pubsub.CreateElement("delete") del.CreateAttr("node", node.Value) dnr, err := deleteNodeRequest.WriteToString() if err != nil { continue } _, err = sendIQ(client, iqc, jid, "set", dnr) if err != nil { continue } } return nil } func oxDecrypt(m xmpp.Chat, client *xmpp.Client, iqc chan xmpp.IQ, user string, oxPrivKey *crypto.Key) (string, time.Time, error) { var cryptMsgByte []byte var err error sender := strings.Split(m.Remote, "/")[0] for _, r := range m.OtherElem { if r.XMLName.Space == nsOx { cryptMsgByte, err = base64.StdEncoding.DecodeString(r.InnerXML) if err != nil { return strError, time.Now(), err } break } } oxMsg := crypto.NewPGPMessage(cryptMsgByte) keyRing, err := crypto.NewKeyRing(oxPrivKey) if err != nil { return strError, time.Now(), err } senderKeyRing, err := oxGetPublicKeyRing(client, iqc, sender) if err != nil { return strError, time.Now(), err } decryptMsg, err := keyRing.Decrypt(oxMsg, senderKeyRing, crypto.GetUnixTime()) if err != nil { return strError, time.Now(), err } // Remove invalid code points. message := validUTF8(string(decryptMsg.Data)) doc := etree.NewDocument() err = doc.ReadFromString(message) if err != nil { return strError, time.Now(), err } signcrypt := doc.SelectElement("signcrypt") if signcrypt == nil { return strError, time.Now(), fmt.Errorf("ox: no signcrypt element") } to := signcrypt.SelectElement("to") if to == nil { return strError, time.Now(), fmt.Errorf("ox: no to element") } jid := to.SelectAttr("jid") if jid == nil { return strError, time.Now(), fmt.Errorf("ox: no jid attribute") } if strings.Split(jid.Value, "/")[0] != user { return strError, time.Now(), fmt.Errorf("ox: encrypted for wrong user") } timestamp := signcrypt.SelectElement("time") if timestamp == nil { return strError, time.Now(), fmt.Errorf("ox: no time element") } stamp := timestamp.SelectAttr("stamp") if stamp == nil { return strError, time.Now(), fmt.Errorf("ox: no stamp attribute") } msgStamp, err := time.Parse("2006-01-02T15:04:05Z0700", stamp.Value) if err != nil { return strError, time.Now(), err } payload := signcrypt.SelectElement("payload") if payload == nil { return strError, time.Now(), fmt.Errorf("ox: no payload element") } body := payload.SelectElement("body") if body == nil { return "", time.Now(), nil } return body.Text(), msgStamp, nil } func isOxMsg(m xmpp.Chat) bool { for _, r := range m.OtherElem { if r.XMLName.Space == nsOx { return true } } return false } func oxImportPrivKey(jid string, privKeyLocation string, client *xmpp.Client, iqc chan xmpp.IQ) error { xmppURI := "xmpp:" + jid buffer, err := readFile(privKeyLocation) if err != nil { return err } key, err := crypto.NewKey(buffer.Bytes()) if err != nil { key, err = crypto.NewKeyFromArmored(buffer.String()) if err != nil { keyDecoded, err := base64.StdEncoding.DecodeString(buffer.String()) if err != nil { return fmt.Errorf("oxImportPrivKey: failed to import private key: %w", err) } key, err = crypto.NewKey(keyDecoded) if err != nil { return fmt.Errorf("oxImportPrivKey: failed to import private key: %w", err) } } } entity := key.GetEntity() if entity.Identities[xmppURI] == nil { return fmt.Errorf("Key identity is not %s", xmppURI) } pk, err := key.GetPublicKey() if err != nil { return fmt.Errorf("oxImportPrivKey: failed to get public key associated to private key: %w", err) } pubKey, err := crypto.NewKey(pk) if err != nil { return fmt.Errorf("oxImportPrivKey: failed to get public key associated to private key: %w", err) } fingerprint := strings.ToUpper(pubKey.GetFingerprint()) _, err = oxRecvPublicKeys(client, iqc, jid, fingerprint) if err != nil { err = oxPublishPubKey(jid, client, iqc, pubKey) if err != nil { return fmt.Errorf("oxImportPrivKey: failed to publish public key: %w", err) } } location, err := oxGetPrivKeyLoc(jid) if err != nil { return fmt.Errorf("oxImportPrivKey: failed to determine private key location: %w", err) } keySerialized, err := key.Serialize() if err != nil { return fmt.Errorf("oxImportPrivKey: failed to serialize private key: %w", err) } err = oxStoreKey(location, base64.StdEncoding.EncodeToString(keySerialized)) if err != nil { log.Fatal(err) } pubKeyRing, err := oxGetPublicKeyRing(client, iqc, jid) if err == nil { pubKeys := pubKeyRing.GetKeys() for _, r := range pubKeys { if strings.ToUpper(r.GetFingerprint()) == fingerprint { return nil } } } err = oxPublishPubKey(jid, client, iqc, pubKey) if err != nil { return fmt.Errorf("oxImportPrivKey: failed to publish public key: %w", err) } return nil } func oxPublishPubKey(jid string, client *xmpp.Client, iqc chan xmpp.IQ, pubKey *crypto.Key) error { keyCreated := time.Now().UTC().Format("2006-01-02T15:04:05Z") fingerprint := strings.ToUpper(pubKey.GetFingerprint()) keySerialized, err := pubKey.Serialize() if err != nil { return fmt.Errorf("oxPublishPubKey: failed to serialize pubkey: %w", err) } pubKeyBase64 := base64.StdEncoding.EncodeToString(keySerialized) root := etree.NewDocument() root.WriteSettings.AttrSingleQuote = true pubsub := root.CreateElement("pubsub") pubsub.CreateAttr("xmlns", nsPubsub) publish := pubsub.CreateElement("publish") publish.CreateAttr("node", nsOxPubKeys+":"+fingerprint) item := publish.CreateElement("item") item.CreateAttr("id", keyCreated) pubkey := item.CreateElement("pubkey") pubkey.CreateAttr("xmlns", nsOx) data := pubkey.CreateElement("data") data.CreateText(pubKeyBase64) publishoptions := pubsub.CreateElement("publish-options") x := publishoptions.CreateElement("x") x.CreateAttr("xmlns", nsJabberData) x.CreateAttr("type", "submit") field := x.CreateElement("field") field.CreateAttr("var", "FORM_TYPE") field.CreateAttr("type", "hidden") value := field.CreateElement("value") value.CreateText(pubsubPubOptions) field = x.CreateElement("field") field.CreateAttr("var", "pubsub#access_model") value = field.CreateElement("value") value.CreateText("open") xmlstring, err := root.WriteToString() if err != nil { return fmt.Errorf("oxPublishPubKey: failed to create publish public key iq xml: %w", err) } iqReply, err := sendIQ(client, iqc, jid, "set", xmlstring) if err != nil { return fmt.Errorf("oxPublishPubKey: iq failure publishing public key: %w", err) } if iqReply.Type != strResult { return fmt.Errorf("error while publishing public key") } ownPubKeyRingFromPubsub, err := oxRecvPublicKeys(client, iqc, jid, fingerprint) if err != nil { return fmt.Errorf("couldn't successfully verify public key upload") } ownPubKeyFromPubsub := ownPubKeyRingFromPubsub.GetKeys()[0] ownPubKeyFromPubsubSerialized, err := ownPubKeyFromPubsub.Serialize() if err != nil { return fmt.Errorf("couldn't successfully verify public key upload") } if pubKeyBase64 != base64.StdEncoding.EncodeToString(ownPubKeyFromPubsubSerialized) { return fmt.Errorf("couldn't successfully verify public key upload") } root = etree.NewDocument() root.WriteSettings.AttrSingleQuote = true pubsub = root.CreateElement("pubsub") pubsub.CreateAttr("xmlns", nsPubsub) publish = pubsub.CreateElement("publish") publish.CreateAttr("node", nsOxPubKeys) item = publish.CreateElement("item") pubkeyslist := item.CreateElement("public-keys-list") pubkeyslist.CreateAttr("xmlns", nsOx) pubkeymeta := pubkeyslist.CreateElement("pubkey-metadata") pubkeymeta.CreateAttr("v4-fingerprint", fingerprint) pubkeymeta.CreateAttr("date", keyCreated) publishoptions = pubsub.CreateElement("publish-options") x = publishoptions.CreateElement("x") x.CreateAttr("xmlns", nsJabberData) x.CreateAttr("type", "submit") field = x.CreateElement("field") field.CreateAttr("var", "FORM_TYPE") field.CreateAttr("type", "hidden") value = field.CreateElement("value") value.CreateText(pubsubPubOptions) field = x.CreateElement("field") field.CreateAttr("var", "pubsub#access_model") value = field.CreateElement("value") value.CreateText("open") xmlstring, err = root.WriteToString() if err != nil { return fmt.Errorf("oxPublishPubKey: failed to create xml for iq to publish public key list: %w", err) } iqReply, err = sendIQ(client, iqc, jid, "set", xmlstring) if err != nil { return fmt.Errorf("oxPublishPubKey: iq failure publishing public key list: %w", err) } if iqReply.Type != strResult { return fmt.Errorf("couldn't publish public key list") } return nil } func oxGetPrivKeyLoc(jid string) (string, error) { dataDir, err := getDataPath(strings.Replace(strings.Replace(jid, "@", "_at_", -1), ".", "_", -1)) if err != nil { return strError, fmt.Errorf("oxGetPrivKeyLoc: %w", err) } oldDataDir, err := getDataPath("oxprivkeys/") if err != nil { return strError, fmt.Errorf("oxGetPrivKeyLoc: %w", err) } // TODO: Remove handling of oldDataFile in a later version when it's very likely that there are no // more versions in use using the oldDataFile (<0.8.3). oldDataFile := oldDataDir + base64.StdEncoding.EncodeToString([]byte(jid)) oldDataFile2 := oldDataDir + strings.Replace(jid, "@", "_at_", -1) oldDataFile3 := oldDataDir + strings.Replace(strings.Replace(jid, "@", "_at_", -1), ".", "_", -1) dataFile := dataDir + "oxprivkey" if _, err := os.Stat(oldDataFile); err == nil { err := os.Rename(oldDataFile, dataFile) if err != nil { return dataFile, err } } if _, err := os.Stat(oldDataFile2); err == nil { err := os.Rename(oldDataFile2, dataFile) if err != nil { return dataFile, err } } if _, err := os.Stat(oldDataFile3); err == nil { err := os.Rename(oldDataFile3, dataFile) if err != nil { return dataFile, err } } return dataFile, nil } func oxGetPubKeyLoc(fingerprint string) (string, error) { dataDir, err := getDataPath("oxpubkeys/") if err != nil { return strError, fmt.Errorf("oxGetPubKeyLoc: %w", err) } dataFile := dataDir + fingerprint return dataFile, nil } func oxGetPrivKey(jid string, passphrase string) (*crypto.Key, error) { dataFile, err := oxGetPrivKeyLoc(jid) if err != nil { log.Fatal(err) } keyBuffer, err := readFile(dataFile) if err != nil { log.Fatal(err) } keyString := keyBuffer.String() decodedPrivKey, err := base64.StdEncoding.DecodeString(keyString) if err != nil { return nil, fmt.Errorf("oxGetPrivKey: failed to decode private key: %w", err) } key, err := crypto.NewKey(decodedPrivKey) if err != nil { return nil, fmt.Errorf("oxGetPrivKey: failed to decode private key: %w", err) } if passphrase != "" { key, err = key.Unlock([]byte(passphrase)) if err != nil { log.Fatal("Ox: couldn't unlock private key.") } } isLocked, err := key.IsLocked() if err != nil { return nil, fmt.Errorf("oxGetPrivKey: failed to check whether private key is locked: %w", err) } if isLocked { log.Fatal("Ox: private key is locked.") } if key.IsExpired() { return nil, fmt.Errorf("Ox: private key is expired: %s", key.GetFingerprint()) } return key, nil } func oxStoreKey(location string, key string) error { var file *os.File file, err := os.Create(location) if err != nil { return fmt.Errorf("oxStoreKey: failed to create key location: %w", err) } if runtime.GOOS != "windows" { _ = file.Chmod(os.FileMode(defaultFileRights)) } else { _ = file.Chmod(os.FileMode(defaultFileRightsWin)) } _, err = file.Write([]byte(key)) if err != nil { return fmt.Errorf("oxStoreKey: failed to write key to file: %w", err) } err = file.Close() if err != nil { fmt.Println("error while closing file:", err) } return nil } func oxGenPrivKey(jid string, client *xmpp.Client, iqc chan xmpp.IQ, passphrase string, keyType string, ) error { xmppURI := "xmpp:" + jid key, err := crypto.GenerateKey(xmppURI, "", keyType, defaultRSABits) if err != nil { return fmt.Errorf("oxGenPrivKey: failed to generate private key: %w", err) } if passphrase != "" { key, err = key.Lock([]byte(passphrase)) if err != nil { return fmt.Errorf("oxGenPrivKey: failed to lock key with passphrase: %w", err) } } keySerialized, err := key.Serialize() if err != nil { return fmt.Errorf("oxGenPrivKey: failed to serialize private key: %w", err) } location, err := oxGetPrivKeyLoc(jid) if err != nil { return fmt.Errorf("oxGenPrivKey: failed to get private key location: %w", err) } err = oxStoreKey(location, base64.StdEncoding.EncodeToString(keySerialized)) if err != nil { log.Fatal(err) } decodedPubKey, err := key.GetPublicKey() if err != nil { return fmt.Errorf("oxGenPrivKey: failed to decode public key: %w", err) } pubKey, err := crypto.NewKey(decodedPubKey) if err != nil { return fmt.Errorf("oxGenPrivKey: failed to decode public key: %w", err) } err = oxPublishPubKey(jid, client, iqc, pubKey) if err != nil { return fmt.Errorf("oxGenPrivKey: failed to publish public key: %w", err) } return nil } func oxRecvPublicKeys(client *xmpp.Client, iqc chan xmpp.IQ, recipient string, fingerprint string) (*crypto.KeyRing, error) { opkr := etree.NewDocument() opkr.WriteSettings.AttrSingleQuote = true opkrPs := opkr.CreateElement("pubsub") opkrPs.CreateAttr("xmlns", nsPubsub) opkrPsItems := opkrPs.CreateElement("items") opkrPsItems.CreateAttr("node", nsOxPubKeys+":"+fingerprint) opkrPsItems.CreateAttr("max_items", "1") opkrString, err := opkr.WriteToString() if err != nil { return nil, fmt.Errorf("oxRecvPublicKeys: failed to generate xml for public key request: %w", err) } oxPublicKey, err := sendIQ(client, iqc, recipient, "get", opkrString) if err != nil { return nil, fmt.Errorf("oxRecvPublicKeys: iq error requesting public keys: %w", err) } if oxPublicKey.Type != strResult { return nil, fmt.Errorf("error while requesting public key for %s", recipient) } oxPublicKeyXML := etree.NewDocument() err = oxPublicKeyXML.ReadFromBytes(oxPublicKey.Query) if err != nil { return nil, fmt.Errorf("oxRecvPublicKeys: failed parsing iq reply to public key request: %w", err) } keyring, err := crypto.NewKeyRing(nil) if err != nil { return nil, fmt.Errorf("oxRecvPublicKeys: failed reading public key: %w", err) } oxPublicKeyXMLPubsub := oxPublicKeyXML.SelectElement("pubsub") if oxPublicKeyXMLPubsub == nil { return nil, fmt.Errorf("ox: no pubsub element in reply to public key request") } oxPublicKeyXMLItems := oxPublicKeyXMLPubsub.SelectElement("items") if oxPublicKeyXMLItems == nil { return nil, fmt.Errorf("ox: no items element in reply to public key request") } oxPublicKeyXMLItem := oxPublicKeyXMLItems.SelectElement("item") if oxPublicKeyXMLItem == nil { return nil, fmt.Errorf("ox: no item element in reply to public key request") } oxPublicKeyXMLPubkeys := oxPublicKeyXMLItem.SelectElements("pubkey") for _, r := range oxPublicKeyXMLPubkeys { data := r.SelectElement("data") if data == nil { continue } decodedPubKey, err := base64.StdEncoding.DecodeString(data.Text()) if err != nil { return nil, fmt.Errorf("oxRecvPublicKeys: failed to decode public key: %w", err) } key, err := crypto.NewKey(decodedPubKey) if err != nil { return nil, fmt.Errorf("oxRecvPublicKeys: failed to decode public key: %w", err) } if key.IsExpired() { return nil, fmt.Errorf("Key is expired: %s", fingerprint) } err = keyring.AddKey(key) if err != nil { return nil, fmt.Errorf("oxRecvPublicKeys: failed adding public key to keyring: %w", err) } } return keyring, nil } func oxGetPublicKeyRing(client *xmpp.Client, iqc chan xmpp.IQ, recipient string) (*crypto.KeyRing, error) { publicKeyRing, err := crypto.NewKeyRing(nil) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to create a new keyring: %w", err) } oxPubKeyListReq := etree.NewDocument() oxPubKeyListReq.WriteSettings.AttrSingleQuote = true oxPubKeyListReqPs := oxPubKeyListReq.CreateElement("pubsub") oxPubKeyListReqPs.CreateAttr("xmlns", nsPubsub) oxPubKeyListReqPsItems := oxPubKeyListReqPs.CreateElement("items") oxPubKeyListReqPsItems.CreateAttr("node", nsOxPubKeys) oxPubKeyListReqPsItems.CreateAttr("max_items", "1") opkl, err := oxPubKeyListReq.WriteToString() if err != nil { log.Fatal(err) } oxPublicKeyList, err := sendIQ(client, iqc, recipient, "get", opkl) if err != nil { log.Fatal(err) } if oxPublicKeyList.Type != strResult { return nil, fmt.Errorf("error while requesting public openpgp keys for %s", recipient) } oxPubKeyListXML := etree.NewDocument() err = oxPubKeyListXML.ReadFromBytes(oxPublicKeyList.Query) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to parse answer to public key list request: %w", err) } pubKeyRingID := "none" newestKey, err := time.Parse(time.RFC3339, "1900-01-01T00:00:00Z") if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to set time for newest key to 1900-01-01: %w", err) } oxPubKeyListXMLPubsub := oxPubKeyListXML.SelectElement("pubsub") if oxPubKeyListXMLPubsub == nil { return nil, fmt.Errorf("ox: no pubsub element in public key list") } oxPubKeyListXMLPubsubItems := oxPubKeyListXMLPubsub.SelectElement("items") if oxPubKeyListXMLPubsubItems == nil { return nil, fmt.Errorf("ox: no items element in public key list") } oxPubKeyListXMLPubsubItemsItem := oxPubKeyListXMLPubsubItems.SelectElement("item") if oxPubKeyListXMLPubsubItemsItem == nil { return nil, fmt.Errorf("ox: no item element in public key list") } oxPubKeyListXMLPubsubItemsItemPkl := oxPubKeyListXMLPubsubItemsItem.SelectElement("public-keys-list") if oxPubKeyListXMLPubsubItemsItemPkl == nil { return nil, fmt.Errorf("ox: no public-keys-list element") } oxPubKeyListXMLPubsubItemsItemPklPm := oxPubKeyListXMLPubsubItemsItemPkl.SelectElements("pubkey-metadata") for _, r := range oxPubKeyListXMLPubsubItemsItemPklPm { date := r.SelectAttr("date") if date == nil { continue } fingerprint := r.SelectAttr("v4-fingerprint") if fingerprint == nil { continue } keyDate, err := time.Parse(time.RFC3339, date.Value) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to parse time stamp for key: %w", err) } if keyDate.After(newestKey) { newestKey = keyDate pubKeyRingID = fingerprint.Value } } if pubKeyRingID == "none" { return nil, fmt.Errorf("server didn't provide public key fingerprints for %s", recipient) } pubKeyRingLocation, err := oxGetPubKeyLoc(pubKeyRingID) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to get public key ring location: %w", err) } pubKeyReadXML := etree.NewDocument() err = pubKeyReadXML.ReadFromFile(pubKeyRingLocation) if err == nil { date := pubKeyReadXML.SelectElement("date") if date != nil { savedKeysDate, err := time.Parse(time.RFC3339, date.Text()) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to parse time for saved key: %w", err) } if !savedKeysDate.Before(newestKey) { pubKeys := pubKeyReadXML.SelectElements("pubkey") if pubKeys == nil { return nil, fmt.Errorf("couldn't read public keys from cache") } for _, r := range pubKeys { keyByte, err := base64.StdEncoding.DecodeString(r.Text()) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to decode saved key: %w", err) } key, err := crypto.NewKey(keyByte) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to parse saved key: %w", err) } if !key.IsExpired() { err = publicKeyRing.AddKey(key) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to add key to public keyring: %w", err) } } } if publicKeyRing.CanEncrypt() { return publicKeyRing, nil } } } } pubKeyRing, err := oxRecvPublicKeys(client, iqc, recipient, pubKeyRingID) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to get public keyring: %w", err) } pubKeySaveXML := etree.NewDocument() date := pubKeySaveXML.CreateElement("date") date.SetText(newestKey.Format(time.RFC3339)) for _, key := range pubKeyRing.GetKeys() { keySerialized, err := key.Serialize() if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to serialize key: %w", err) } saveKey := pubKeySaveXML.CreateElement("pubkey") saveKey.SetText(base64.StdEncoding.EncodeToString(keySerialized)) } err = pubKeySaveXML.WriteToFile(pubKeyRingLocation) if err != nil { return nil, fmt.Errorf("oxGetPublicKeyRing: failed to create xml for saving public key: %w", err) } return pubKeyRing, nil } func oxEncrypt(client *xmpp.Client, oxPrivKey *crypto.Key, recipient string, keyRing *crypto.KeyRing, message string, subject string) (string, error) { if message == "" { return "", nil } privKeyRing, err := crypto.NewKeyRing(oxPrivKey) if err != nil { return strError, fmt.Errorf("oxEncrypt: failed to create private keyring: %w", err) } ownJid := strings.Split(client.JID(), "/")[0] if recipient != ownJid { opk, err := oxPrivKey.GetPublicKey() if err == nil { ownKey, _ := crypto.NewKey(opk) _ = keyRing.AddKey(ownKey) } } oxCryptMessage := etree.NewDocument() oxCryptMessage.WriteSettings.AttrSingleQuote = true oxCryptMessageSc := oxCryptMessage.CreateElement("signcrypt") oxCryptMessageSc.CreateAttr("xmlns", nsOx) oxCryptMessageScTo := oxCryptMessageSc.CreateElement("to") oxCryptMessageScTo.CreateAttr("jid", recipient) oxCryptMessageScTime := oxCryptMessageSc.CreateElement("time") oxCryptMessageScTime.CreateAttr("stamp", time.Now().UTC().Format("2006-01-02T15:04:05Z")) oxCryptMessageScRpad := oxCryptMessageSc.CreateElement("rpad") oxCryptMessageScRpad.CreateText(getRpad(len(message))) oxCryptMessageScPayload := oxCryptMessageSc.CreateElement("payload") if subject != "" { oxCryptMessageScPayloadSub := oxCryptMessageScPayload.CreateElement("subject") oxCryptMessageScPayloadSub.CreateText(subject) } oxCryptMessageScPayloadBody := oxCryptMessageScPayload.CreateElement("body") oxCryptMessageScPayloadBody.CreateAttr("xmlns", nsJabberClient) oxCryptMessageScPayloadBody.CreateText(message) ocm, err := oxCryptMessage.WriteToString() if err != nil { return strError, fmt.Errorf("oxEncrypt: failed to create xml for ox crypt message: %w", err) } plainMessage := crypto.NewPlainMessage([]byte(ocm)) pgpMessage, err := keyRing.Encrypt(plainMessage, privKeyRing) if err != nil { return strError, fmt.Errorf("oxEncrypt: failed to create pgp message: %w", err) } om := etree.NewDocument() om.WriteSettings.AttrSingleQuote = true omMessage := om.CreateElement("message") omMessage.CreateAttr("to", recipient) omMessage.CreateAttr("id", getID()) omMessageStore := omMessage.CreateElement("store") omMessageStore.CreateAttr("xmlns", nsHints) omMessageEme := omMessage.CreateElement("encryption") omMessageEme.CreateAttr("xmlns", nsEme) omMessageEme.CreateAttr("namespace", nsOx) omMessageOpgp := omMessage.CreateElement("openpgp") omMessageOpgp.CreateAttr("xmlns", nsOx) omMessageOpgp.CreateText(base64.StdEncoding.EncodeToString(pgpMessage.Data)) omMessageBody := omMessage.CreateElement("body") omMessageBody.CreateText(oxAltBody) oms, err := om.WriteToString() if err != nil { return strError, fmt.Errorf("oxEncrypt: failed to create xml for ox message: %w", err) } return oms, nil }