github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv2/message.go (about)

     1  // Copyright 2014 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The Spectrum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Contains the Whisper protocol Message element. For formal details please see
    18  // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#messages.
    19  
    20  package whisperv2
    21  
    22  import (
    23  	"crypto/ecdsa"
    24  	crand "crypto/rand"
    25  	"fmt"
    26  	"math/rand"
    27  	"time"
    28  
    29  	"github.com/SmartMeshFoundation/Spectrum/common"
    30  	"github.com/SmartMeshFoundation/Spectrum/crypto"
    31  	"github.com/SmartMeshFoundation/Spectrum/crypto/ecies"
    32  	"github.com/SmartMeshFoundation/Spectrum/log"
    33  )
    34  
    35  // Message represents an end-user data packet to transmit through the Whisper
    36  // protocol. These are wrapped into Envelopes that need not be understood by
    37  // intermediate nodes, just forwarded.
    38  type Message struct {
    39  	Flags     byte // First bit is signature presence, rest reserved and should be random
    40  	Signature []byte
    41  	Payload   []byte
    42  
    43  	Sent time.Time     // Time when the message was posted into the network
    44  	TTL  time.Duration // Maximum time to live allowed for the message
    45  
    46  	To   *ecdsa.PublicKey // Message recipient (identity used to decode the message)
    47  	Hash common.Hash      // Message envelope hash to act as a unique id
    48  }
    49  
    50  // Options specifies the exact way a message should be wrapped into an Envelope.
    51  type Options struct {
    52  	From   *ecdsa.PrivateKey
    53  	To     *ecdsa.PublicKey
    54  	TTL    time.Duration
    55  	Topics []Topic
    56  }
    57  
    58  // NewMessage creates and initializes a non-signed, non-encrypted Whisper message.
    59  func NewMessage(payload []byte) *Message {
    60  	// Construct an initial flag set: no signature, rest random
    61  	flags := byte(rand.Intn(256))
    62  	flags &= ^signatureFlag
    63  
    64  	// Assemble and return the message
    65  	return &Message{
    66  		Flags:   flags,
    67  		Payload: payload,
    68  		Sent:    time.Now(),
    69  	}
    70  }
    71  
    72  // Wrap bundles the message into an Envelope to transmit over the network.
    73  //
    74  // pow (Proof Of Work) controls how much time to spend on hashing the message,
    75  // inherently controlling its priority through the network (smaller hash, bigger
    76  // priority).
    77  //
    78  // The user can control the amount of identity, privacy and encryption through
    79  // the options parameter as follows:
    80  //   - options.From == nil && options.To == nil: anonymous broadcast
    81  //   - options.From != nil && options.To == nil: signed broadcast (known sender)
    82  //   - options.From == nil && options.To != nil: encrypted anonymous message
    83  //   - options.From != nil && options.To != nil: encrypted signed message
    84  func (self *Message) Wrap(pow time.Duration, options Options) (*Envelope, error) {
    85  	// Use the default TTL if non was specified
    86  	if options.TTL == 0 {
    87  		options.TTL = DefaultTTL
    88  	}
    89  	self.TTL = options.TTL
    90  
    91  	// Sign and encrypt the message if requested
    92  	if options.From != nil {
    93  		if err := self.sign(options.From); err != nil {
    94  			return nil, err
    95  		}
    96  	}
    97  	if options.To != nil {
    98  		if err := self.encrypt(options.To); err != nil {
    99  			return nil, err
   100  		}
   101  	}
   102  	// Wrap the processed message, seal it and return
   103  	envelope := NewEnvelope(options.TTL, options.Topics, self)
   104  	envelope.Seal(pow)
   105  
   106  	return envelope, nil
   107  }
   108  
   109  // sign calculates and sets the cryptographic signature for the message , also
   110  // setting the sign flag.
   111  func (self *Message) sign(key *ecdsa.PrivateKey) (err error) {
   112  	self.Flags |= signatureFlag
   113  	self.Signature, err = crypto.Sign(self.hash(), key)
   114  	return
   115  }
   116  
   117  // Recover retrieves the public key of the message signer.
   118  func (self *Message) Recover() *ecdsa.PublicKey {
   119  	defer func() { recover() }() // in case of invalid signature
   120  
   121  	// Short circuit if no signature is present
   122  	if self.Signature == nil {
   123  		return nil
   124  	}
   125  	// Otherwise try and recover the signature
   126  	pub, err := crypto.SigToPub(self.hash(), self.Signature)
   127  	if err != nil {
   128  		log.Error(fmt.Sprintf("Could not get public key from signature: %v", err))
   129  		return nil
   130  	}
   131  	return pub
   132  }
   133  
   134  // encrypt encrypts a message payload with a public key.
   135  func (self *Message) encrypt(key *ecdsa.PublicKey) (err error) {
   136  	self.Payload, err = ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), self.Payload, nil, nil)
   137  	return
   138  }
   139  
   140  // decrypt decrypts an encrypted payload with a private key.
   141  func (self *Message) decrypt(key *ecdsa.PrivateKey) error {
   142  	cleartext, err := ecies.ImportECDSA(key).Decrypt(crand.Reader, self.Payload, nil, nil)
   143  	if err == nil {
   144  		self.Payload = cleartext
   145  	}
   146  	return err
   147  }
   148  
   149  // hash calculates the SHA3 checksum of the message flags and payload.
   150  func (self *Message) hash() []byte {
   151  	return crypto.Keccak256(append([]byte{self.Flags}, self.Payload...))
   152  }
   153  
   154  // bytes flattens the message contents (flags, signature and payload) into a
   155  // single binary blob.
   156  func (self *Message) bytes() []byte {
   157  	return append([]byte{self.Flags}, append(self.Signature, self.Payload...)...)
   158  }