Skip to content

Commit

Permalink
Clean message format and improve validation a bit.
Browse files Browse the repository at this point in the history
  • Loading branch information
bahner committed Feb 28, 2024
1 parent b507188 commit cc5294c
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 70 deletions.
20 changes: 5 additions & 15 deletions ma.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package ma

import "time"

const (

// Just our name
Expand All @@ -16,27 +14,19 @@ const (
// The rendezvous string used for peer discovery in libp2p.
RENDEZVOUS = "/" + NAME + "/" + VERSION

// The topic prefix used in pubsub. It is the same as the rendezvous string.
// But we keep it separate for future flexibility.
TOPIC_PREFIX = RENDEZVOUS

// BLAKE3 label for symmetric key generation.
HASH_ALGORITHM_MULTICODEC_STRING = "blake3"
BLAKE3_LABEL = NAME
BLAKE3_SUM_SIZE = 32 // 256 bits

// MIME types

// A MIME type for a message. Just to implement it for future proofing.
MESSAGE_MIME_TYPE = "application/x-ma-message; version=" + VERSION
ENVELOPE_MIME_TYPE = "application/x-ma-envelope; version=" + VERSION
// Message constants
MESSAGE_TYPE = "/ma/message/" + VERSION
ENVELOPE_MESSAGE_TYPE = "/ma/message/envelope/" + VERSION
BROADCAST_MESSAGE_TYPE = "/ma/message/broadcast/" + VERSION

// Broadcasts
BROADCAST_MIME_TYPE = "application/x-ma-broadcast; version=" + VERSION
BROADCAST_TOPIC = "/" + NAME + "/broadcast/" + VERSION
BROADCAST_TOPIC = BROADCAST_MESSAGE_TYPE

MESSAGE_DEFAULT_CONTENT_TYPE = "text/plain"
MESSAGE_DEFAULT_TTL = time.Hour * 24

// API
DEFAULT_IPFS_API_MULTIADDR = "/ip4/127.0.0.1/tcp/45005" // Default to Brave browser, Kubo is /ip4/127.0.0.1/tcp/5001
Expand Down
10 changes: 4 additions & 6 deletions msg/broadcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ func NewBroadcast(

m := &Message{
// Message meta data
ID: id,
MimeType: ma.BROADCAST_MIME_TYPE,
Version: ma.VERSION,
Id: id,
Type: ma.BROADCAST_MESSAGE_TYPE,
// Recipients
From: from,
// Body
ContentType: contentType,
// The content is not signed as such, but the hash is.
Content: content,
Content: content,
}

err = m.Sign(priv_key)
Expand Down Expand Up @@ -72,7 +70,7 @@ func (m *Message) Broadcast(ctx context.Context, t *pubsub.Topic) error {
}

func (m *Message) verifyBroadcast(t *pubsub.Topic) error {
if m.MimeType != ma.BROADCAST_MIME_TYPE {
if m.Type != ma.BROADCAST_MESSAGE_TYPE {
return ErrMessageInvalidType
}

Expand Down
3 changes: 2 additions & 1 deletion msg/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import (
var (
ErrBroadcastHasRecipient = errors.New("broadcast message must not have a recipient")
ErrBroadcastInvalidTopic = fmt.Errorf("broadcast topic must be %s", ma.BROADCAST_TOPIC)
ErrBroadcastInvalidType = fmt.Errorf("broadcast message must not %s", ma.BROADCAST_MIME_TYPE)
ErrBroadcastInvalidType = fmt.Errorf("broadcast message must not %s", ma.BROADCAST_MESSAGE_TYPE)
ErrEmptyID = errors.New("id must be non-empty")
ErrInvalidID = errors.New("invalid message id")
ErrFetchDoc = errors.New("failed to fetch entity document")
ErrMessageInvalidType = errors.New("invalid Message type")
ErrInvalidSender = errors.New("invalid sender")
ErrInvalidRecipient = errors.New("invalid recipient")
ErrMissingContentType = errors.New("empty ContentType")
ErrMissingContent = errors.New("empty ContentType")
ErrMissingFrom = errors.New("mmissing From sender")
ErrMissinSignature = errors.New("mmissing signature")
ErrNilMessage = errors.New("nil Message provided")
Expand Down
16 changes: 4 additions & 12 deletions msg/headers.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package msg

import (
semver "github.com/blang/semver/v4"
cbor "github.com/fxamacker/cbor/v2"
)

Expand All @@ -10,11 +9,9 @@ import (
// NB! Content is *not* a part of the headers
type Headers struct {
// Version of the message format
Version string
// Unique identifier of the message
ID string `cbor:"id"`
Id string `cbor:"id"`
// MIME type of the message
MimeType string `cbor:"mimeType"`
Type string `cbor:"type"`
// Sender of the message
From string `cbor:"from"`
// Recipient of the message
Expand All @@ -29,9 +26,8 @@ func (m *Message) unsignedHeaders() Headers {

return Headers{
// Message Headers
ID: m.ID,
MimeType: m.MimeType,
Version: m.Version,
Id: m.Id,
Type: m.Type,
From: m.From,
To: m.To,
ContentType: m.ContentType,
Expand All @@ -54,7 +50,3 @@ func (m *Message) Headers() Headers {
func (m *Message) marshalHeadersToCBOR() ([]byte, error) {
return cbor.Marshal(m.Headers())
}

func (h *Headers) semVersion() (semver.Version, error) {
return semver.Make(h.Version)
}
33 changes: 14 additions & 19 deletions msg/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,13 @@ import (
nanoid "github.com/matoous/go-nanoid/v2"
)

const (

// Messages which are older than a day should be ignored
MESSAGE_TTL = ma.MESSAGE_DEFAULT_TTL
)

// This struct mimicks the Message format, but it's *not* Message.
// It should enable using Message later, if that's a good idea.
type Message struct {
// Version of the message format
Version string `cbor:"version"`
// Unique identifier of the message
ID string `cbor:"id"`
Id string `cbor:"id"`
// MIME type of the message
MimeType string `cbor:"mimeType"`
Type string `cbor:"mimeType"`
// Sender of the message
From string `cbor:"from"`
// Recipient of the message
Expand Down Expand Up @@ -54,16 +46,19 @@ func New(

m := &Message{
// Message meta data
ID: id,
MimeType: ma.MESSAGE_MIME_TYPE,
Version: ma.VERSION,
Id: id,
Type: ma.MESSAGE_TYPE,
// Recipient
From: from,
To: to,
// Body
ContentType: contentType,
// The content is not signed as such, but the hash is.
Content: content,
Content: content,
}

err = verifyContent(content)
if err != nil {
return nil, err
}

err = m.Sign(priv_key)
Expand All @@ -81,14 +76,13 @@ func newFromHeaders(h *Headers) (*Message, error) {

err := h.validate()
if err != nil {
return nil, fmt.Errorf("msg_new_from_headers: failed to validate headers: %w", err)
return nil, fmt.Errorf("newFromHeaders: %w", err)
}

m := &Message{
// Message meta data
ID: h.ID,
MimeType: h.MimeType,
Version: h.Version,
Id: h.Id,
Type: h.Type,
// Recipient
From: h.From,
To: h.To,
Expand All @@ -97,6 +91,7 @@ func newFromHeaders(h *Headers) (*Message, error) {
// Signature
Signature: h.Signature,
}

return m, nil
}

Expand Down
73 changes: 56 additions & 17 deletions msg/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package msg

import (
"fmt"
"strings"

"github.com/bahner/go-ma"
"github.com/bahner/go-ma/did"
Expand All @@ -18,29 +19,30 @@ func (h *Headers) validate() error {
return ErrNilMessage
}

if h.MimeType != ma.MESSAGE_MIME_TYPE && h.MimeType != ma.BROADCAST_MIME_TYPE {
return ErrMessageInvalidType
// Verify ID
err = verifyID(h.Id)
if err != nil {
return err
}

// Check that message body headers are valid
if h.ContentType == "" {
return ErrMissingContentType
err = verifyType(h.Type)
if err != nil {
return err
}

// Verify ID
err = h.verifyID()
// Message version check. Check the type first
err = verifyMessageVersion(h.Type)
if err != nil {
return err
}

// Verify actors
err = h.verifyActors()
err = verifyContentType(h.ContentType)
if err != nil {
return err
}

// Message version check
err = h.verifyMessageVersion()
// Verify actors
err = h.verifyActors()
if err != nil {
return err
}
Expand All @@ -49,9 +51,14 @@ func (h *Headers) validate() error {
}

// Compare messageVersion. Return nil if ok else an error
func (h *Headers) verifyMessageVersion() error {
// Takes the type string as input
func verifyMessageVersion(t string) error {

// Split the string on "/"
parts := strings.Split(t, "/")

messageSemver, err := h.semVersion()
// Make a semver version from the last element
messageSemver, err := semver.Make(parts[len(parts)-1])
if err != nil {
return err
}
Expand Down Expand Up @@ -89,7 +96,7 @@ func (h *Headers) verifyActors() error {
return err
}

if h.ContentType == ma.BROADCAST_MIME_TYPE {
if h.ContentType == ma.BROADCAST_MESSAGE_TYPE {
if h.To != "" {
return ErrBroadcastHasRecipient
}
Expand All @@ -110,14 +117,46 @@ func (h *Headers) verifyActors() error {
}

// Check that ID is valid
func (h *Headers) verifyID() error {
if h.ID == "" {
func verifyID(id string) error {
if id == "" {
return ErrEmptyID
}

if !internal.IsValidNanoID(h.ID) {
if !internal.IsValidNanoID(id) {
return ErrInvalidID
}

return nil
}

func verifyType(t string) error {

if t == ma.MESSAGE_TYPE || t == ma.BROADCAST_MESSAGE_TYPE {
return nil
}

return ErrMessageInvalidType

}

// We don't want to parse this. That's up to the receiver
// But we do want to check that it is there.
func verifyContentType(ct string) error {

if ct == "" {
return ErrMissingContentType
}

return nil
}

// We don't want to parse this. That's up to the receiver
// But we do want to check that it is there.
func verifyContent(c []byte) error {

if c == nil {
return ErrMissingContent
}

return nil
}

0 comments on commit cc5294c

Please sign in to comment.