Add experimental Ox (OpenPGP for XMPP) support.
parent
709ba5ad51
commit
e9e0d8c735
@ -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=
|
||||
|
@ -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…
Reference in New Issue