mirror of
https://salsa.debian.org/mdosch/go-sendxmpp
synced 2024-11-12 13:10:25 +00:00
2f2a2fd4fd
Calling client.Recv() multiple times caused messages not appear in `--listen` mode as calling Recv() for receiving IQs (e.g. for receiving Ox keys) also received the messages so they were no longer available for the Recv in the listening function.
659 lines
18 KiB
Go
659 lines
18 KiB
Go
// Copyright 2022 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"
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/ProtonMail/gopenpgp/v2/crypto" // MIT License
|
|
"github.com/beevik/etree" // BSD-2-clause
|
|
"github.com/mattn/go-xmpp" // BSD-3-Clause
|
|
)
|
|
|
|
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 "error", time.Now(), err
|
|
}
|
|
break
|
|
}
|
|
}
|
|
oxMsg := crypto.NewPGPMessage(cryptMsgByte)
|
|
keyRing, err := crypto.NewKeyRing(oxPrivKey)
|
|
if err != nil {
|
|
return "error", time.Now(), err
|
|
}
|
|
senderKeyRing, err := oxGetPublicKeyRing(client, iqc, sender)
|
|
if err != nil {
|
|
return "error", time.Now(), err
|
|
}
|
|
decryptMsg, err := keyRing.Decrypt(oxMsg, senderKeyRing, crypto.GetUnixTime())
|
|
if err != nil {
|
|
return "error", time.Now(), err
|
|
}
|
|
// Remove invalid code points.
|
|
message := strings.ToValidUTF8(string(decryptMsg.Data), "")
|
|
reg := regexp.MustCompile(`[\x{0000}-\x{0008}\x{000B}\x{000C}\x{000E}-\x{001F}]`)
|
|
message = reg.ReplaceAllString(message, "")
|
|
doc := etree.NewDocument()
|
|
err = doc.ReadFromString(message)
|
|
if err != nil {
|
|
return "error", time.Now(), err
|
|
}
|
|
signcrypt := doc.SelectElement("signcrypt")
|
|
if signcrypt == nil {
|
|
return "error", time.Now(), errors.New("Ox: no signcrypt element")
|
|
}
|
|
to := signcrypt.SelectElement("to")
|
|
if to == nil {
|
|
return "error", time.Now(), errors.New("Ox: no to element")
|
|
}
|
|
jid := to.SelectAttr("jid")
|
|
if jid == nil {
|
|
return "error", time.Now(), errors.New("Ox: no jid attribute")
|
|
}
|
|
if strings.Split(jid.Value, "/")[0] != user {
|
|
return "error", time.Now(), errors.New("Ox: encrypted for wrong user")
|
|
}
|
|
timestamp := signcrypt.SelectElement("time")
|
|
if timestamp == nil {
|
|
return "error", time.Now(), errors.New("Ox: no time element")
|
|
}
|
|
stamp := timestamp.SelectAttr("stamp")
|
|
if stamp == nil {
|
|
return "error", time.Now(), errors.New("Ox: no stamp attribute")
|
|
}
|
|
msgStamp, err := time.Parse("2006-01-02T15:04:05Z0700", stamp.Value)
|
|
if err != nil {
|
|
return "error", time.Now(), err
|
|
}
|
|
payload := signcrypt.SelectElement("payload")
|
|
if payload == nil {
|
|
return "error", time.Now(), errors.New("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 {
|
|
return err
|
|
}
|
|
}
|
|
entity := key.GetEntity()
|
|
if entity.Identities[xmppURI] == nil {
|
|
return errors.New("Key identity is not " + xmppURI)
|
|
}
|
|
pk, err := key.GetPublicKey()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pubKey, err := crypto.NewKey(pk)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fingerprint := strings.ToUpper(pubKey.GetFingerprint())
|
|
_, err = oxRecvPublicKeys(client, iqc, jid, fingerprint)
|
|
if err != nil {
|
|
return errors.New("Key not found in pubsub: " + fingerprint)
|
|
}
|
|
location, err := oxGetPrivKeyLoc(jid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
keySerialized, err := key.Serialize()
|
|
if err != nil {
|
|
return 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 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 err
|
|
}
|
|
pubKeyBase64 := base64.StdEncoding.EncodeToString(keySerialized)
|
|
root := etree.NewDocument()
|
|
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 err
|
|
}
|
|
iqReply, err := sendIQ(client, iqc, jid, "set", xmlstring)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if iqReply.Type != "result" {
|
|
return errors.New("Error while publishing public key")
|
|
}
|
|
ownPubKeyRingFromPubsub, err := oxRecvPublicKeys(client, iqc, jid, fingerprint)
|
|
if err != nil {
|
|
return errors.New("Couldn't successfully verify public key upload")
|
|
}
|
|
ownPubKeyFromPubsub := ownPubKeyRingFromPubsub.GetKeys()[0]
|
|
ownPubKeyFromPubsubSerialized, err := ownPubKeyFromPubsub.Serialize()
|
|
if err != nil {
|
|
return errors.New("Couldn't successfully verify public key upload")
|
|
}
|
|
if pubKeyBase64 != base64.StdEncoding.EncodeToString(ownPubKeyFromPubsubSerialized) {
|
|
return errors.New("Couldn't successfully verify public key upload")
|
|
}
|
|
root = etree.NewDocument()
|
|
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 err
|
|
}
|
|
iqReply, err = sendIQ(client, iqc, jid, "set", xmlstring)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if iqReply.Type != "result" {
|
|
return errors.New("Couldn't publish public key list")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func oxGetPrivKeyLoc(jid string) (string, error) {
|
|
var err error
|
|
var homeDir, dataDir string
|
|
switch {
|
|
case os.Getenv("$XDG_DATA_HOME") != "":
|
|
dataDir = os.Getenv("$XDG_DATA_HOME")
|
|
case os.Getenv("$XDG_HOME") != "":
|
|
homeDir = os.Getenv("$XDG_HOME")
|
|
dataDir = homeDir + "/.local/share"
|
|
case os.Getenv("$HOME") != "":
|
|
homeDir = os.Getenv("$HOME")
|
|
dataDir = homeDir + "/.local/share"
|
|
default:
|
|
homeDir, err = os.UserHomeDir()
|
|
if err != nil {
|
|
return "error", err
|
|
}
|
|
if homeDir == "" {
|
|
return "error", err
|
|
}
|
|
dataDir = homeDir + "/.local/share"
|
|
}
|
|
dataDir = dataDir + "/go-sendxmpp/oxprivkeys/"
|
|
if _, err = os.Stat(dataDir); os.IsNotExist(err) {
|
|
err = os.MkdirAll(dataDir, 0700)
|
|
if err != nil {
|
|
return "error", err
|
|
}
|
|
}
|
|
dataFile := dataDir + base64.StdEncoding.EncodeToString([]byte(jid))
|
|
return dataFile, nil
|
|
}
|
|
|
|
func oxGetPubKeyLoc(fingerprint string) (string, error) {
|
|
var err error
|
|
var homeDir, dataDir string
|
|
switch {
|
|
case os.Getenv("$XDG_DATA_HOME") != "":
|
|
dataDir = os.Getenv("$XDG_DATA_HOME")
|
|
case os.Getenv("$XDG_HOME") != "":
|
|
homeDir = os.Getenv("$XDG_HOME")
|
|
dataDir = homeDir + "/.local/share"
|
|
case os.Getenv("$HOME") != "":
|
|
homeDir = os.Getenv("$HOME")
|
|
dataDir = homeDir + "/.local/share"
|
|
default:
|
|
homeDir, err = os.UserHomeDir()
|
|
if err != nil {
|
|
return "error", err
|
|
}
|
|
if homeDir == "" {
|
|
return "error", err
|
|
}
|
|
dataDir = homeDir + "/.local/share"
|
|
}
|
|
dataDir = dataDir + "/go-sendxmpp/oxpubkeys/"
|
|
if _, err = os.Stat(dataDir); os.IsNotExist(err) {
|
|
err = os.MkdirAll(dataDir, 0700)
|
|
if err != nil {
|
|
return "error", 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, err
|
|
}
|
|
key, err := crypto.NewKey(decodedPrivKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if passphrase != "" {
|
|
key, err = key.Unlock([]byte(passphrase))
|
|
if err != nil {
|
|
log.Fatal("Couldn't unlock private key.")
|
|
}
|
|
}
|
|
isLocked, err := key.IsLocked()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if isLocked {
|
|
log.Fatal("Private key is locked.")
|
|
}
|
|
if key.IsExpired() {
|
|
return nil, errors.New("Key is expired: " + 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 err
|
|
}
|
|
defer file.Close()
|
|
if runtime.GOOS != "windows" {
|
|
_ = file.Chmod(os.FileMode(0600))
|
|
} else {
|
|
_ = file.Chmod(os.FileMode(0200))
|
|
}
|
|
_, err = file.Write([]byte(key))
|
|
if err != nil {
|
|
return 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, 4096)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if passphrase != "" {
|
|
key, err = key.Lock([]byte(passphrase))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
keySerialized, err := key.Serialize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
location, err := oxGetPrivKeyLoc(jid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = oxStoreKey(location,
|
|
base64.StdEncoding.EncodeToString(keySerialized))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
decodedPubKey, err := key.GetPublicKey()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pubKey, err := crypto.NewKey(decodedPubKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = oxPublishPubKey(jid, client, iqc, pubKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func oxRecvPublicKeys(client *xmpp.Client, iqc chan xmpp.IQ, recipient string,
|
|
fingerprint string) (*crypto.KeyRing, error) {
|
|
opkr := etree.NewDocument()
|
|
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, err
|
|
}
|
|
oxPublicKey, err := sendIQ(client, iqc, recipient, "get", opkrString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if oxPublicKey.Type != "result" {
|
|
return nil, errors.New("Error while requesting public key for " +
|
|
recipient)
|
|
}
|
|
oxPublicKeyXML := etree.NewDocument()
|
|
err = oxPublicKeyXML.ReadFromBytes(oxPublicKey.Query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keyring, err := crypto.NewKeyRing(nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
oxPublicKeyXMLPubsub := oxPublicKeyXML.SelectElement("pubsub")
|
|
oxPublicKeyXMLItems := oxPublicKeyXMLPubsub.SelectElement("items")
|
|
oxPublicKeyXMLItem := oxPublicKeyXMLItems.SelectElement("item")
|
|
oxPublicKeyXMLPubkeys := oxPublicKeyXMLItem.SelectElements("pubkey")
|
|
for _, r := range oxPublicKeyXMLPubkeys {
|
|
data := r.SelectElement("data")
|
|
decodedPubKey, err := base64.StdEncoding.DecodeString(data.Text())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key, err := crypto.NewKey(decodedPubKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if key.IsExpired() {
|
|
return nil, errors.New("Key is expired: " + fingerprint)
|
|
}
|
|
err = keyring.AddKey(key)
|
|
if err != nil {
|
|
return nil, 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, err
|
|
}
|
|
|
|
oxPubKeyListReq := etree.NewDocument()
|
|
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 != "result" {
|
|
return nil, errors.New("Error while requesting public openpgp keys for " +
|
|
recipient)
|
|
}
|
|
oxPubKeyListXML := etree.NewDocument()
|
|
err = oxPubKeyListXML.ReadFromBytes(oxPublicKeyList.Query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pubKeyRingID := "none"
|
|
newestKey, err := time.Parse(time.RFC3339, "1900-01-01T00:00:00Z")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
oxPubKeyListXMLPubsub := oxPubKeyListXML.SelectElement("pubsub")
|
|
oxPubKeyListXMLPubsubItems := oxPubKeyListXMLPubsub.SelectElement("items")
|
|
oxPubKeyListXMLPubsubItemsItem := oxPubKeyListXMLPubsubItems.SelectElement("item")
|
|
oxPubKeyListXMLPubsubItemsItemPkl := oxPubKeyListXMLPubsubItemsItem.SelectElement("public-keys-list")
|
|
oxPubKeyListXMLPubsubItemsItemPklPm :=
|
|
oxPubKeyListXMLPubsubItemsItemPkl.SelectElements("pubkey-metadata")
|
|
for _, r := range oxPubKeyListXMLPubsubItemsItemPklPm {
|
|
date := r.SelectAttr("date")
|
|
fingerprint := r.SelectAttr("v4-fingerprint")
|
|
keyDate, err := time.Parse(time.RFC3339, date.Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if keyDate.After(newestKey) {
|
|
newestKey = keyDate
|
|
pubKeyRingID = fingerprint.Value
|
|
}
|
|
}
|
|
if pubKeyRingID == "none" {
|
|
return nil, errors.New("server didn't provide public key fingerprints for " + recipient)
|
|
}
|
|
|
|
pubKeyRingLocation, err := oxGetPubKeyLoc(pubKeyRingID)
|
|
if err != nil {
|
|
return nil, 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, err
|
|
}
|
|
if !savedKeysDate.Before(newestKey) {
|
|
pubKeys := pubKeyReadXML.SelectElements("pubkey")
|
|
if pubKeys == nil {
|
|
return nil, errors.New("Couldn't read public keys from cache")
|
|
}
|
|
for _, r := range pubKeys {
|
|
keyByte, err := base64.StdEncoding.DecodeString(r.Text())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key, err := crypto.NewKey(keyByte)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !key.IsExpired() {
|
|
err = publicKeyRing.AddKey(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
if publicKeyRing.CanEncrypt() {
|
|
return publicKeyRing, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pubKeyRing, err := oxRecvPublicKeys(client, iqc, recipient, pubKeyRingID)
|
|
if err != nil {
|
|
return nil, 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, err
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
saveKey := pubKeySaveXML.CreateElement("pubkey")
|
|
saveKey.SetText(base64.StdEncoding.EncodeToString(keySerialized))
|
|
}
|
|
err = pubKeySaveXML.WriteToFile(pubKeyRingLocation)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return pubKeyRing, nil
|
|
}
|
|
|
|
func oxEncrypt(client *xmpp.Client, iqc chan xmpp.IQ, oxPrivKey *crypto.Key,
|
|
recipient string, keyRing *crypto.KeyRing, message string) (string, error) {
|
|
privKeyRing, err := crypto.NewKeyRing(oxPrivKey)
|
|
if err != nil {
|
|
return "error", 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()
|
|
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")
|
|
oxCryptMessageScPayloadBody := oxCryptMessageScPayload.CreateElement("body")
|
|
oxCryptMessageScPayloadBody.CreateAttr("xmlns", nsJabberClient)
|
|
oxCryptMessageScPayloadBody.CreateText(message)
|
|
ocm, err := oxCryptMessage.WriteToString()
|
|
if err != nil {
|
|
return "error", err
|
|
}
|
|
plainMessage := crypto.NewPlainMessage([]byte(ocm))
|
|
pgpMessage, err := keyRing.Encrypt(plainMessage, privKeyRing)
|
|
if err != nil {
|
|
return "error", err
|
|
}
|
|
om := etree.NewDocument()
|
|
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 "error", err
|
|
}
|
|
|
|
return oms, nil
|
|
}
|