Add experimental Ox (OpenPGP for XMPP) support.

code-cleanup
Martin Dosch 2 years ago
parent 709ba5ad51
commit e9e0d8c735

@ -1,6 +1,8 @@
# Changelog
## Unreleased
### Added
- Experimental support for Ox (OpenPGP for XMPP) encryption.
## [v0.3.0] 2022-03-21
### Added

@ -83,7 +83,7 @@ If no configuration file is present or if the values should be overridden it is
the account details via command line options:
```plain
Usage: go-sendxmpp [-cdilnt] [-f value] [--help] [--http-upload value] [-j value] [-m value] [--muc-password value] [-p value] [--raw] [-r value] [--timeout value] [--tls-version value] [-u value] [--version] [parameters ...]
Usage: go-sendxmpp [-cdilnt] [-f value] [--help] [--http-upload value] [-j value] [-m value] [--muc-password value] [--ox] [--ox-genprivkey] [--ox-passphrase value] [-p value] [--raw] [-r value] [--timeout value] [--tls-version value] [-u value] [--version] [parameters ...]
-c, --chatroom Send message to a chatroom.
-d, --debug Show debugging info.
-f, --file=value Set configuration file. (Default:
@ -101,6 +101,13 @@ Usage: go-sendxmpp [-cdilnt] [-f value] [--help] [--http-upload value] [-j value
Password for password protected MUCs.
-n, --no-tls-verify
Skip verification of TLS certificates (not recommended).
--ox Use "OpenPGP for XMPP" encryption (experimental).
--ox-genprivkey
Generate a public OpenPGP key for the given JID and publish
the corresponding public key.
--ox-passphrase=value
Passphrase for locking and unlocking the private OpenPGP
key.
-p, --password=value
Password for XMPP account.
--raw Send raw XML.
@ -111,7 +118,7 @@ Usage: go-sendxmpp [-cdilnt] [-f value] [--help] [--http-upload value] [-j value
Connection timeout in seconds. [10]
-t, --tls Use direct TLS.
--tls-version=value
Minimal TLS version. 10 (TSLv1.0), 11 (TLSv1.1), 12
Minimal TLS version. 10 (TLSv1.0), 11 (TLSv1.1), 12
(TLSv1.2) or 13 (TLSv1.3). [12]
-u, --username=value
Username for XMPP account.

@ -5,6 +5,14 @@
package main
const (
VERSION = "0.4.0-devel"
nsHttpUpload = "urn:xmpp:http:upload:0"
VERSION = "0.4.0-devel"
nsEme = "urn:xmpp:eme:0"
nsHttpUpload = "urn:xmpp:http:upload:0"
nsJabberClient = "jabber:client"
nsJabberData = "jabber:x:data"
nsOx = "urn:xmpp:openpgp:0"
nsOxPubKeys = "urn:xmpp:openpgp:0:public-keys"
nsPubsub = "http://jabber.org/protocol/pubsub"
oxAltBody = "This message is encrypted (XEP-0373: OpenPGP for XMPP)."
pubsubPubOptions = "http://jabber.org/protocol/pubsub#publish-options"
)

@ -3,10 +3,21 @@ module salsa.debian.org/mdosch/go-sendxmpp
go 1.17
require (
github.com/ProtonMail/gopenpgp/v2 v2.4.6
github.com/beevik/etree v1.1.0
github.com/gabriel-vasile/mimetype v1.4.0
github.com/mattn/go-xmpp v0.0.0-20220410054612-99ddfc1aa46a
github.com/pborman/getopt/v2 v2.1.0
salsa.debian.org/mdosch/xmppsrv v0.1.1
)
require golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 // indirect
require (
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/text v0.3.7 // indirect
)

@ -1,20 +1,84 @@
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 h1:cSHEbLj0GZeHM1mWG84qEnGFojNEQ83W7cwaPRjcwXU=
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
github.com/ProtonMail/gopenpgp/v2 v2.4.6 h1:/EcJsFIsE0ywShAJ+lNLafcaSd6GBhIzHsaBID5pGXw=
github.com/ProtonMail/gopenpgp/v2 v2.4.6/go.mod h1:ZW1KxHNG6q5LMgFKf9Ap/d2eVYeyGf5+fAUEAjJWtmo=
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mattn/go-xmpp v0.0.0-20220410054612-99ddfc1aa46a h1:ErhE3+ijxVepytrWsoNSz9D11gCEhWIFJ2kN1eUvj5A=
github.com/mattn/go-xmpp v0.0.0-20220410054612-99ddfc1aa46a/go.mod h1:Cs5mF0OsrRRmhkyOod//ldNPOwJsrBvJ+1WRspv0xoc=
github.com/pborman/getopt/v2 v2.1.0 h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA=
github.com/pborman/getopt/v2 v2.1.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c=
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
salsa.debian.org/mdosch/xmppsrv v0.1.1 h1:I/5HS+cOg27LRADQ2R4KqQY6DTVMsaPMu9ywSAMTOG8=
salsa.debian.org/mdosch/xmppsrv v0.1.1/go.mod h1:udWXnWFa9zkcyN9YSB/u44BCnnRDpeQ0eDy3MVLjHZQ=

@ -8,8 +8,21 @@ import (
"crypto/rand"
"fmt"
"log"
mrand "math/rand"
"time"
)
func getRpad() string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,;?!+-_§$%&/()=")
mrand.Seed(time.Now().UnixNano())
s := make([]rune, mrand.Intn(30)+20)
for i := range s {
s[i] = letters[mrand.Intn(len(letters))]
}
return string(s)
}
func getID() string {
b := make([]byte, 12)
_, err := rand.Read(b)

@ -5,7 +5,6 @@
package main
import (
"errors"
"log"
"github.com/mattn/go-xmpp" // BSD-3-Clause
@ -24,9 +23,6 @@ func sendIQ(client *xmpp.Client, target string, iQtype string,
}
iq = <-c
close(c)
if iq.Type != "result" {
return iq, errors.New("No result for " + content)
}
return iq, nil
}

@ -6,6 +6,105 @@ package main
import "encoding/xml"
// Created with https://github.com/miku/zek
type OxMessageElement struct {
XMLName xml.Name `xml:"message"`
Text string `xml:",chardata"`
To string `xml:"to,attr"`
Id string `xml:"id,attr"`
From string `xml:"from,attr"`
Openpgp struct {
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
} `xml:"openpgp"`
Encryption struct {
XMLName xml.Name `xml:"encryption"`
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
Namespace string `xml:"namespace,attr"`
}
Body string `xml:"body"`
}
// Created with https://github.com/miku/zek
type OxCryptElement struct {
XMLName xml.Name `xml:"signcrypt"`
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
To struct {
Text string `xml:",chardata"`
Jid string `xml:"jid,attr"`
} `xml:"to"`
Time struct {
Text string `xml:",chardata"`
Stamp string `xml:"stamp,attr"`
} `xml:"time"`
Rpad string `xml:"rpad"`
Payload struct {
Text string `xml:",chardata"`
Body struct {
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
} `xml:"body"`
} `xml:"payload"`
}
// Created with https://github.com/miku/zek
type OxPublicKey struct {
XMLName xml.Name `xml:"pubsub"`
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
Items struct {
Text string `xml:",chardata"`
Node string `xml:"node,attr"`
Item struct {
Text string `xml:",chardata"`
ID string `xml:"id,attr"`
Pubkey struct {
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
Data string `xml:"data"`
} `xml:"pubkey"`
} `xml:"item"`
} `xml:"items"`
}
// Created with https://github.com/miku/zek
type OxPublicKeysList struct {
XMLName xml.Name `xml:"pubsub"`
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
Items struct {
Text string `xml:",chardata"`
Node string `xml:"node,attr"`
Item struct {
Text string `xml:",chardata"`
ID string `xml:"id,attr"`
PublicKeysList struct {
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
PubkeyMetadata []struct {
Text string `xml:",chardata"`
Date string `xml:"date,attr"`
V4Fingerprint string `xml:"v4-fingerprint,attr"`
} `xml:"pubkey-metadata"`
} `xml:"public-keys-list"`
} `xml:"item"`
} `xml:"items"`
}
// Created with https://github.com/miku/zek
type IQPubsubRequest struct {
XMLName xml.Name `xml:"pubsub"`
Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
Items struct {
Text string `xml:",chardata"`
Node string `xml:"node,attr"`
MaxItems string `xml:"max_items,attr"`
} `xml:"items"`
}
// Created with https://github.com/miku/zek
type IQDiscoItemsType struct {
XMLName xml.Name `xml:"query"`

@ -16,8 +16,9 @@ import (
"strings"
"time"
"github.com/mattn/go-xmpp" // BSD-3-Clause
"github.com/pborman/getopt/v2" // BSD-3-Clause
"github.com/ProtonMail/gopenpgp/v2/crypto" // MIT License
"github.com/mattn/go-xmpp" // BSD-3-Clause
"github.com/pborman/getopt/v2" // BSD-3-Clause
)
type configuration struct {
@ -69,10 +70,16 @@ func readMessage(messageFilePath string) (string, error) {
}
func main() {
type recipientsType struct {
Jid string
OxKey *crypto.Key
}
var (
err error
message, user, server, password, resource string
oxPrivKey *crypto.Key
recipients []recipientsType
)
// Define command line flags.
@ -99,6 +106,11 @@ func main() {
"Minimal TLS version. 10 (TLSv1.0), 11 (TLSv1.1), 12 (TLSv1.2) or 13 (TLSv1.3).")
flagVersion := getopt.BoolLong("version", 0, "Show version information.")
flagMUCPassword := getopt.StringLong("muc-password", 0, "", "Password for password protected MUCs.")
flagOx := getopt.BoolLong("ox", 0, "Use \"OpenPGP for XMPP\" encryption (experimental).")
flagOxGenPrivKey := getopt.BoolLong("ox-genprivkey", 0,
"Generate a public OpenPGP key for the given JID and publish the corresponding public key.")
flagOxPassphrase := getopt.StringLong("ox-passphrase", 0, "",
"Passphrase for locking and unlocking the private OpenPGP key.")
// Parse command line flags.
getopt.Parse()
@ -116,24 +128,28 @@ func main() {
os.Exit(0)
}
// Quit if Ox (OpenPGP for XMPP) is requested for unsupported operations like
// groupchat, http-upload or listening.
if *flagOx && (*flagHttpUpload != "" || *flagChatroom || *flagListen) {
switch {
case *flagHttpUpload != "":
log.Fatal("No Ox support for http-upload available.")
case *flagChatroom:
log.Fatal("No Ox support for chat rooms available.")
case *flagListen:
log.Fatal("No Ox support for receiving messages available.")
}
}
// Read recipients from command line and quit if none are specified.
// For listening or sending raw XML it's not required to specify a recipient except
// when sending raw messages to MUCs (go-sendxmpp will join the MUC automatically).
recipients := getopt.Args()
if (len(recipients) == 0 && !*flagRaw && !*flagListen) ||
(len(recipients) == 0 && *flagChatroom) {
recipientsList := getopt.Args()
if (len(recipientsList) == 0 && !*flagRaw && !*flagListen && !*flagOxGenPrivKey) ||
(len(recipientsList) == 0 && *flagChatroom) {
log.Fatal("No recipient specified.")
}
// Check that all recipient JIDs are valid.
for i, recipient := range recipients {
validatedJid, err := MarshalJID(recipient)
if err != nil {
log.Fatal(err)
}
recipients[i] = validatedJid
}
// Read configuration file if user or password is not specified.
if *flagUser == "" || *flagPassword == "" {
// Read configuration from file.
@ -240,6 +256,48 @@ func main() {
log.Fatal(err)
}
for _, r := range getopt.Args() {
var re recipientsType
re.Jid = r
re.OxKey, err = oxGetPublicKey(client, r)
if err != nil {
fmt.Println("Couldn't receive key for:", r)
}
recipients = append(recipients, re)
}
// Check that all recipient JIDs are valid.
for i, recipient := range recipients {
validatedJid, err := MarshalJID(recipient.Jid)
if err != nil {
log.Fatal(err)
}
recipients[i].Jid = validatedJid
}
if *flagOxGenPrivKey {
validatedOwnJid, err := MarshalJID(user)
if err != nil {
log.Fatal(err)
}
err = oxGenPrivKey(validatedOwnJid, client, *flagOxPassphrase)
if err != nil {
log.Fatal(err)
}
os.Exit(0)
}
if *flagOx {
validatedOwnJid, err := MarshalJID(user)
if err != nil {
log.Fatal(err)
}
oxPrivKey, err = oxGetPrivKey(validatedOwnJid, *flagOxPassphrase)
if err != nil {
log.Fatal(err)
}
}
if *flagHttpUpload != "" {
message = httpUpload(client, tlsConfig.ServerName,
*flagHttpUpload)
@ -282,10 +340,10 @@ func main() {
for _, recipient := range recipients {
if *flagMUCPassword != "" {
dummyTime := time.Now()
_, err = client.JoinProtectedMUC(recipient, *flagResource,
_, err = client.JoinProtectedMUC(recipient.Jid, *flagResource,
*flagMUCPassword, xmpp.NoHistory, 0, &dummyTime)
} else {
_, err = client.JoinMUCNoHistory(recipient, *flagResource)
_, err = client.JoinMUCNoHistory(recipient.Jid, *flagResource)
}
if err != nil {
// Try to nicely close connection,
@ -304,7 +362,7 @@ func main() {
}
// After sending the message, leave the MUCs
for _, recipient := range recipients {
_, err = client.LeaveMUC(recipient)
_, err = client.LeaveMUC(recipient.Jid)
if err != nil {
log.Println(err)
}
@ -334,7 +392,7 @@ func main() {
fmt.Println(t.Format(time.RFC3339), bareFrom+":", v.Text)
} else {
for _, recipient := range recipients {
if bareFrom == strings.ToLower(recipient) {
if bareFrom == strings.ToLower(recipient.Jid) {
fmt.Println(t.Format(time.RFC3339), bareFrom+":", v.Text)
}
}
@ -353,10 +411,10 @@ func main() {
// Join the MUC.
if *flagMUCPassword != "" {
dummyTime := time.Now()
_, err = client.JoinProtectedMUC(recipient, *flagResource,
_, err = client.JoinProtectedMUC(recipient.Jid, *flagResource,
*flagMUCPassword, xmpp.NoHistory, 0, &dummyTime)
} else {
_, err = client.JoinMUCNoHistory(recipient, *flagResource)
_, err = client.JoinMUCNoHistory(recipient.Jid, *flagResource)
}
if err != nil {
// Try to nicely close connection,
@ -373,7 +431,7 @@ func main() {
scanner.Scan()
message = scanner.Text()
for _, recipient := range recipients {
_, err = client.Send(xmpp.Chat{Remote: recipient,
_, err = client.Send(xmpp.Chat{Remote: recipient.Jid,
Type: "groupchat", Text: message})
if err != nil {
// Try to nicely close connection,
@ -387,10 +445,10 @@ func main() {
// Send the message.
for _, recipient := range recipients {
if *flagHttpUpload != "" {
_, err = client.Send(xmpp.Chat{Remote: recipient,
_, err = client.Send(xmpp.Chat{Remote: recipient.Jid,
Type: "groupchat", Ooburl: message, Text: message})
} else {
_, err = client.Send(xmpp.Chat{Remote: recipient,
_, err = client.Send(xmpp.Chat{Remote: recipient.Jid,
Type: "groupchat", Text: message})
}
if err != nil {
@ -404,7 +462,7 @@ func main() {
for _, recipient := range recipients {
// After sending the message, leave the Muc
_, err = client.LeaveMUC(recipient)
_, err = client.LeaveMUC(recipient.Jid)
if err != nil {
log.Println(err)
}
@ -432,24 +490,51 @@ func main() {
scanner.Scan()
message = scanner.Text()
for _, recipient := range recipients {
_, err = client.Send(xmpp.Chat{Remote: recipient,
Type: "chat", Text: message})
if err != nil {
// Try to nicely close connection,
// even if there was an error sending.
_ = client.Close()
log.Fatal(err)
if *flagOx {
oxMessage, err := oxEncrypt(client, oxPrivKey,
recipient.Jid, recipient.OxKey, message)
if err != nil {
fmt.Println("Ox: couldn't encrypt to",
recipient.Jid)
continue
}
_, err = client.SendOrg(oxMessage)
if err != nil {
log.Fatal(err)
}
} else {
_, err = client.Send(xmpp.Chat{Remote: recipient.Jid,
Type: "chat", Text: message})
if err != nil {
// Try to nicely close connection,
// even if there was an error sending.
_ = client.Close()
log.Fatal(err)
}
}
}
}
} else {
for _, recipient := range recipients {
if *flagHttpUpload != "" {
_, err = client.Send(xmpp.Chat{Remote: recipient, Type: "chat",
Ooburl: message, Text: message})
_, err = client.Send(xmpp.Chat{Remote: recipient.Jid,
Type: "chat", Ooburl: message, Text: message})
} else {
_, err = client.Send(xmpp.Chat{Remote: recipient, Type: "chat",
Text: message})
if *flagOx {
oxMessage, err := oxEncrypt(client, oxPrivKey,
recipient.Jid, recipient.OxKey, message)
if err != nil {
fmt.Println("Ox: couldn't encrypt to", recipient.Jid)
continue
}
_, err = client.SendOrg(oxMessage)
if err != nil {
log.Fatal(err)
}
} else {
_, err = client.Send(xmpp.Chat{Remote: recipient.Jid,
Type: "chat", Text: message})
}
}
if err != nil {
// Try to nicely close connection,

@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
.TH "GO\-SENDXMPP" "1" "March 2022" ""
.TH "GO\-SENDXMPP" "1" "April 2022" ""
.SH "NAME"
\fBgo\-sendxmpp\fR \- A little tool to send messages to an XMPP contact or MUC\.
.SH "SYNOPSIS"
@ -32,6 +32,16 @@ You can either pipe a programs output to \fBgo\-sendxmpp\fR, write in your termi
.P
\fB\-n\fR, \fB\-\-no\-tls\-verify\fR: Skip verification of TLS certificates (not recommended)\.
.P
\fB\-\-ox\fR: Use "OpenPGP for XMPP" encryption (experimental)\.
.br
\fBOx\fR in go\-sendxmpp only supports sending encrypted 1\-1 messages\. Sending to groupchats, sending encrypted files or receiveing encrypted messages is not supported\. The recipients public key is not cached at the moment but requested via pubsub for every single message\.
.br
There is also no check whether the recipients key is trusted as there is no local keyring used\. Go\-sendxmpp just uses the most recent key that is provided via pubsub and checks that it is not expired\.
.P
\fB\-\-ox\-genprivkey\fR: Generate a public OpenPGP key for the given JID and publish the corresponding public key\. Go\-sendxmpp will save the key in \fB$XDG_DATA_HOME/go\-sendxmpp/oxprivkeys\fR or \fB$HOME/\.local/share/go\-sendxmpp/oxprivkeys\fR\. To protect the key you might want to set a passphrase using \fB\-\-ox\-passphrase\fR while generating the key\.
.P
\fB\-\-ox\-passphrase=value\fR: Passphrase for locking and unlocking the private OpenPGP key\.
.P
\fB\-\-tls\-version\fR=[\fIvalue\fR]: Minimal TLS version\. 10 (TLSv1\.0), 11 (TLSv1\.1), 12 (TLSv1\.2), 13 (TLSv1\.3) (Default: 12)
.P
\fB\-p\fR, \fB\-\-password\fR=[\fIvalue\fR]: Password for XMPP account\.

@ -125,6 +125,24 @@ file location is specified with <code>-f</code> or <code>--file</code>.</p>
<p><code>-n</code>, <code>--no-tls-verify</code>:
Skip verification of TLS certificates (not recommended).</p>
<p><code>--ox</code>:
Use "OpenPGP for XMPP" encryption (experimental). <br>
<code>Ox</code> in go-sendxmpp only supports sending encrypted 1-1 messages. Sending to groupchats, sending
encrypted files or receiveing encrypted messages is not supported.
The recipients public key is not cached at the moment but requested via pubsub for every single message. <br>
There is also no check whether the recipients key is trusted as there is no local keyring used.
Go-sendxmpp just uses the most recent key that is provided via pubsub and checks that it is not
expired.</p>
<p><code>--ox-genprivkey</code>:
Generate a public OpenPGP key for the given JID and publish the corresponding public key.
Go-sendxmpp will save the key in <code>$XDG_DATA_HOME/go-sendxmpp/oxprivkeys</code> or
<code>$HOME/.local/share/go-sendxmpp/oxprivkeys</code>. To protect the key you might want to set a
passphrase using <code>--ox-passphrase</code> while generating the key.</p>
<p><code>--ox-passphrase=value</code>:
Passphrase for locking and unlocking the private OpenPGP key.</p>
<p><code>--tls-version</code>=[<var>value</var>]:
Minimal TLS version. 10 (TLSv1.0), 11 (TLSv1.1), 12 (TLSv1.2), 13 (TLSv1.3) (Default: 12)</p>
@ -172,7 +190,7 @@ License: BSD 2-clause License</p>
<ol class='man-decor man-foot man foot'>
<li class='tl'></li>
<li class='tc'>March 2022</li>
<li class='tc'>April 2022</li>
<li class='tr'>go-sendxmpp(1)</li>
</ol>

@ -50,6 +50,24 @@ file location is specified with `-f` or `--file`.
`-n`, `--no-tls-verify`:
Skip verification of TLS certificates (not recommended).
`--ox`:
Use "OpenPGP for XMPP" encryption (experimental).
`Ox` in go-sendxmpp only supports sending encrypted 1-1 messages. Sending to groupchats, sending
encrypted files or receiveing encrypted messages is not supported.
The recipients public key is not cached at the moment but requested via pubsub for every single message.
There is also no check whether the recipients key is trusted as there is no local keyring used.
Go-sendxmpp just uses the most recent key that is provided via pubsub and checks that it is not
expired.
`--ox-genprivkey`:
Generate a public OpenPGP key for the given JID and publish the corresponding public key.
Go-sendxmpp will save the key in `$XDG_DATA_HOME/go-sendxmpp/oxprivkeys` or
`$HOME/.local/share/go-sendxmpp/oxprivkeys`. To protect the key you might want to set a
passphrase using `--ox-passphrase` while generating the key.
`--ox-passphrase=value`:
Passphrase for locking and unlocking the private OpenPGP key.
`--tls-version`=[<value>]:
Minimal TLS version. 10 (TLSv1.0), 11 (TLSv1.1), 12 (TLSv1.2), 13 (TLSv1.3) (Default: 12)

384
ox.go

@ -0,0 +1,384 @@
// 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 (
"bytes"
"encoding/base64"
"encoding/xml"
"errors"
"log"
"os"
"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 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 oxGetPrivKey(jid string, passphrase string) (*crypto.Key, error) {
dataFile, err := oxGetPrivKeyLoc(jid)
if err != nil {
log.Fatal(err)
}
file, err := os.OpenFile(dataFile, os.O_RDWR, 0600)
if err != nil {
log.Fatal("Error: can't open private key file:", err)
}
defer file.Close()
keyBuffer := new(bytes.Buffer)
_, err = keyBuffer.ReadFrom(file)
if err != nil {
return nil, 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 oxStorePrivKey(jid string, privKey string) error {
dataFile, err := oxGetPrivKeyLoc(jid)
if err != nil {
log.Fatal(err)
}
var file *os.File
if _, err := os.Stat(dataFile); os.IsNotExist(err) {
file, err = os.Create(dataFile)
if err != nil {
return err
}
} else {
file, err = os.OpenFile(dataFile, os.O_RDWR, 0600)
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(privKey))
if err != nil {
return err
}
return nil
}
func oxGenPrivKey(jid string, client *xmpp.Client, passphrase string) error {
xmppUri := "xmpp:" + jid
key, err := crypto.GenerateKey(xmppUri, xmppUri, "x25519", 0)
if err != nil {
return err
}
decodedPubKey, err := key.GetPublicKey()
if err != nil {
return err
}
pubKeyBase64 := base64.StdEncoding.EncodeToString(decodedPubKey)
if passphrase != "" {
key, err = key.Lock([]byte(passphrase))
if err != nil {
return err
}
}
keySerialized, err := key.Serialize()
if err != nil {
return err
}
err = oxStorePrivKey(jid,
base64.StdEncoding.EncodeToString(keySerialized))
if err != nil {
log.Fatal(err)
}
keyCreated := time.Now().UTC().Format("2006-01-02T15:04:05Z")
pubKey, err := crypto.NewKey(decodedPubKey)
if err != nil {
return err
}
fingerprint := strings.ToUpper(pubKey.GetFingerprint())
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, jid, "set", xmlstring)
if err != nil {
return err
}
if iqReply.Type != "result" {
return errors.New("Error while publishing public key.")
}
ownPubKeyFromPubsub, err := oxRecvPublicKey(client, jid, fingerprint)
if err != nil {
return errors.New("Couldn't successfully verify public key upload.")
}
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, jid, "set", xmlstring)
if err != nil {
return err
}
if iqReply.Type != "result" {
return errors.New("Couldn't publish public key list.")
}
return nil
}
func oxRecvPublicKey(client *xmpp.Client, recipient string,
fingerprint string) (*crypto.Key, error) {
var oxPublicKeyRequest IQPubsubRequest
var oxPublicKeyXML OxPublicKey
oxPublicKeyRequest.Xmlns = nsPubsub
oxPublicKeyRequest.Items.Node = nsOxPubKeys + ":" + fingerprint
oxPublicKeyRequest.Items.MaxItems = "1"
opk, err := xml.Marshal(oxPublicKeyRequest)
if err != nil {
return nil, err
}
oxPublicKey, err := sendIQ(client, recipient, "get", string(opk))
if err != nil {
return nil, err
}
if oxPublicKey.Type != "result" {
return nil, errors.New("Err while requesting public key for " + recipient)
}
err = xml.Unmarshal(oxPublicKey.Query, &oxPublicKeyXML)
if err != nil {
return nil, err
}
decodedPubKey, err := base64.StdEncoding.DecodeString(oxPublicKeyXML.Items.Item.Pubkey.Data)
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)
}
return key, nil
}
func oxGetPublicKey(client *xmpp.Client, recipient string) (*crypto.Key, error) {
var oxPublicKeyListRequest IQPubsubRequest
var oxPublicKeyListXML OxPublicKeysList
oxPublicKeyListRequest.Xmlns = nsPubsub
oxPublicKeyListRequest.Items.Node = nsOxPubKeys
oxPublicKeyListRequest.Items.MaxItems = "100"
opkl, err := xml.Marshal(oxPublicKeyListRequest)
if err != nil {
log.Fatal(err)
}
oxPublicKeyList, err := sendIQ(client, recipient, "get", string(opkl))
if err != nil {
log.Fatal(err)
}
if oxPublicKeyList.Type != "result" {
return nil, errors.New("Error while requesting public openpgp keys for " +
recipient)
}
err = xml.Unmarshal(oxPublicKeyList.Query, &oxPublicKeyListXML)
if err != nil {
return nil, err
}
fingerprint := "none"
newestKey, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
if err != nil {
return nil, err
}
for _, r := range oxPublicKeyListXML.Items.Item.PublicKeysList.PubkeyMetadata {
keyDate, err := time.Parse(time.RFC3339, r.Date)
if err != nil {
return nil, err
}
if keyDate.After(newestKey) {
newestKey = keyDate
fingerprint = r.V4Fingerprint
}
}
if fingerprint == "none" {
return nil, errors.New("server didn't provide public key fingerprints for " + recipient)
}
key, err := oxRecvPublicKey(client, recipient, fingerprint)
if err != nil {
return nil, errors.New("Couldn't fetch public key.")
}
return key, nil
}
func oxEncrypt(client *xmpp.Client, oxPrivKey *crypto.Key, recipient string,
recipientKey *crypto.Key, message string) (string, error) {
var oxCryptMessage OxCryptElement
var oxMessage OxMessageElement
keyRing, err := crypto.NewKeyRing(recipientKey)
if err != nil {
return "error", err
}
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.Xmlns = nsOx
oxCryptMessage.To.Jid = recipient
oxCryptMessage.Time.Stamp = time.Now().UTC().Format("2006-01-02T15:04:05Z")
oxCryptMessage.Rpad = getRpad()
oxCryptMessage.Payload.Body.Xmlns = nsJabberClient
oxCryptMessage.Payload.Body.Text = message
ocm, err := xml.Marshal(oxCryptMessage)
if err != nil {
return "error", err
}
plainMessage := crypto.NewPlainMessage([]byte(ocm))
pgpMessage, err := keyRing.Encrypt(plainMessage, privKeyRing)
if err != nil {
return "error", err
}
oxMessage.To = recipient
oxMessage.Id = getID()
oxMessage.From = client.JID()
oxMessage.Openpgp.Text = base64.StdEncoding.EncodeToString(pgpMessage.Data)
oxMessage.Openpgp.Xmlns = nsOx
oxMessage.Encryption.Xmlns = nsEme
oxMessage.Encryption.Namespace = nsOx
oxMessage.Body = oxAltBody
om, err := xml.Marshal(oxMessage)
if err != nil {
return "error", err
}
return string(om), nil
}
Loading…
Cancel
Save