Commit ff2a4a38 authored by zauberstuhl's avatar zauberstuhl

Implement XML sorting and dynamic author signatures

parent d41b4df7
......@@ -26,6 +26,8 @@ import (
"encoding/base64"
"encoding/pem"
"errors"
"reflect"
"strings"
)
func ParseBase64RSAPubKey(encodedKey string) (pubkey *rsa.PublicKey, err error) {
......@@ -99,25 +101,39 @@ func (request *DiasporaUnmarshal) VerifySignature(serialized []byte) error {
return rsa.VerifyPKCS1v15(pubkey, crypto.SHA256, digest, ds)
}
func AuthorSignature(data interface{}, privKey string) (string, error) {
// XXX the order should be tracked in the database
// otherwise this can break stuff very quickly
func AuthorSignature(data interface{}, order, privKey string) (string, error) {
var text string
switch entity := data.(type) {
case *EntityComment:
text = entity.DiasporaHandle+";"+entity.Guid+";"+
entity.ParentGuid+";"+entity.Text
case *EntityLike:
var r = reflect.TypeOf(data)
var v = reflect.ValueOf(data)
for _, o := range strings.Split(order, " ") {
for i := 0; i < r.NumField(); i++ {
tagList := strings.Split(r.Field(i).Tag.Get("xml"), ",")
if len(tagList) <= 0 {
panic("xml struct always requires an xml tag for signatures")
}
tag := tagList[0] // the first element is always the xml name
if tag == o {
value := v.Field(i).Interface()
switch v := value.(type) {
case string:
text += v + ";"
case bool:
positive := "false"
if entity.Positive {
if v {
positive = "true"
}
// positive guid parent_guid parent_type author
text = positive+";"+entity.Guid+";"+entity.ParentGuid+
";"+entity.TargetType+";"+entity.DiasporaHandle
text += positive + ";"
default:
panic("Not supported interface type for AuthorSignature!")
fatal("Unknown type in AuthorSignature that will break federation!")
}
}
}
}
// trim last semicolon
text = text[:len(text)-1]
return Sign(text, privKey)
}
......@@ -154,8 +170,6 @@ func Sign(text, privKey string) (sig string, err error) {
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(sigInByte), nil
}
......@@ -55,7 +55,7 @@ type DiasporaUnmarshal struct {
type Entity struct {
XMLName xml.Name `xml:"XML"`
Post EntityPost `xml:"post"`
RequestBody []byte `xml:"-"` // original body payload
SignatureOrder string `xml:"-"`
}
type EntityPost struct {
......
......@@ -19,9 +19,104 @@ package federation
import (
"regexp"
"strings"
"errors"
)
// This is a workaround for sorting xml elements. Diaspora requires
// a specific order otherwise the signature check will fail and
// ignore the entity. This should be a TODO since we could implement
// this kind of logic in a custom xml marshaller
func SortByEntityOrder(order string, entity []byte) (sorted []byte) {
// remove all newline character
entity = []byte(strings.Replace(string(entity), "\n", "", -1))
entity = []byte(strings.Replace(string(entity), "\r", "", -1))
var lines []string
var linesOffset int
var closingTag bool
var entityLen = len(entity)
for index, c := range entity {
offset := index + 1
// abort on last character
if offset >= entityLen {
lines = append(lines, string(entity[linesOffset:]))
break
}
// check on "><" open xml tags
if c == 0x003e && entity[offset] == 0x003c {
lines = append(lines, string(entity[linesOffset:offset]))
linesOffset = offset
}
// set the closing tag to true if "/" occurs
if c == 0x002f {
closingTag = true
}
// append the whole xml element after ">" if "/" is true
if c == 0x003e && closingTag {
lines = append(lines, string(entity[linesOffset:offset]))
linesOffset = offset
closingTag = false
}
}
var start bool = true
var orderArr = strings.Split(order, " ")
var sortedXmlElements string
// sort the elements in the prefered order
for _, o := range orderArr {
re := regexp.MustCompile("<"+o+">(.+?)</"+o+">")
elements := re.FindAllStringSubmatch(string(entity), 1)
sortedXmlElements += elements[0][0]
}
// replace only the elements we have to sort
// with the new sortedXmlElements
for _, line := range lines {
var orderMatch bool = false
for _, o := range orderArr {
re := regexp.MustCompile("<"+o+">(.+?)</"+o+">")
if re.Find([]byte(line)) != nil {
orderMatch = true
break
}
}
if !orderMatch {
sorted = append(sorted, []byte(line)...)
} else {
if start {
sorted = append(sorted, []byte(sortedXmlElements)...)
}
start = false
}
}
return
}
func ExtractEntityOrder(entity string) string {
var order []string
re := regexp.MustCompile("<([^/]+?)>")
elements := re.FindAllStringSubmatch(entity, -1)
for index, e := range elements {
if index <= 2 {
// skip XML, post and entity name field
continue
}
signature := regexp.MustCompile("author_signature")
if signature.Find([]byte(e[1])) != nil {
// author_signature and parent_author_signature
// are not required for a valid order string
continue
}
order = append(order, e[1])
}
return strings.Join(order, " ")
}
func ParseDiasporaHandle(handle string) (string, string, error) {
parts, err := parseStringHelper(handle, `^(.+?)@(.+?)$`, 2)
if err != nil {
......
......@@ -28,7 +28,6 @@ func PreparePublicRequest(body string) (request DiasporaUnmarshal, err error) {
warn(err)
return
}
info("public request", request.Env.Data)
return
}
......@@ -59,7 +58,6 @@ func PreparePrivateRequest(body string, privkey []byte) (request DiasporaUnmarsh
warn(err)
return
}
info("private request to", request.Env.Data)
return
}
......@@ -84,6 +82,10 @@ func (request *DiasporaUnmarshal) ParsePrivate(pubkey []byte) (entity Entity, er
}
func _parse(payload []byte) (entity Entity, err error) {
info("received payload", string(payload))
entity.SignatureOrder = ExtractEntityOrder(string(payload))
err = xml.Unmarshal(payload, &entity)
if err != nil {
warn(err)
......
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