go-sendxmpp/ox.go

770 lines
26 KiB
Go
Raw Normal View History

2023-05-11 18:05:31 +00:00
// 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"
"errors"
"fmt"
"log"
"os"
"runtime"
"strings"
"time"
"github.com/ProtonMail/gopenpgp/v2/crypto" // MIT License
"github.com/beevik/etree" // BSD-2-clause
2024-01-10 15:21:39 +00:00
"github.com/xmppo/go-xmpp" // BSD-3-Clause
)
2023-06-07 20:33:43 +00:00
func oxDeleteNodes(jid string, client *xmpp.Client, iqc chan xmpp.IQ) error {
2022-05-04 09:18:24 +00:00
nodeListRequest := etree.NewDocument()
nodeListRequest.WriteSettings.AttrSingleQuote = true
2022-05-04 09:18:24 +00:00
query := nodeListRequest.CreateElement("query")
query.CreateAttr("xmlns", nsDiscoItems)
nlr, err := nodeListRequest.WriteToString()
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxDeleteNodes: failed to create node list request %w", err)
2022-05-04 09:18:24 +00:00
}
iqReply, err := sendIQ(client, iqc, jid, "get", nlr)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxDeleteNodes: failure with node list request iq: %w", err)
2022-05-04 09:18:24 +00:00
}
nodeListReply := etree.NewDocument()
err = nodeListReply.ReadFromBytes(iqReply.Query)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxDeleteNodes: failed to read node list reply iq: %w", err)
2022-05-04 09:18:24 +00:00
}
query = nodeListReply.SelectElement("query")
2022-05-04 10:12:10 +00:00
if query == nil {
return errors.New("error parsing iq reply")
}
2022-05-04 09:18:24 +00:00
items := query.SelectElements("item")
2022-05-04 10:12:10 +00:00
if items == nil {
return errors.New("error parsing iq reply")
}
2022-05-04 09:18:24 +00:00
for _, item := range items {
node := item.SelectAttr("node")
2022-05-04 10:12:10 +00:00
if node == nil {
continue
}
2022-05-04 09:18:24 +00:00
if !strings.Contains(node.Value, nsOx) {
continue
}
deleteNodeRequest := etree.NewDocument()
deleteNodeRequest.WriteSettings.AttrSingleQuote = true
2022-05-04 09:18:24 +00:00
pubsub := deleteNodeRequest.CreateElement("pubsub")
pubsub.CreateAttr("xmlns", nsPubsubOwner)
2023-06-06 08:47:41 +00:00
del := pubsub.CreateElement("delete")
del.CreateAttr("node", node.Value)
2022-05-04 09:18:24 +00:00
dnr, err := deleteNodeRequest.WriteToString()
if err != nil {
2022-05-04 09:18:24 +00:00
continue
}
_, err = sendIQ(client, iqc, jid, "set", dnr)
if err != nil {
2022-05-04 09:18:24 +00:00
continue
}
}
return nil
}
2023-06-07 20:33:43 +00:00
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 {
2023-06-06 08:47:40 +00:00
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(), errors.New("ox: no signcrypt element")
}
to := signcrypt.SelectElement("to")
if to == nil {
return strError, time.Now(), errors.New("ox: no to element")
}
jid := to.SelectAttr("jid")
if jid == nil {
return strError, time.Now(), errors.New("ox: no jid attribute")
}
if strings.Split(jid.Value, "/")[0] != user {
return strError, time.Now(), errors.New("ox: encrypted for wrong user")
}
timestamp := signcrypt.SelectElement("time")
if timestamp == nil {
return strError, time.Now(), errors.New("ox: no time element")
}
stamp := timestamp.SelectAttr("stamp")
if stamp == nil {
return strError, time.Now(), errors.New("ox: no stamp attribute")
}
2022-05-02 14:35:25 +00:00
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(), 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
}
2023-06-07 20:33:43 +00:00
func oxImportPrivKey(jid string, privKeyLocation string, client *xmpp.Client, iqc chan xmpp.IQ) error {
xmppURI := "xmpp:" + jid
2022-04-18 09:16:41 +00:00
buffer, err := readFile(privKeyLocation)
if err != nil {
2022-04-18 09:16:41 +00:00
return err
}
key, err := crypto.NewKey(buffer.Bytes())
if err != nil {
2022-04-18 09:16:41 +00:00
key, err = crypto.NewKeyFromArmored(buffer.String())
if err != nil {
2022-05-04 09:42:42 +00:00
keyDecoded, err := base64.StdEncoding.DecodeString(buffer.String())
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxImportPrivKey: failed to import private key: %w", err)
2022-05-04 09:42:42 +00:00
}
key, err = crypto.NewKey(keyDecoded)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxImportPrivKey: failed to import private key: %w", err)
2022-05-04 09:42:42 +00:00
}
2022-04-18 09:16:41 +00:00
}
}
entity := key.GetEntity()
if entity.Identities[xmppURI] == nil {
return errors.New("Key identity is not " + xmppURI)
2022-04-18 09:16:41 +00:00
}
pk, err := key.GetPublicKey()
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxImportPrivKey: failed to get public key associated to private key: %w", err)
2022-04-18 09:16:41 +00:00
}
pubKey, err := crypto.NewKey(pk)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxImportPrivKey: failed to get public key associated to private key: %w", err)
2022-04-18 09:16:41 +00:00
}
fingerprint := strings.ToUpper(pubKey.GetFingerprint())
_, err = oxRecvPublicKeys(client, iqc, jid, fingerprint)
if err != nil {
2022-05-04 09:42:42 +00:00
err = oxPublishPubKey(jid, client, iqc, pubKey)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxImportPrivKey: failed to publish public key: %w", err)
2022-05-04 09:42:42 +00:00
}
2022-04-18 09:16:41 +00:00
}
location, err := oxGetPrivKeyLoc(jid)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxImportPrivKey: failed to determine private key location: %w", err)
2022-04-18 09:16:41 +00:00
}
keySerialized, err := key.Serialize()
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxImportPrivKey: failed to serialize private key: %w", err)
2022-04-18 09:16:41 +00:00
}
2022-04-18 15:14:03 +00:00
err = oxStoreKey(location,
2022-04-18 09:16:41 +00:00
base64.StdEncoding.EncodeToString(keySerialized))
if err != nil {
2022-04-18 09:16:41 +00:00
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 {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxImportPrivKey: failed to publish public key: %w", err)
}
return nil
}
2023-06-07 20:33:43 +00:00
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 {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxPublishPubKey: iq failure publishing public key: %w", err)
}
if iqReply.Type != strResult {
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()
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 {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxPublishPubKey: iq failure publishing public key list: %w", err)
}
if iqReply.Type != strResult {
return errors.New("couldn't publish public key list")
}
2022-04-18 09:16:41 +00:00
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 {
2023-06-07 20:28:01 +00:00
return strError, fmt.Errorf("oxGetPrivKeyLoc: failed to determine user dir: %w", err)
}
if homeDir == "" {
2023-06-07 20:28:01 +00:00
return strError, errors.New("oxGetPrivKeyLoc: received empty string for home directory")
}
dataDir = homeDir + "/.local/share"
}
dataDir += "/go-sendxmpp/oxprivkeys/"
if _, err = os.Stat(dataDir); os.IsNotExist(err) {
err = os.MkdirAll(dataDir, defaultDirRights)
if err != nil {
2023-06-07 20:28:01 +00:00
return strError, fmt.Errorf("oxGetPrivKeyLoc: could not create folder for private keys: %w", err)
}
}
// TODO: Remove handling of oldDataFile in a later version when it's very likely that there are no
2024-02-17 11:57:47 +00:00
// more versions in use using the oldDataFile (<0.8.3).
oldDataFile := dataDir + base64.StdEncoding.EncodeToString([]byte(jid))
2024-02-21 19:01:51 +00:00
oldDataFile2 := dataDir + strings.Replace(jid, "@", "_at_", -1)
dataFile := dataDir + strings.Replace(strings.Replace(jid, "@", "_at_", -1), ".", "_", -1)
if _, err := os.Stat(oldDataFile); err == nil {
err := os.Rename(oldDataFile, dataFile)
if err != nil {
return dataFile, err
}
}
2024-02-21 19:01:51 +00:00
if _, err := os.Stat(oldDataFile2); err == nil {
err := os.Rename(oldDataFile2, dataFile)
if err != nil {
return dataFile, err
}
}
return dataFile, nil
}
2022-04-18 15:14:03 +00:00
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 {
2023-06-07 20:28:01 +00:00
return strError, fmt.Errorf("oxGetPubKeyLoc: failed to determine user dir: %w", err)
2022-04-18 15:14:03 +00:00
}
if homeDir == "" {
2023-06-07 20:28:01 +00:00
return strError, errors.New("oxGetPubKeyLoc: received empty string for home directory")
2022-04-18 15:14:03 +00:00
}
dataDir = homeDir + "/.local/share"
}
dataDir += "/go-sendxmpp/oxpubkeys/"
2022-04-18 15:14:03 +00:00
if _, err = os.Stat(dataDir); os.IsNotExist(err) {
err = os.MkdirAll(dataDir, defaultDirRights)
if err != nil {
2023-06-07 20:28:01 +00:00
return strError, fmt.Errorf("oxGetPubKeyLoc: could not create folder for public keys: %w", err)
2022-04-18 15:14:03 +00:00
}
}
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 {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPrivKey: failed to decode private key: %w", err)
}
key, err := crypto.NewKey(decodedPrivKey)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPrivKey: failed to decode private key: %w", err)
}
if passphrase != "" {
key, err = key.Unlock([]byte(passphrase))
if err != nil {
2022-05-05 13:56:53 +00:00
log.Fatal("Ox: couldn't unlock private key.")
}
}
isLocked, err := key.IsLocked()
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPrivKey: failed to check whether private key is locked: %w", err)
}
if isLocked {
2022-05-05 13:56:53 +00:00
log.Fatal("Ox: private key is locked.")
}
if key.IsExpired() {
2022-05-05 13:56:53 +00:00
return nil, errors.New("Ox: private key is expired: " + key.GetFingerprint())
}
return key, nil
}
2022-04-18 15:14:03 +00:00
func oxStoreKey(location string, key string) error {
var file *os.File
file, err := os.Create(location)
if err != nil {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
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,
2023-06-06 08:47:40 +00:00
passphrase string, keyType string,
) error {
xmppURI := "xmpp:" + jid
key, err := crypto.GenerateKey(xmppURI, "", keyType, defaultRSABits)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxGenPrivKey: failed to generate private key: %w", err)
}
if passphrase != "" {
key, err = key.Lock([]byte(passphrase))
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxGenPrivKey: failed to lock key with passphrase: %w", err)
}
}
keySerialized, err := key.Serialize()
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxGenPrivKey: failed to serialize private key: %w", err)
}
2022-04-18 09:16:41 +00:00
location, err := oxGetPrivKeyLoc(jid)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxGenPrivKey: failed to get private key location: %w", err)
2022-04-18 09:16:41 +00:00
}
2022-04-18 15:14:03 +00:00
err = oxStoreKey(location,
base64.StdEncoding.EncodeToString(keySerialized))
if err != nil {
log.Fatal(err)
}
decodedPubKey, err := key.GetPublicKey()
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxGenPrivKey: failed to decode public key: %w", err)
}
pubKey, err := crypto.NewKey(decodedPubKey)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxGenPrivKey: failed to decode public key: %w", err)
}
err = oxPublishPubKey(jid, client, iqc, pubKey)
if err != nil {
2023-06-07 20:28:01 +00:00
return fmt.Errorf("oxGenPrivKey: failed to publish public key: %w", err)
}
return nil
}
2023-06-07 20:33:43 +00:00
func oxRecvPublicKeys(client *xmpp.Client, iqc chan xmpp.IQ, recipient string, fingerprint string) (*crypto.KeyRing, error) {
2022-04-23 21:34:03 +00:00
opkr := etree.NewDocument()
opkr.WriteSettings.AttrSingleQuote = true
2022-04-23 21:34:03 +00:00
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 {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxRecvPublicKeys: iq error requesting public keys: %w", err)
}
if oxPublicKey.Type != strResult {
2023-04-09 12:56:22 +00:00
return nil, errors.New("error while requesting public key for " +
recipient)
}
2022-04-23 21:34:03 +00:00
oxPublicKeyXML := etree.NewDocument()
err = oxPublicKeyXML.ReadFromBytes(oxPublicKey.Query)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxRecvPublicKeys: failed parsing iq reply to public key request: %w", err)
}
keyring, err := crypto.NewKeyRing(nil)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxRecvPublicKeys: failed reading public key: %w", err)
}
2022-04-23 21:34:03 +00:00
oxPublicKeyXMLPubsub := oxPublicKeyXML.SelectElement("pubsub")
2022-09-01 20:13:13 +00:00
if oxPublicKeyXMLPubsub == nil {
return nil, errors.New("ox: no pubsub element in reply to public " +
"key request")
}
2022-04-23 21:34:03 +00:00
oxPublicKeyXMLItems := oxPublicKeyXMLPubsub.SelectElement("items")
2022-09-01 20:13:13 +00:00
if oxPublicKeyXMLItems == nil {
return nil, errors.New("ox: no items element in reply to public " +
"key request")
}
2022-04-23 21:34:03 +00:00
oxPublicKeyXMLItem := oxPublicKeyXMLItems.SelectElement("item")
2022-09-01 20:13:13 +00:00
if oxPublicKeyXMLItem == nil {
return nil, errors.New("ox: no item element in reply to public " +
"key request")
}
2022-04-23 21:34:03 +00:00
oxPublicKeyXMLPubkeys := oxPublicKeyXMLItem.SelectElements("pubkey")
for _, r := range oxPublicKeyXMLPubkeys {
data := r.SelectElement("data")
2022-09-01 20:13:13 +00:00
if data == nil {
continue
}
2022-04-23 21:34:03 +00:00
decodedPubKey, err := base64.StdEncoding.DecodeString(data.Text())
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxRecvPublicKeys: failed to decode public key: %w", err)
}
key, err := crypto.NewKey(decodedPubKey)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxRecvPublicKeys: failed to decode public key: %w", err)
}
if key.IsExpired() {
return nil, errors.New("Key is expired: " + fingerprint)
}
err = keyring.AddKey(key)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxRecvPublicKeys: failed adding public key to keyring: %w", err)
}
}
return keyring, nil
}
2023-06-07 20:33:43 +00:00
func oxGetPublicKeyRing(client *xmpp.Client, iqc chan xmpp.IQ, recipient string) (*crypto.KeyRing, error) {
publicKeyRing, err := crypto.NewKeyRing(nil)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPublicKeyRing: failed to create a new keyring: %w", err)
}
2022-04-18 15:14:03 +00:00
2022-04-23 21:59:19 +00:00
oxPubKeyListReq := etree.NewDocument()
oxPubKeyListReq.WriteSettings.AttrSingleQuote = true
2022-04-23 21:59:19 +00:00
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 {
2023-04-09 12:56:22 +00:00
return nil, errors.New("error while requesting public openpgp keys for " +
recipient)
}
2022-04-23 23:20:39 +00:00
oxPubKeyListXML := etree.NewDocument()
err = oxPubKeyListXML.ReadFromBytes(oxPublicKeyList.Query)
if err != nil {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPublicKeyRing: failed to set time for newest key to 1900-01-01: %w", err)
}
2022-04-23 23:20:39 +00:00
oxPubKeyListXMLPubsub := oxPubKeyListXML.SelectElement("pubsub")
2022-09-01 20:13:13 +00:00
if oxPubKeyListXMLPubsub == nil {
return nil, errors.New("ox: no pubsub element in public key list")
}
2022-04-23 23:20:39 +00:00
oxPubKeyListXMLPubsubItems := oxPubKeyListXMLPubsub.SelectElement("items")
2022-09-01 20:13:13 +00:00
if oxPubKeyListXMLPubsubItems == nil {
return nil, errors.New("ox: no items element in public key list")
}
2022-04-23 23:20:39 +00:00
oxPubKeyListXMLPubsubItemsItem := oxPubKeyListXMLPubsubItems.SelectElement("item")
2022-09-01 20:13:13 +00:00
if oxPubKeyListXMLPubsubItemsItem == nil {
return nil, errors.New("ox: no item element in public key list")
}
2022-04-23 23:20:39 +00:00
oxPubKeyListXMLPubsubItemsItemPkl := oxPubKeyListXMLPubsubItemsItem.SelectElement("public-keys-list")
2022-09-01 20:13:13 +00:00
if oxPubKeyListXMLPubsubItemsItemPkl == nil {
2023-06-07 20:46:51 +00:00
return nil, errors.New("ox: no public-keys-list element")
2022-09-01 20:13:13 +00:00
}
2023-06-06 08:47:40 +00:00
oxPubKeyListXMLPubsubItemsItemPklPm := oxPubKeyListXMLPubsubItemsItemPkl.SelectElements("pubkey-metadata")
2022-04-23 23:20:39 +00:00
for _, r := range oxPubKeyListXMLPubsubItemsItemPklPm {
date := r.SelectAttr("date")
2022-09-01 20:13:13 +00:00
if date == nil {
continue
}
2022-04-23 23:20:39 +00:00
fingerprint := r.SelectAttr("v4-fingerprint")
2022-09-01 20:13:13 +00:00
if fingerprint == nil {
continue
}
2022-04-23 23:20:39 +00:00
keyDate, err := time.Parse(time.RFC3339, date.Value)
if err != nil {
2023-06-07 20:28:01 +00:00
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, errors.New("server didn't provide public key fingerprints for " + recipient)
}
pubKeyRingLocation, err := oxGetPubKeyLoc(pubKeyRingID)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPublicKeyRing: failed to get public key ring location: %w", err)
2022-04-18 15:14:03 +00:00
}
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 {
2023-06-07 20:28:01 +00:00
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, errors.New("couldn't read public keys from cache")
}
for _, r := range pubKeys {
keyByte, err := base64.StdEncoding.DecodeString(r.Text())
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPublicKeyRing: failed to decode saved key: %w", err)
}
key, err := crypto.NewKey(keyByte)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPublicKeyRing: failed to parse saved key: %w", err)
}
if !key.IsExpired() {
err = publicKeyRing.AddKey(key)
if err != nil {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPublicKeyRing: failed to serialize key: %w", err)
2022-04-18 15:14:03 +00:00
}
saveKey := pubKeySaveXML.CreateElement("pubkey")
saveKey.SetText(base64.StdEncoding.EncodeToString(keySerialized))
2022-04-18 15:14:03 +00:00
}
err = pubKeySaveXML.WriteToFile(pubKeyRingLocation)
if err != nil {
2023-06-07 20:28:01 +00:00
return nil, fmt.Errorf("oxGetPublicKeyRing: failed to create xml for saving public key: %w", err)
2022-04-18 15:14:03 +00:00
}
return pubKeyRing, nil
}
2023-06-07 20:33:43 +00:00
func oxEncrypt(client *xmpp.Client, oxPrivKey *crypto.Key, recipient string, keyRing *crypto.KeyRing, message string) (string, error) {
2022-07-04 14:20:17 +00:00
if message == "" {
return "", nil
}
privKeyRing, err := crypto.NewKeyRing(oxPrivKey)
if err != nil {
2023-06-07 20:28:01 +00:00
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)
}
}
2022-04-23 21:59:19 +00:00
oxCryptMessage := etree.NewDocument()
oxCryptMessage.WriteSettings.AttrSingleQuote = true
2022-04-23 22:33:17 +00:00
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)))
2022-04-23 22:33:17 +00:00
oxCryptMessageScPayload := oxCryptMessageSc.CreateElement("payload")
oxCryptMessageScPayloadBody := oxCryptMessageScPayload.CreateElement("body")
oxCryptMessageScPayloadBody.CreateAttr("xmlns", nsJabberClient)
oxCryptMessageScPayloadBody.CreateText(message)
2022-04-23 21:59:19 +00:00
ocm, err := oxCryptMessage.WriteToString()
if err != nil {
2023-06-07 20:28:01 +00:00
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 {
2023-06-07 20:28:01 +00:00
return strError, fmt.Errorf("oxEncrypt: failed to create pgp message: %w", err)
}
2022-04-23 22:56:44 +00:00
om := etree.NewDocument()
om.WriteSettings.AttrSingleQuote = true
2022-04-23 22:56:44 +00:00
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 {
2023-06-07 20:28:01 +00:00
return strError, fmt.Errorf("oxEncrypt: failed to create xml for ox message: %w", err)
}
2022-04-23 22:56:44 +00:00
return oms, nil
}