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

     1  // Copyright 2016 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 Envelope element.
    18  
    19  package whisperv6
    20  
    21  import (
    22  	"crypto/ecdsa"
    23  	"encoding/binary"
    24  	"fmt"
    25  	gmath "math"
    26  	"math/big"
    27  	"time"
    28  
    29  	"github.com/SmartMeshFoundation/Spectrum/common"
    30  	"github.com/SmartMeshFoundation/Spectrum/common/math"
    31  	"github.com/SmartMeshFoundation/Spectrum/crypto"
    32  	"github.com/SmartMeshFoundation/Spectrum/crypto/ecies"
    33  	"github.com/SmartMeshFoundation/Spectrum/rlp"
    34  )
    35  
    36  // Envelope represents a clear-text data packet to transmit through the Whisper
    37  // network. Its contents may or may not be encrypted and signed.
    38  type Envelope struct {
    39  	Expiry uint32
    40  	TTL    uint32
    41  	Topic  TopicType
    42  	Data   []byte
    43  	Nonce  uint64
    44  
    45  	pow  float64     // Message-specific PoW as described in the Whisper specification.
    46  	hash common.Hash // Cached hash of the envelope to avoid rehashing every time.
    47  	// Don't access hash directly, use Hash() function instead.
    48  }
    49  
    50  // size returns the size of envelope as it is sent (i.e. public fields only)
    51  func (e *Envelope) size() int {
    52  	return EnvelopeHeaderLength + len(e.Data)
    53  }
    54  
    55  // rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
    56  func (e *Envelope) rlpWithoutNonce() []byte {
    57  	res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Data})
    58  	return res
    59  }
    60  
    61  // NewEnvelope wraps a Whisper message with expiration and destination data
    62  // included into an envelope for network forwarding.
    63  func NewEnvelope(ttl uint32, topic TopicType, msg *sentMessage) *Envelope {
    64  	env := Envelope{
    65  		Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
    66  		TTL:    ttl,
    67  		Topic:  topic,
    68  		Data:   msg.Raw,
    69  		Nonce:  0,
    70  	}
    71  
    72  	return &env
    73  }
    74  
    75  // Seal closes the envelope by spending the requested amount of time as a proof
    76  // of work on hashing the data.
    77  func (e *Envelope) Seal(options *MessageParams) error {
    78  	var target, bestBit int
    79  	if options.PoW == 0 {
    80  		// adjust for the duration of Seal() execution only if execution time is predefined unconditionally
    81  		e.Expiry += options.WorkTime
    82  	} else {
    83  		target = e.powToFirstBit(options.PoW)
    84  		if target < 1 {
    85  			target = 1
    86  		}
    87  	}
    88  
    89  	buf := make([]byte, 64)
    90  	h := crypto.Keccak256(e.rlpWithoutNonce())
    91  	copy(buf[:32], h)
    92  
    93  	finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano()
    94  	for nonce := uint64(0); time.Now().UnixNano() < finish; {
    95  		for i := 0; i < 1024; i++ {
    96  			binary.BigEndian.PutUint64(buf[56:], nonce)
    97  			d := new(big.Int).SetBytes(crypto.Keccak256(buf))
    98  			firstBit := math.FirstBitSet(d)
    99  			if firstBit > bestBit {
   100  				e.Nonce, bestBit = nonce, firstBit
   101  				if target > 0 && bestBit >= target {
   102  					return nil
   103  				}
   104  			}
   105  			nonce++
   106  		}
   107  	}
   108  
   109  	if target > 0 && bestBit < target {
   110  		return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime)
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func (e *Envelope) PoW() float64 {
   117  	if e.pow == 0 {
   118  		e.calculatePoW(0)
   119  	}
   120  	return e.pow
   121  }
   122  
   123  func (e *Envelope) calculatePoW(diff uint32) {
   124  	buf := make([]byte, 64)
   125  	h := crypto.Keccak256(e.rlpWithoutNonce())
   126  	copy(buf[:32], h)
   127  	binary.BigEndian.PutUint64(buf[56:], e.Nonce)
   128  	d := new(big.Int).SetBytes(crypto.Keccak256(buf))
   129  	firstBit := math.FirstBitSet(d)
   130  	x := gmath.Pow(2, float64(firstBit))
   131  	x /= float64(e.size())
   132  	x /= float64(e.TTL + diff)
   133  	e.pow = x
   134  }
   135  
   136  func (e *Envelope) powToFirstBit(pow float64) int {
   137  	x := pow
   138  	x *= float64(e.size())
   139  	x *= float64(e.TTL)
   140  	bits := gmath.Log2(x)
   141  	bits = gmath.Ceil(bits)
   142  	return int(bits)
   143  }
   144  
   145  // Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
   146  func (e *Envelope) Hash() common.Hash {
   147  	if (e.hash == common.Hash{}) {
   148  		encoded, _ := rlp.EncodeToBytes(e)
   149  		e.hash = crypto.Keccak256Hash(encoded)
   150  	}
   151  	return e.hash
   152  }
   153  
   154  // DecodeRLP decodes an Envelope from an RLP data stream.
   155  func (e *Envelope) DecodeRLP(s *rlp.Stream) error {
   156  	raw, err := s.Raw()
   157  	if err != nil {
   158  		return err
   159  	}
   160  	// The decoding of Envelope uses the struct fields but also needs
   161  	// to compute the hash of the whole RLP-encoded envelope. This
   162  	// type has the same structure as Envelope but is not an
   163  	// rlp.Decoder (does not implement DecodeRLP function).
   164  	// Only public members will be encoded.
   165  	type rlpenv Envelope
   166  	if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil {
   167  		return err
   168  	}
   169  	e.hash = crypto.Keccak256Hash(raw)
   170  	return nil
   171  }
   172  
   173  // OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
   174  func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) {
   175  	message := &ReceivedMessage{Raw: e.Data}
   176  	err := message.decryptAsymmetric(key)
   177  	switch err {
   178  	case nil:
   179  		return message, nil
   180  	case ecies.ErrInvalidPublicKey: // addressed to somebody else
   181  		return nil, err
   182  	default:
   183  		return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err)
   184  	}
   185  }
   186  
   187  // OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
   188  func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
   189  	msg = &ReceivedMessage{Raw: e.Data}
   190  	err = msg.decryptSymmetric(key)
   191  	if err != nil {
   192  		msg = nil
   193  	}
   194  	return msg, err
   195  }
   196  
   197  // Open tries to decrypt an envelope, and populates the message fields in case of success.
   198  func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
   199  	// The API interface forbids filters doing both symmetric and
   200  	// asymmetric encryption.
   201  	if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() {
   202  		return nil
   203  	}
   204  
   205  	if watcher.expectsAsymmetricEncryption() {
   206  		msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
   207  		if msg != nil {
   208  			msg.Dst = &watcher.KeyAsym.PublicKey
   209  		}
   210  	} else if watcher.expectsSymmetricEncryption() {
   211  		msg, _ = e.OpenSymmetric(watcher.KeySym)
   212  		if msg != nil {
   213  			msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
   214  		}
   215  	}
   216  
   217  	if msg != nil {
   218  		ok := msg.Validate()
   219  		if !ok {
   220  			return nil
   221  		}
   222  		msg.Topic = e.Topic
   223  		msg.PoW = e.PoW()
   224  		msg.TTL = e.TTL
   225  		msg.Sent = e.Expiry - e.TTL
   226  		msg.EnvelopeHash = e.Hash()
   227  	}
   228  	return msg
   229  }