github.com/status-im/status-go@v1.1.0/waku/common/message.go (about)

     1  // Copyright 2019 The Waku Library Authors.
     2  //
     3  // The Waku library is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Lesser General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // (at your option) any later version.
     7  //
     8  // The Waku library is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty off
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    11  // GNU Lesser General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Lesser General Public License
    14  // along with the Waku library. If not, see <http://www.gnu.org/licenses/>.
    15  //
    16  // This software uses the go-ethereum library, which is licensed
    17  // under the GNU Lesser General Public Library, version 3 or any later.
    18  
    19  package common
    20  
    21  import (
    22  	"crypto/aes"
    23  	"crypto/cipher"
    24  	"crypto/ecdsa"
    25  	crand "crypto/rand"
    26  	"encoding/binary"
    27  	"errors"
    28  	"fmt"
    29  	"strconv"
    30  	"sync"
    31  	"time"
    32  
    33  	"github.com/ethereum/go-ethereum/common"
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	"github.com/ethereum/go-ethereum/crypto/ecies"
    36  	"github.com/ethereum/go-ethereum/log"
    37  )
    38  
    39  // MessageParams specifies the exact way a message should be wrapped
    40  // into an Envelope.
    41  type MessageParams struct {
    42  	TTL      uint32
    43  	Src      *ecdsa.PrivateKey
    44  	Dst      *ecdsa.PublicKey
    45  	KeySym   []byte
    46  	Topic    TopicType
    47  	WorkTime uint32
    48  	PoW      float64
    49  	Payload  []byte
    50  	Padding  []byte
    51  }
    52  
    53  // SentMessage represents an end-user data packet to transmit through the
    54  // Waku protocol. These are wrapped into Envelopes that need not be
    55  // understood by intermediate nodes, just forwarded.
    56  type SentMessage struct {
    57  	Raw []byte
    58  }
    59  
    60  // ReceivedMessage represents a data packet to be received through the
    61  // Waku protocol and successfully decrypted.
    62  type ReceivedMessage struct {
    63  	Raw []byte
    64  
    65  	Payload   []byte
    66  	Padding   []byte
    67  	Signature []byte
    68  	Salt      []byte
    69  
    70  	PoW   float64          // Proof of work as described in the Waku spec
    71  	Sent  uint32           // Time when the message was posted into the network
    72  	TTL   uint32           // Maximum time to live allowed for the message
    73  	Src   *ecdsa.PublicKey // Message recipient (identity used to decode the message)
    74  	Dst   *ecdsa.PublicKey // Message recipient (identity used to decode the message)
    75  	Topic TopicType
    76  
    77  	SymKeyHash   common.Hash // The Keccak256Hash of the key
    78  	EnvelopeHash common.Hash // Message envelope hash to act as a unique id
    79  
    80  	P2P bool // is set to true if this message was received from mail server.
    81  }
    82  
    83  // MessagesRequest contains details of a request for historic messages.
    84  type MessagesRequest struct {
    85  	// ID of the request. The current implementation requires ID to be 32-byte array,
    86  	// however, it's not enforced for future implementation.
    87  	ID []byte `json:"id"`
    88  
    89  	// From is a lower bound of time range.
    90  	From uint32 `json:"from"`
    91  
    92  	// To is a upper bound of time range.
    93  	To uint32 `json:"to"`
    94  
    95  	// Limit determines the number of messages sent by the mail server
    96  	// for the current paginated request.
    97  	Limit uint32 `json:"limit"`
    98  
    99  	// Cursor is used as starting point for paginated requests.
   100  	Cursor []byte `json:"cursor"`
   101  
   102  	// Bloom is a filter to match requested messages.
   103  	Bloom []byte `json:"bloom"`
   104  
   105  	// Topics is a list of topics. A returned message should
   106  	// belong to one of the topics from the list.
   107  	Topics [][]byte `json:"topics"`
   108  }
   109  
   110  func (r MessagesRequest) Validate() error {
   111  	if len(r.ID) != common.HashLength {
   112  		return errors.New("invalid 'ID', expected a 32-byte slice")
   113  	}
   114  
   115  	if r.From > r.To {
   116  		return errors.New("invalid 'From' value which is greater than To")
   117  	}
   118  
   119  	if r.Limit > MaxLimitInMessagesRequest {
   120  		return fmt.Errorf("invalid 'Limit' value, expected value lower than %d", MaxLimitInMessagesRequest)
   121  	}
   122  
   123  	if len(r.Bloom) == 0 && len(r.Topics) == 0 {
   124  		return errors.New("invalid 'Bloom' or 'Topics', one must be non-empty")
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  // MessagesResponse sent as a response after processing batch of envelopes.
   131  type MessagesResponse struct {
   132  	// Hash is a hash of all envelopes sent in the single batch.
   133  	Hash common.Hash
   134  	// Per envelope error.
   135  	Errors []EnvelopeError
   136  }
   137  
   138  func IsMessageSigned(flags byte) bool {
   139  	return (flags & signatureFlag) != 0
   140  }
   141  
   142  func (msg *ReceivedMessage) isSymmetricEncryption() bool {
   143  	return msg.SymKeyHash != common.Hash{}
   144  }
   145  
   146  func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
   147  	return msg.Dst != nil
   148  }
   149  
   150  // NewSentMessage creates and initializes a non-signed, non-encrypted Waku message.
   151  func NewSentMessage(params *MessageParams) (*SentMessage, error) {
   152  	const payloadSizeFieldMaxSize = 4
   153  	msg := SentMessage{}
   154  	msg.Raw = make([]byte, 1,
   155  		flagsLength+payloadSizeFieldMaxSize+len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit)
   156  	msg.Raw[0] = 0 // set all the flags to zero
   157  	msg.addPayloadSizeField(params.Payload)
   158  	msg.Raw = append(msg.Raw, params.Payload...)
   159  	err := msg.appendPadding(params)
   160  	return &msg, err
   161  }
   162  
   163  // addPayloadSizeField appends the auxiliary field containing the size of payload
   164  func (msg *SentMessage) addPayloadSizeField(payload []byte) {
   165  	fieldSize := getSizeOfPayloadSizeField(payload)
   166  	field := make([]byte, 4)
   167  	binary.LittleEndian.PutUint32(field, uint32(len(payload)))
   168  	field = field[:fieldSize]
   169  	msg.Raw = append(msg.Raw, field...)
   170  	msg.Raw[0] |= byte(fieldSize)
   171  }
   172  
   173  // getSizeOfPayloadSizeField returns the number of bytes necessary to encode the size of payload
   174  func getSizeOfPayloadSizeField(payload []byte) int {
   175  	s := 1
   176  	for i := len(payload); i >= 256; i /= 256 {
   177  		s++
   178  	}
   179  	return s
   180  }
   181  
   182  // appendPadding appends the padding specified in params.
   183  // If no padding is provided in params, then random padding is generated.
   184  func (msg *SentMessage) appendPadding(params *MessageParams) error {
   185  	if len(params.Padding) != 0 {
   186  		// padding data was provided by the Dapp, just use it as is
   187  		msg.Raw = append(msg.Raw, params.Padding...)
   188  		return nil
   189  	}
   190  
   191  	rawSize := flagsLength + getSizeOfPayloadSizeField(params.Payload) + len(params.Payload)
   192  	if params.Src != nil {
   193  		rawSize += signatureLength
   194  	}
   195  	odd := rawSize % padSizeLimit
   196  	paddingSize := padSizeLimit - odd
   197  	pad := make([]byte, paddingSize)
   198  	_, err := crand.Read(pad)
   199  	if err != nil {
   200  		return err
   201  	}
   202  	if !ValidateDataIntegrity(pad, paddingSize) {
   203  		return errors.New("failed to generate random padding of size " + strconv.Itoa(paddingSize))
   204  	}
   205  	msg.Raw = append(msg.Raw, pad...)
   206  	return nil
   207  }
   208  
   209  // sign calculates and sets the cryptographic signature for the message,
   210  // also setting the sign flag.
   211  func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error {
   212  	if IsMessageSigned(msg.Raw[0]) {
   213  		// this should not happen, but no reason to panic
   214  		log.Error("failed to sign the message: already signed")
   215  		return nil
   216  	}
   217  
   218  	msg.Raw[0] |= signatureFlag // it is important to set this flag before signing
   219  	hash := crypto.Keccak256(msg.Raw)
   220  	signature, err := crypto.Sign(hash, key)
   221  	if err != nil {
   222  		msg.Raw[0] &= 0xFF ^ signatureFlag // clear the flag
   223  		return err
   224  	}
   225  	msg.Raw = append(msg.Raw, signature...)
   226  	return nil
   227  }
   228  
   229  // encryptAsymmetric encrypts a message with a public key.
   230  func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
   231  	if !ValidatePublicKey(key) {
   232  		return errors.New("invalid public key provided for asymmetric encryption")
   233  	}
   234  	encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil)
   235  	if err == nil {
   236  		msg.Raw = encrypted
   237  	}
   238  	return err
   239  }
   240  
   241  // encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
   242  // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
   243  func (msg *SentMessage) encryptSymmetric(key []byte) (err error) {
   244  	if !ValidateDataIntegrity(key, AESKeyLength) {
   245  		return errors.New("invalid key provided for symmetric encryption, size: " + strconv.Itoa(len(key)))
   246  	}
   247  	block, err := aes.NewCipher(key)
   248  	if err != nil {
   249  		return err
   250  	}
   251  	aesgcm, err := cipher.NewGCM(block)
   252  	if err != nil {
   253  		return err
   254  	}
   255  	salt, err := GenerateSecureRandomData(aesNonceLength) // never use more than 2^32 random nonces with a given key
   256  	if err != nil {
   257  		return err
   258  	}
   259  	encrypted := aesgcm.Seal(nil, salt, msg.Raw, nil)
   260  	msg.Raw = append(encrypted, salt...)
   261  	return nil
   262  }
   263  
   264  // Wrap bundles the message into an Envelope to transmit over the network.
   265  func (msg *SentMessage) Wrap(options *MessageParams, now time.Time) (envelope *Envelope, err error) {
   266  	if options.TTL == 0 {
   267  		options.TTL = DefaultTTL
   268  	}
   269  	if options.Src != nil {
   270  		if err = msg.sign(options.Src); err != nil {
   271  			return nil, err
   272  		}
   273  	}
   274  	if options.Dst != nil {
   275  		err = msg.encryptAsymmetric(options.Dst)
   276  	} else if options.KeySym != nil {
   277  		err = msg.encryptSymmetric(options.KeySym)
   278  	} else {
   279  		err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
   280  	}
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	envelope = NewEnvelope(options.TTL, options.Topic, msg, now)
   286  	if err = envelope.Seal(options); err != nil {
   287  		return nil, err
   288  	}
   289  	return envelope, nil
   290  }
   291  
   292  // decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
   293  // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
   294  func (msg *ReceivedMessage) decryptSymmetric(key []byte) error {
   295  	// symmetric messages are expected to contain the 12-byte nonce at the end of the payload
   296  	if len(msg.Raw) < aesNonceLength {
   297  		return errors.New("missing salt or invalid payload in symmetric message")
   298  	}
   299  	salt := msg.Raw[len(msg.Raw)-aesNonceLength:]
   300  
   301  	block, err := aes.NewCipher(key)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	aesgcm, err := cipher.NewGCM(block)
   306  	if err != nil {
   307  		return err
   308  	}
   309  	decrypted, err := aesgcm.Open(nil, salt, msg.Raw[:len(msg.Raw)-aesNonceLength], nil)
   310  	if err != nil {
   311  		return err
   312  	}
   313  	msg.Raw = decrypted
   314  	msg.Salt = salt
   315  	return nil
   316  }
   317  
   318  // decryptAsymmetric decrypts an encrypted payload with a private key.
   319  func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error {
   320  	decrypted, err := ecies.ImportECDSA(key).Decrypt(msg.Raw, nil, nil)
   321  	if err == nil {
   322  		msg.Raw = decrypted
   323  	}
   324  	return err
   325  }
   326  
   327  // ValidateAndParse checks the message validity and extracts the fields in case of success.
   328  func (msg *ReceivedMessage) ValidateAndParse() bool {
   329  	end := len(msg.Raw)
   330  	if end < 1 {
   331  		return false
   332  	}
   333  
   334  	if IsMessageSigned(msg.Raw[0]) {
   335  		end -= signatureLength
   336  		if end <= 1 {
   337  			return false
   338  		}
   339  		msg.Signature = msg.Raw[end : end+signatureLength]
   340  		msg.Src = msg.SigToPubKey()
   341  		if msg.Src == nil {
   342  			return false
   343  		}
   344  	}
   345  
   346  	beg := 1
   347  	payloadSize := 0
   348  	sizeOfPayloadSizeField := int(msg.Raw[0] & SizeMask) // number of bytes indicating the size of payload
   349  	if sizeOfPayloadSizeField != 0 {
   350  		if end < beg+sizeOfPayloadSizeField {
   351  			return false
   352  		}
   353  		payloadSize = int(BytesToUintLittleEndian(msg.Raw[beg : beg+sizeOfPayloadSizeField]))
   354  		beg += sizeOfPayloadSizeField
   355  		if beg+payloadSize > end {
   356  			return false
   357  		}
   358  		msg.Payload = msg.Raw[beg : beg+payloadSize]
   359  	}
   360  
   361  	beg += payloadSize
   362  	msg.Padding = msg.Raw[beg:end]
   363  	return true
   364  }
   365  
   366  // SigToPubKey returns the public key associated to the message's
   367  // signature.
   368  func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
   369  	// in case of invalid signature
   370  	defer func() { recover() }() // nolint: errcheck
   371  
   372  	pub, err := crypto.SigToPub(msg.hash(), msg.Signature)
   373  	if err != nil {
   374  		log.Error("failed to recover public key from signature", "err", err)
   375  		return nil
   376  	}
   377  	return pub
   378  }
   379  
   380  // hash calculates the SHA3 checksum of the message flags, payload size field, payload and padding.
   381  func (msg *ReceivedMessage) hash() []byte {
   382  	if IsMessageSigned(msg.Raw[0]) {
   383  		sz := len(msg.Raw) - signatureLength
   384  		return crypto.Keccak256(msg.Raw[:sz])
   385  	}
   386  	return crypto.Keccak256(msg.Raw)
   387  }
   388  
   389  // MessageStore defines interface for temporary message store.
   390  type MessageStore interface {
   391  	Add(*ReceivedMessage) error
   392  	Pop() ([]*ReceivedMessage, error)
   393  }
   394  
   395  // NewMemoryMessageStore returns pointer to an instance of the MemoryMessageStore.
   396  func NewMemoryMessageStore() *MemoryMessageStore {
   397  	return &MemoryMessageStore{
   398  		messages: map[common.Hash]*ReceivedMessage{},
   399  	}
   400  }
   401  
   402  // MemoryMessageStore represents messages stored in a memory hash table.
   403  type MemoryMessageStore struct {
   404  	mu       sync.Mutex
   405  	messages map[common.Hash]*ReceivedMessage
   406  }
   407  
   408  // Add adds message to store.
   409  func (store *MemoryMessageStore) Add(msg *ReceivedMessage) error {
   410  	store.mu.Lock()
   411  	defer store.mu.Unlock()
   412  	if _, exist := store.messages[msg.EnvelopeHash]; !exist {
   413  		store.messages[msg.EnvelopeHash] = msg
   414  	}
   415  	return nil
   416  }
   417  
   418  // Pop returns all available messages and cleans the store.
   419  func (store *MemoryMessageStore) Pop() ([]*ReceivedMessage, error) {
   420  	store.mu.Lock()
   421  	defer store.mu.Unlock()
   422  	all := make([]*ReceivedMessage, 0, len(store.messages))
   423  	for hash, msg := range store.messages {
   424  		delete(store.messages, hash)
   425  		all = append(all, msg)
   426  	}
   427  	return all, nil
   428  }