|
|
|
@ -9,6 +9,8 @@ import (
|
|
|
|
|
"encoding/xml"
|
|
|
|
|
"errors"
|
|
|
|
|
"log"
|
|
|
|
|
"os"
|
|
|
|
|
"runtime"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
@ -16,50 +18,154 @@ import (
|
|
|
|
|
"github.com/mattn/go-xmpp" // BSD-3-Clause
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func oxGetPublicKey(client *xmpp.Client, recipient string) (*crypto.Key, error) {
|
|
|
|
|
var oxPublicKeyListRequest IQPubsubRequest
|
|
|
|
|
var oxPublicKeyRequest IQPubsubRequest
|
|
|
|
|
var oxPublicKeyListXML OxPublicKeysList
|
|
|
|
|
var oxPublicKeyXML OxPublicKey
|
|
|
|
|
oxPublicKeyListRequest.Xmlns = nsPubsub
|
|
|
|
|
oxPublicKeyListRequest.Items.Node = nsOxPubKeys
|
|
|
|
|
oxPublicKeyListRequest.Items.MaxItems = "100"
|
|
|
|
|
opkl, err := xml.Marshal(oxPublicKeyListRequest)
|
|
|
|
|
func oxStorePrivKey(jid string, privKey string) error {
|
|
|
|
|
var err error
|
|
|
|
|
var homeDir, dataDir string
|
|
|
|
|
dataDir = os.Getenv("$XDG_DATA_HOME")
|
|
|
|
|
if dataDir == "" {
|
|
|
|
|
homeDir = os.Getenv("$XDG_HOME")
|
|
|
|
|
if homeDir == "" {
|
|
|
|
|
homeDir = os.Getenv("$HOME")
|
|
|
|
|
if homeDir == "" {
|
|
|
|
|
homeDir, err = os.UserHomeDir()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if homeDir == "" {
|
|
|
|
|
return errors.New("No XDG_DATA_HOME")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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 err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dataFile := dataDir + base64.StdEncoding.EncodeToString([]byte(jid))
|
|
|
|
|
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 {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
oxPublicKeyList, err := sendIQ(client, recipient, "get", string(opkl))
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func oxGenPrivKey(jid string, client *xmpp.Client) (*crypto.Key, error) {
|
|
|
|
|
var iqOxPublishKey IQoxPublishKeyType
|
|
|
|
|
var iqOxPublishKeyList IQoxPublishKeyListType
|
|
|
|
|
xmppUri := "xmpp:" + jid
|
|
|
|
|
key, err := crypto.GenerateKey(xmppUri, xmppUri, "x25519", 0)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
keySerialized, _ := key.Serialize()
|
|
|
|
|
|
|
|
|
|
pubKey, err := key.GetPublicKey()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
pubKeyBase64 := base64.StdEncoding.EncodeToString(pubKey)
|
|
|
|
|
err = oxStorePrivKey(jid,
|
|
|
|
|
base64.StdEncoding.EncodeToString(keySerialized))
|
|
|
|
|
if err != nil {
|
|
|
|
|
// return nil, errors.New("Couldn't store private key:")
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if oxPublicKeyList.Type != "result" {
|
|
|
|
|
return nil, errors.New("Error while requesting public openpgp keys for " +
|
|
|
|
|
recipient)
|
|
|
|
|
fingerprint := key.GetFingerprint()
|
|
|
|
|
iqOxPublishKey.Xmlns = nsPubsub
|
|
|
|
|
iqOxPublishKey.Publish.Node = nsOxPubKeys + ":" + fingerprint
|
|
|
|
|
iqOxPublishKey.Publish.Item.ID = time.Now().Format("1900-01-01T06:06:06Z")
|
|
|
|
|
iqOxPublishKey.Publish.Item.Pubkey.Xmlns = nsJabberData
|
|
|
|
|
iqOxPublishKey.Publish.Item.Pubkey.Data = pubKeyBase64
|
|
|
|
|
iqOxPublishKey.PublishOptions.X.Xmlns = nsJabberData
|
|
|
|
|
iqOxPublishKey.PublishOptions.X.Type = "submit"
|
|
|
|
|
iqOxPublishKey.PublishOptions.X.Field[0].Var = "FORM_TYPE"
|
|
|
|
|
iqOxPublishKey.PublishOptions.X.Field[0].Type = "hidden"
|
|
|
|
|
iqOxPublishKey.PublishOptions.X.Field[0].Value = "open"
|
|
|
|
|
iqOxPublishKey.PublishOptions.X.Field[1].Var = "pubsub#acces_model"
|
|
|
|
|
iqOxPublishKey.PublishOptions.X.Field[1].Value = "open"
|
|
|
|
|
|
|
|
|
|
opk, err := xml.Marshal(iqOxPublishKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
err = xml.Unmarshal(oxPublicKeyList.Query, &oxPublicKeyListXML)
|
|
|
|
|
iqReply, err := sendIQ(client, jid, "set", string(opk))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if iqReply.Type != "result" {
|
|
|
|
|
return nil, errors.New("Error whil publishing public key.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fingerprint := "none"
|
|
|
|
|
newestKey, err := time.Parse(time.RFC3339, "1900-01-01T06:06:06Z")
|
|
|
|
|
// TODO: Split GetPublicKey in GetPublicKeyList and GetPublicKey
|
|
|
|
|
// and use the GetPublicKey here as we haven't yet published a device list.
|
|
|
|
|
// The key list will only be published after successfully verifiying the
|
|
|
|
|
// upload.
|
|
|
|
|
ownPubKeyFromPubsub, err := oxRecvPublicKey(client, jid, fingerprint)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.New("Couldn't successfully verify public key upload.")
|
|
|
|
|
}
|
|
|
|
|
ownPubKeyFromPubsubSerialized, err := ownPubKeyFromPubsub.Serialize()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.New("Couldn't successfully verify public key upload.")
|
|
|
|
|
}
|
|
|
|
|
if pubKeyBase64 != base64.StdEncoding.EncodeToString(ownPubKeyFromPubsubSerialized) {
|
|
|
|
|
return nil, errors.New("Couldn't successfully verify public key upload.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iqOxPublishKeyList.Xmlns = nsPubsub
|
|
|
|
|
iqOxPublishKeyList.Publish.Node = nsOxPubKeys
|
|
|
|
|
iqOxPublishKeyList.Publish.Item.PublicKeysList.Xmlns = nsOx
|
|
|
|
|
iqOxPublishKeyList.Publish.Item.PublicKeysList.PubkeyMetadata[0].V4Fingerprint = fingerprint
|
|
|
|
|
iqOxPublishKeyList.Publish.Item.PublicKeysList.PubkeyMetadata[0].Date =
|
|
|
|
|
time.Now().Format("1900-01-01T06:06:06Z")
|
|
|
|
|
iqOxPublishKeyList.PublishOptions.X.Xmlns = nsJabberData
|
|
|
|
|
iqOxPublishKeyList.PublishOptions.X.Field[0].Var = "FORM_TYPE"
|
|
|
|
|
iqOxPublishKeyList.PublishOptions.X.Field[0].Type = "hidden"
|
|
|
|
|
iqOxPublishKeyList.PublishOptions.X.Field[0].Value = pubsubPubOptions
|
|
|
|
|
iqOxPublishKeyList.PublishOptions.X.Field[1].Var = "pubsub#acces_model"
|
|
|
|
|
iqOxPublishKeyList.PublishOptions.X.Field[1].Value = "open"
|
|
|
|
|
|
|
|
|
|
opkl, err := xml.Marshal(iqOxPublishKeyList)
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
iqReply, err = sendIQ(client, jid, "set", string(opkl))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if fingerprint == "none" {
|
|
|
|
|
return nil, errors.New("server didn't provide public key fingerprints for " + recipient)
|
|
|
|
|
if iqReply.Type != "result" {
|
|
|
|
|
return nil, errors.New("Error while publishing public key list.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return key, 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"
|
|
|
|
@ -93,6 +199,56 @@ func oxGetPublicKey(client *xmpp.Client, recipient string) (*crypto.Key, error)
|
|
|
|
|
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, "1900-01-01T06:06:06Z")
|
|
|
|
|
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, recipient string, message string) (string, error) {
|
|
|
|
|
var oxCryptMessage OxCryptElement
|
|
|
|
|
var oxMessage OxMessageElement
|
|
|
|
|