129 lines
3.1 KiB
Go
129 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
func reconstructMessage(message pigeonMessage) (string, string) {
|
|
return "top_half", "bottom_half"
|
|
}
|
|
|
|
// Every body entry is a key value pair. Keys and values are separated by a : character (no spaces).
|
|
// A key must be 1-90 characters in length.
|
|
// A key cannot contain whitespace or control characters
|
|
// A key may contain any of the following characters:
|
|
// alphanumeric characters (a-z, A-Z, 0-9, -, _)
|
|
// A value may be a:
|
|
// A string (128 characters or less)
|
|
// A multihash referencing an identity (USER.), a message (TEXT.) or a blob (FILE.).
|
|
|
|
const keyRegex = `^[A-Z|a-z|\-|\_|\.|0-9]{1,90}$`
|
|
const valueRegex = `^(NONE)|(\"[\x20-\x7E]{1,126}\")|(FILE\.[A-Z|0-9]{52})|(USER\.[A-Z|0-9]{52})|(TEXT\.[A-Z|0-9]{52})$`
|
|
const kindRegex = `^[\x20-\x7E]{1,90}$`
|
|
|
|
func validateKind(kind *string) error {
|
|
matched, err := regexp.MatchString(kindRegex, *kind)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !matched {
|
|
errMessage := fmt.Sprintf("%s does not match %s", *kind, kindRegex)
|
|
return errors.New(errMessage)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateBodyKey(key *string) error {
|
|
matched, err := regexp.MatchString(keyRegex, *key)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !matched {
|
|
errMessage := fmt.Sprintf("%s does not match %s", *key, keyRegex)
|
|
return errors.New(errMessage)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateBodyValue(value *string) error {
|
|
matched, err := regexp.MatchString(valueRegex, *value)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !matched {
|
|
errMessage := fmt.Sprintf("%s does not match %s", *value, valueRegex)
|
|
return errors.New(errMessage)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateSignature(message *pigeonMessage, topHalf string) error {
|
|
asciiSignature := message.signature
|
|
signature := []byte(B32Decode(asciiSignature))
|
|
publicKey := decodeMhash(message.author)
|
|
ok := ed25519.Verify(publicKey, []byte(topHalf), signature)
|
|
|
|
if ok {
|
|
return nil
|
|
}
|
|
|
|
error := fmt.Sprintf("Can't verify message %s", message.signature)
|
|
return errors.New(error)
|
|
}
|
|
|
|
func verifyBodyItem(bodyItem *pigeonBodyItem) error {
|
|
keyError := validateBodyKey(&bodyItem.key)
|
|
if keyError != nil {
|
|
return keyError
|
|
}
|
|
|
|
valueError := validateBodyValue(&bodyItem.value)
|
|
if valueError != nil {
|
|
return valueError
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Verify format and signature of a message, but do NOT cross-check its header
|
|
// fields.
|
|
func verifyShallow(message *pigeonMessage) error {
|
|
var buffer strings.Builder
|
|
buffer.Write([]byte(fmt.Sprintf("author %s\n", message.author)))
|
|
buffer.Write([]byte(fmt.Sprintf("depth %d\n", message.depth)))
|
|
err := validateKind(&message.kind)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
buffer.Write([]byte(fmt.Sprintf("kind %s\n", message.kind)))
|
|
buffer.Write([]byte(fmt.Sprintf("prev %s\n", message.prev)))
|
|
buffer.Write([]byte("\n"))
|
|
for count, bodyItem := range message.body {
|
|
if count > 128 {
|
|
return errors.New("A message may not exceed 128 key/value pairs %s")
|
|
}
|
|
err := verifyBodyItem(&bodyItem)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
line := fmt.Sprintf("%s:%s\n", bodyItem.key, bodyItem.value)
|
|
buffer.Write([]byte(line))
|
|
}
|
|
|
|
buffer.Write([]byte("\n"))
|
|
validateSignature(message, buffer.String())
|
|
return nil
|
|
}
|