Commit d41b4df7 authored by zauberstuhl's avatar zauberstuhl
Browse files

Clean up EncryptedMagicEnvelope

parent 43eb17fb
...@@ -19,10 +19,10 @@ package federation ...@@ -19,10 +19,10 @@ package federation
import ( import (
"bytes" "bytes"
"io"
"encoding/base64" "encoding/base64"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"io"
"crypto/rand" "crypto/rand"
) )
...@@ -63,15 +63,9 @@ func (a *Aes) Encrypt(data []byte) error { ...@@ -63,15 +63,9 @@ func (a *Aes) Encrypt(data []byte) error {
// CBC mode works on blocks so plaintexts may need to be padded to the // CBC mode works on blocks so plaintexts may need to be padded to the
// next whole block. For an example of such padding, see // next whole block. For an example of such padding, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. // https://tools.ietf.org/html/rfc5246#section-6.2.3.2.
if len(data)%aes.BlockSize != 0 { padding := aes.BlockSize - len(data)%aes.BlockSize
paddingLen := aes.BlockSize - (len(data)%aes.BlockSize) padtext := bytes.Repeat([]byte{byte(padding)}, padding)
paddingText := bytes.Repeat([]byte{byte(paddingLen)}, paddingLen) data = append(data, padtext...)
//for i := 0; i < paddingLen; i++ {
// data = append(data, 0x20)
//}
data = append(data, paddingText...)
}
key, err := base64.StdEncoding.DecodeString(a.Key) key, err := base64.StdEncoding.DecodeString(a.Key)
if err != nil { if err != nil {
...@@ -91,7 +85,7 @@ func (a *Aes) Encrypt(data []byte) error { ...@@ -91,7 +85,7 @@ func (a *Aes) Encrypt(data []byte) error {
} }
mode := cipher.NewCBCEncrypter(block, iv) mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[:], data) mode.CryptBlocks(ciphertext, data)
a.Data = base64.StdEncoding.EncodeToString(ciphertext) a.Data = base64.StdEncoding.EncodeToString(ciphertext)
return nil return nil
......
...@@ -100,6 +100,8 @@ func (request *DiasporaUnmarshal) VerifySignature(serialized []byte) error { ...@@ -100,6 +100,8 @@ func (request *DiasporaUnmarshal) VerifySignature(serialized []byte) error {
} }
func AuthorSignature(data interface{}, privKey string) (string, error) { func AuthorSignature(data interface{}, privKey string) (string, error) {
// XXX the order should be tracked in the database
// otherwise this can break stuff very quickly
var text string var text string
switch entity := data.(type) { switch entity := data.(type) {
case *EntityComment: case *EntityComment:
...@@ -113,24 +115,26 @@ func AuthorSignature(data interface{}, privKey string) (string, error) { ...@@ -113,24 +115,26 @@ func AuthorSignature(data interface{}, privKey string) (string, error) {
// positive guid parent_guid parent_type author // positive guid parent_guid parent_type author
text = positive+";"+entity.Guid+";"+entity.ParentGuid+ text = positive+";"+entity.Guid+";"+entity.ParentGuid+
";"+entity.TargetType+";"+entity.DiasporaHandle ";"+entity.TargetType+";"+entity.DiasporaHandle
default:
panic("Not supported interface type for AuthorSignature!")
} }
return Sign(text, privKey) return Sign(text, privKey)
} }
func (d *PrivateEnvMarshal) Sign(privKey string) (err error) { func (envelope *MagicEnvelopeMarshal) Sign(privKey string) (err error) {
type64 := base64.StdEncoding.EncodeToString( type64 := base64.StdEncoding.EncodeToString(
[]byte(d.Data.Type), []byte(envelope.Data.Type),
) )
encoding64 := base64.StdEncoding.EncodeToString( encoding64 := base64.StdEncoding.EncodeToString(
[]byte(d.Encoding), []byte(envelope.Encoding),
) )
alg64 := base64.StdEncoding.EncodeToString( alg64 := base64.StdEncoding.EncodeToString(
[]byte(d.Alg), []byte(envelope.Alg),
) )
text := d.Data.Data + "." + type64 + text := envelope.Data.Data + "." + type64 +
"." + encoding64 + "." + alg64 "." + encoding64 + "." + alg64
(*d).Sig.Sig, err = Sign(text, privKey) (*envelope).Sig.Sig, err = Sign(text, privKey)
return return
} }
......
...@@ -19,88 +19,6 @@ package federation ...@@ -19,88 +19,6 @@ package federation
import "encoding/xml" import "encoding/xml"
/* Header
<?xml version="1.0" encoding="UTF-8"?>
<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
<header>
<author_id>dia@192.168.0.173:3000</author_id>
</header>
*/
type Header struct {
XMLName xml.Name `xml:"header"`
AuthorId string `xml:"author_id"`
}
/* Encrypted Header
<?xml version="1.0" encoding="UTF-8"?>
<decrypted_header>
<iv>...</iv>
<aes_key>...</aes_key>
<author_id>one@two.tld</author_id>
</decrypted_header>
*/
// XXX legacy?
type XmlDecryptedHeader struct {
XMLName xml.Name `xml:"decrypted_header"`
Iv string `xml:"iv"`
AesKey string `xml:"aes_key"`
AuthorId string `xml:"author_id"`
}
type JsonEnvHeader struct {
AesKey string `json:"aes_key"`
Ciphertext string `json:"ciphertext"`
}
// Private Request
//
// <?xml version="1.0" encoding="UTF-8"?>
// <diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
// <encrypted_header>eyJhZXNfa2V5IjoiVTFGbVZ5TE5CT0pyOWViZVpiMUxnSGQzMlJwMzNMa1ViZVRnY3BEaDluZHAyT2cyMUZNeHh2Mm9RUXp0eWxLVjZsOEkzR0wvQlEzcEQxbGNYbjFGQWlEVTBmTHJwRUZwUEJNc1AwT3MvdmhEZ3I3MDJvaWNkMUxFN0ZMZ3ZVc1VKRGxGOHdvTVZUaGtnTmNVWGlCNUZpajcvb1J4ZFZ1QlJxbVpCQ0VXLys4PSIsImNpcGhlcnRleHQiOiJveWhWVjR5bXJoTGxYRVU1WVcxQWdpK29sTkxRSDlvR2NPQnVBOG00a25FZ09GZnJoNzEwUi9XQloyQ1I3WVlMeEhuOEcvUWFyU1UraDFka0VHMGFhcS9FQ1RWQjFHOXNkT2lQMnZSRTRkY1JOclNqb3RINXBsV3F4QVBvNlBpRkdwNUJZcURoYnB2RUNYNFNpM3BTN2hyOTdyWUs3NTFyYnYvRjNwRERJNG13d2NoUFA3S21nN21yVXBCZTBEeXRqQk5xK0k3S2lxSVQrRmVTUUMxc0pQSmd4TVAxck1VcDB6cURpT2ZlR3U2RGVnVG5MRnd5WHM3WEhGQW1FN29iSlZJY2NxS3czNjNWcnBrWXE0N1BDRWc3R3p1bFVlOXA5eFhDN3dVRmNOak91RTVQV1NXU1h3cWM0K2EybHFuOCJ9</encrypted_header>
// <me:env>
// <me:data type="application/xml">WXRPc09JakFleHh0dW5rQytRallnSEg0a08zOGd3ZVQyVVg4NkppVVZzVlJlMFFHbExhSjBtbzBnRUVGSnQyc2pqRG5OOUFETk1nakJ6MThGVnJsRWNEcjBqUDJ2emZPRjlCVHVLNXlvRjZqQ3NIRlh5USt4L3RlMnUxN0xCSUlxSDk4OGNhL1BVLzE2NEhBL0plQU1sMEZCOFBnTTFzYW9qUUJaV3V1RW95Y25ONWVZTm9wRi9adElXU3AxOFVzL05RWG13cktiWVVYYXJpNitOSWo1RWVOdTJrYnJnN2FoWUdhRFJKMVFtcEdhL0pMNmVvMEM1ZHFkS2s1amgrNFU0dnA0ZVY5bkRPK0ZGSkhpd3pDWm84NWJkdUR4T0NRRURpa2QxeGE1OWl4NkZWMTYzQ0MvMXBqdmhTa3cvTjNOTGdHOUZjWExQZDRwdTYzQ2pkS1E4QlE2MTYrMEFGREV0bjF0RldqYU9DV0VXOW5ORzQrTmNVZ0ZKamM0dzZxU3Ruc00rSFdzS0hOTnpRZElRc1pXT3c2bzJUK3ZCOUZmeUY3T3NTNHhwYnF0OW5MZFM1Z0U1M1lFb3FXVmRzK3E1a2xYV3dXdjlBeHRGdlhIVWxyNEE9PQ==</me:data>
// <me:encoding>base64url</me:encoding>
// <me:alg>RSA-SHA256</me:alg>
// <me:sig>sJPN--TJ9IqVwhT_j9mNGrLF4yQvwXUQKo24cPLi5FVXl-tVpyEOxrUI1gwRJ5j5UkkqNJO8mLph2ravlxqt7PNhS9YAOTuo46nXWXyOJjP_ESxq3DaMrYqQt57PDnM29x5yQ0QATbSAs6XneHtxmVKzwKgi4ZpdQ3THFj_iWwac0BI3Or1okt9wLxxl3LTLO9vwfIZaeo-XDNT7JlIfSMZDv1xipjtbl-P0z0q4u2wYOLquvvDjRdI_9vStZK3EOmYARhDXhH0vcJNjVXYTuq16BtXsyfEW3WLBPH67t9Ef3c6cWqU3qPSS3-ddZY5VVq6pPpmtnHuBNzB5hZvZ8asMexc7S0V075ZG-7axUcwXkWKTwCZuxwZNm3VinQze4meWY6vWITtD6zHCguMIWZgxW5z7LGZ04j8_26NbBmZXV52-TFRJExi6H6kUmDb3GrYTlLTOziEUB9pl4NkCX_ghi-Pixbzg7zc_LD_cKXoUyj7iHRNfdfXLck9SOxXkmWAI7hNAswBgx1ngnH6AVyNSsFYVNF1jzc-uTwANfMQqjqq5_XCdE2Z2GFOvFJQ6JK4S-gAEpLygzeltbvYxuK_qqONA9cCTqoJlOxSgQY_lj7h6s__1EuyP9_-Fh4J9MWi9i118ndkmzaswOcxU_VfnHLDgbQbXD5B7zaS1c90=</me:sig>
// </me:env>
// </diaspora>
type PrivateEnvMarshal struct {
XMLName xml.Name `xml:"me:env"`
Me string `xml:"xmlns:me,attr"`
Data struct {
XMLName xml.Name `xml:"me:data"`
Type string `xml:"type,attr"`
Data string `xml:",chardata"`
}
Encoding string `xml:"me:encoding"`
Alg string `xml:"me:alg"`
Sig struct {
XMLName xml.Name `xml:"me:sig"`
Sig string `xml:",chardata"`
KeyId string `xml:"key_id,attr,omitempty"`
}
}
type PrivateMarshal struct {
XMLName xml.Name `xml:"diaspora"`
Xmlns string `xml:"xmlns,attr"`
XmlnsMe string `xml:"me,attr"`
EncryptedHeader string `xml:"encrypted_header,omitempty"`
Env PrivateEnvMarshal
}
type PublicMarshal struct {
XMLName xml.Name `xml:"diaspora"`
Xmlns string `xml:"xmlns,attr"`
XmlnsMe string `xml:"me,attr"`
Header Header
Env PrivateEnvMarshal
}
// NOTE I had huge problems with marshal // NOTE I had huge problems with marshal
// and unmarshal with the same structs // and unmarshal with the same structs
// apperently namespaces in tags are a problem // apperently namespaces in tags are a problem
......
...@@ -25,67 +25,37 @@ import( ...@@ -25,67 +25,37 @@ import(
"crypto/rand" "crypto/rand"
) )
func (request *PrivateMarshal) EncryptHeader(authorId string, serializedPubKey []byte) (err error) { /* Header
// Generate the AES key before you start
// encrypting the plain header <?xml version="1.0" encoding="UTF-8"?>
var aesKeySet Aes <diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
err = aesKeySet.Generate() <header>
if err != nil { <author_id>dia@192.168.0.173:3000</author_id>
return </header>
} */
// The actual header type Header struct {
// XMLName xml.Name `xml:"header"`
// <decrypted_header> AuthorId string `xml:"author_id"`
// <iv>...</iv> }
// <aes_key>...</aes_key> /* Decrypted Header
// <author_id>one@two.tld</author_id>
// </decrypted_header> <?xml version="1.0" encoding="UTF-8"?>
decryptedHeader := XmlDecryptedHeader{ <decrypted_header>
Iv: aesKeySet.Iv, <iv>...</iv>
AesKey: aesKeySet.Key, <aes_key>...</aes_key>
AuthorId: authorId, <author_id>one@two.tld</author_id>
} </decrypted_header>
*/
decryptedHeaderXml, err := json.Marshal(decryptedHeader) type XmlDecryptedHeader struct {
if err != nil { XMLName xml.Name `xml:"decrypted_header"`
return err Iv string `xml:"iv"`
} AesKey string `xml:"aes_key"`
AuthorId string `xml:"author_id"`
err = aesKeySet.Encrypt(decryptedHeaderXml) }
if err != nil {
return
}
aesKeySetXml, err := json.Marshal(aesKeySet)
if err != nil {
return err
}
pubKey, err := ParseRSAPubKey(serializedPubKey)
if err != nil {
return err
}
// aes_key
aesKey, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, aesKeySetXml)
if err != nil {
return err
}
aesKeyEncoded := base64.StdEncoding.EncodeToString(aesKey)
header := JsonEnvHeader{
AesKey: aesKeyEncoded,
Ciphertext: aesKeySet.Data,
}
headerXml, err := json.Marshal(header)
if err != nil {
return err
}
(*request).EncryptedHeader = base64.StdEncoding.EncodeToString(headerXml) type JsonEnvHeader struct {
return AesKey string `json:"aes_key"`
Ciphertext string `json:"ciphertext"`
} }
func (request *DiasporaUnmarshal) DecryptHeader(serialized []byte) error { func (request *DiasporaUnmarshal) DecryptHeader(serialized []byte) error {
......
...@@ -30,36 +30,38 @@ import ( ...@@ -30,36 +30,38 @@ import (
const ( const (
PROTO_HTTP = "http://" PROTO_HTTP = "http://"
PROTO_HTTPS = "https://" PROTO_HTTPS = "https://"
CONTENT_TYPE_ENVELOPE = "application/magic-envelope+xml"
CONTENT_TYPE_JSON = "application/json"
CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"
) )
var timeout = time.Duration(10 * time.Second) var timeout = time.Duration(10 * time.Second)
func PushXmlToPrivate(host, guid string, body io.Reader) error { func PushJsonToPrivate(host, guid string, body io.Reader) error {
return pushXml(host, "/receive/users/" + guid, PROTO_HTTPS, body) return push(host, "/receive/users/" + guid, PROTO_HTTPS, CONTENT_TYPE_JSON, body)
} }
func PushXmlToPublic(host string, body io.Reader) error { func PushXmlToPublic(host string, body io.Reader) error {
return pushXml(host, "/receive/public", PROTO_HTTPS, body) return push(host, "/receive/public", PROTO_HTTPS, CONTENT_TYPE_ENVELOPE, body)
} }
func pushXml(host, endpoint, proto string, body io.Reader) error { func PushFormToPrivate(host, guid string, body io.Reader) error {
return push(host, "/receive/users/" + guid, PROTO_HTTPS, CONTENT_TYPE_FORM, body)
}
func push(host, endpoint, proto, contentType string, body io.Reader) error {
req, err := http.NewRequest("POST", proto + host + endpoint, body) req, err := http.NewRequest("POST", proto + host + endpoint, body)
if err != nil { if err != nil {
return err return err
} }
req.Header.Add("Content-Type", contentType)
if strings.Contains(endpoint, "public") {
req.Header.Add("Content-Type", "application/magic-envelope+xml")
} else {
req.Header.Add("Content-Type", "application/json")
}
client := &http.Client{Timeout: timeout} client := &http.Client{Timeout: timeout}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
if proto == PROTO_HTTPS { if proto == PROTO_HTTPS {
info("Retry with", PROTO_HTTP, "on", host, err) info("Retry with", PROTO_HTTP, "on", host, err)
return pushXml(host, endpoint, PROTO_HTTP, body) return push(host, endpoint, PROTO_HTTP, contentType, body)
} }
return err return err
} }
......
...@@ -20,13 +20,28 @@ package federation ...@@ -20,13 +20,28 @@ package federation
import ( import (
"encoding/base64" "encoding/base64"
"encoding/xml" "encoding/xml"
//XXX
"encoding/json" "encoding/json"
"crypto/rsa" "crypto/rsa"
"crypto/rand" "crypto/rand"
) )
type MagicEnvelopeMarshal struct {
XMLName xml.Name `xml:"me:env"`
Me string `xml:"xmlns:me,attr"`
Data struct {
XMLName xml.Name `xml:"me:data"`
Type string `xml:"type,attr"`
Data string `xml:",chardata"`
}
Encoding string `xml:"me:encoding"`
Alg string `xml:"me:alg"`
Sig struct {
XMLName xml.Name `xml:"me:sig"`
Sig string `xml:",chardata"`
KeyId string `xml:"key_id,attr,omitempty"`
}
}
func MagicEnvelope(privkey, handle string, plainXml []byte) (payload []byte, err error) { func MagicEnvelope(privkey, handle string, plainXml []byte) (payload []byte, err error) {
info("plain xml", string(plainXml)) info("plain xml", string(plainXml))
info("privkey length", len(privkey)) info("privkey length", len(privkey))
...@@ -35,7 +50,7 @@ func MagicEnvelope(privkey, handle string, plainXml []byte) (payload []byte, err ...@@ -35,7 +50,7 @@ func MagicEnvelope(privkey, handle string, plainXml []byte) (payload []byte, err
data := base64.URLEncoding.EncodeToString(plainXml) data := base64.URLEncoding.EncodeToString(plainXml)
keyId := base64.URLEncoding.EncodeToString([]byte(handle)) keyId := base64.URLEncoding.EncodeToString([]byte(handle))
xmlBody := PrivateEnvMarshal{} xmlBody := MagicEnvelopeMarshal{}
xmlBody.Data.Type = APPLICATION_XML xmlBody.Data.Type = APPLICATION_XML
xmlBody.Data.Data = data xmlBody.Data.Data = data
xmlBody.Me = XMLNS_ME xmlBody.Me = XMLNS_ME
...@@ -59,6 +74,9 @@ func MagicEnvelope(privkey, handle string, plainXml []byte) (payload []byte, err ...@@ -59,6 +74,9 @@ func MagicEnvelope(privkey, handle string, plainXml []byte) (payload []byte, err
} }
func EncryptedMagicEnvelope(privkey, pubkey, handle string, serializedXml []byte) (payload []byte, err error) { func EncryptedMagicEnvelope(privkey, pubkey, handle string, serializedXml []byte) (payload []byte, err error) {
var aesKeySet Aes
var aesWrapper AesWrapper
info("serialized xml", string(serializedXml)) info("serialized xml", string(serializedXml))
info("privkey length", len(privkey)) info("privkey length", len(privkey))
info("pubkey length", len(pubkey)) info("pubkey length", len(pubkey))
...@@ -67,58 +85,46 @@ func EncryptedMagicEnvelope(privkey, pubkey, handle string, serializedXml []byte ...@@ -67,58 +85,46 @@ func EncryptedMagicEnvelope(privkey, pubkey, handle string, serializedXml []byte
data := base64.URLEncoding.EncodeToString(serializedXml) data := base64.URLEncoding.EncodeToString(serializedXml)
keyId := base64.URLEncoding.EncodeToString([]byte(handle)) keyId := base64.URLEncoding.EncodeToString([]byte(handle))
// encrypted header envelope := MagicEnvelopeMarshal{
//xmlBody := PrivateMarshal{
// Xmlns: XMLNS,
// XmlnsMe: XMLNS_ME,
xmlBodyEnv := PrivateEnvMarshal{
Me: XMLNS_ME, Me: XMLNS_ME,
Encoding: BASE64_URL, Encoding: BASE64_URL,
Alg: RSA_SHA256, Alg: RSA_SHA256,
} }
//} envelope.Data.Type = APPLICATION_XML
xmlBodyEnv.Data.Type = APPLICATION_XML envelope.Data.Data = data
xmlBodyEnv.Data.Data = data envelope.Sig.KeyId = keyId
xmlBodyEnv.Sig.KeyId = keyId
err = xmlBodyEnv.Sign(privkey) err = envelope.Sign(privkey)
if err != nil { if err != nil {
warn(err) warn(err)
return return
} }
// XXX NOTE move below to header file
// Generate a new AES key pair // Generate a new AES key pair
var (
aesKeySet Aes
aesWrapper AesWrapper
)
err = aesKeySet.Generate() err = aesKeySet.Generate()
if err != nil { if err != nil {
warn(err) warn(err)
return return
} }
// payload mit aes versch und base64 // payload with aes encryption
payload, err = xml.Marshal(xmlBodyEnv) payload, err = xml.Marshal(envelope)
if err != nil { if err != nil {
warn(err) warn(err)
return return
} }
info("payload, err = xml.Marshal(envelope) ", string(payload))
err = aesKeySet.Encrypt(payload) err = aesKeySet.Encrypt(payload)
if err != nil { if err != nil {
warn(err) warn(err)
return return
} }
//aesWrapper.MagicEnvelope = base64.StdEncoding.EncodeToString([]byte(aesKeySet.Data)) //aesWrapper.MagicEnvelope = base64.StdEncoding.EncodeToString([]byte(aesKeySet.Data))
aesWrapper.MagicEnvelope = aesKeySet.Data aesWrapper.MagicEnvelope = aesKeySet.Data
// aes mit pub key versch // aes with rsa encryption
aesKeySetXml, err := json.Marshal(aesKeySet) aesKeySetXml, err := json.Marshal(aesKeySet)
if err != nil { if err != nil {
warn(err) warn(err)
...@@ -133,23 +139,19 @@ func EncryptedMagicEnvelope(privkey, pubkey, handle string, serializedXml []byte ...@@ -133,23 +139,19 @@ func EncryptedMagicEnvelope(privkey, pubkey, handle string, serializedXml []byte
info("aesKeySetXml", string(aesKeySetXml)) info("aesKeySetXml", string(aesKeySetXml))
// aes_key
aesKey, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, aesKeySetXml) aesKey, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, aesKeySetXml)
if err != nil { if err != nil {
warn(err) warn(err)
return return
} }
aesWrapper.AesKey = base64.StdEncoding.EncodeToString(aesKey) aesWrapper.AesKey = base64.StdEncoding.EncodeToString(aesKey)
payload, err = json.Marshal(aesWrapper) payload, err = json.Marshal(aesWrapper)
if err != nil { if err != nil {
warn(err) warn(err)
return return
} }
info("payload", string(payload)) info("payload", string(payload))
return return
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment