github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/whisper/whisperv5/envelope.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  // Contains the Whisper protocol Envelope element.
    13  
    14  package whisperv5
    15  
    16  import (
    17  	"crypto/ecdsa"
    18  	"encoding/binary"
    19  	"fmt"
    20  	gmath "math"
    21  	"math/big"
    22  	"time"
    23  
    24  	"github.com/Sberex/go-sberex/common"
    25  	"github.com/Sberex/go-sberex/common/math"
    26  	"github.com/Sberex/go-sberex/crypto"
    27  	"github.com/Sberex/go-sberex/crypto/ecies"
    28  	"github.com/Sberex/go-sberex/rlp"
    29  )
    30  
    31  // Envelope represents a clear-text data packet to transmit through the Whisper
    32  // network. Its contents may or may not be encrypted and signed.
    33  type Envelope struct {
    34  	Version  []byte
    35  	Expiry   uint32
    36  	TTL      uint32
    37  	Topic    TopicType
    38  	AESNonce []byte
    39  	Data     []byte
    40  	EnvNonce uint64
    41  
    42  	pow  float64     // Message-specific PoW as described in the Whisper specification.
    43  	hash common.Hash // Cached hash of the envelope to avoid rehashing every time.
    44  	// Don't access hash directly, use Hash() function instead.
    45  }
    46  
    47  // size returns the size of envelope as it is sent (i.e. public fields only)
    48  func (e *Envelope) size() int {
    49  	return 20 + len(e.Version) + len(e.AESNonce) + len(e.Data)
    50  }
    51  
    52  // rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
    53  func (e *Envelope) rlpWithoutNonce() []byte {
    54  	res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.AESNonce, e.Data})
    55  	return res
    56  }
    57  
    58  // NewEnvelope wraps a Whisper message with expiration and destination data
    59  // included into an envelope for network forwarding.
    60  func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *sentMessage) *Envelope {
    61  	env := Envelope{
    62  		Version:  make([]byte, 1),
    63  		Expiry:   uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
    64  		TTL:      ttl,
    65  		Topic:    topic,
    66  		AESNonce: aesNonce,
    67  		Data:     msg.Raw,
    68  		EnvNonce: 0,
    69  	}
    70  
    71  	if EnvelopeVersion < 256 {
    72  		env.Version[0] = byte(EnvelopeVersion)
    73  	} else {
    74  		panic("please increase the size of Envelope.Version before releasing this version")
    75  	}
    76  
    77  	return &env
    78  }
    79  
    80  func (e *Envelope) IsSymmetric() bool {
    81  	return len(e.AESNonce) > 0
    82  }
    83  
    84  func (e *Envelope) isAsymmetric() bool {
    85  	return !e.IsSymmetric()
    86  }
    87  
    88  func (e *Envelope) Ver() uint64 {
    89  	return bytesToUintLittleEndian(e.Version)
    90  }
    91  
    92  // Seal closes the envelope by spending the requested amount of time as a proof
    93  // of work on hashing the data.
    94  func (e *Envelope) Seal(options *MessageParams) error {
    95  	var target, bestBit int
    96  	if options.PoW == 0 {
    97  		// adjust for the duration of Seal() execution only if execution time is predefined unconditionally
    98  		e.Expiry += options.WorkTime
    99  	} else {
   100  		target = e.powToFirstBit(options.PoW)
   101  		if target < 1 {
   102  			target = 1
   103  		}
   104  	}
   105  
   106  	buf := make([]byte, 64)
   107  	h := crypto.Keccak256(e.rlpWithoutNonce())
   108  	copy(buf[:32], h)
   109  
   110  	finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano()
   111  	for nonce := uint64(0); time.Now().UnixNano() < finish; {
   112  		for i := 0; i < 1024; i++ {
   113  			binary.BigEndian.PutUint64(buf[56:], nonce)
   114  			d := new(big.Int).SetBytes(crypto.Keccak256(buf))
   115  			firstBit := math.FirstBitSet(d)
   116  			if firstBit > bestBit {
   117  				e.EnvNonce, bestBit = nonce, firstBit
   118  				if target > 0 && bestBit >= target {
   119  					return nil
   120  				}
   121  			}
   122  			nonce++
   123  		}
   124  	}
   125  
   126  	if target > 0 && bestBit < target {
   127  		return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime)
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  func (e *Envelope) PoW() float64 {
   134  	if e.pow == 0 {
   135  		e.calculatePoW(0)
   136  	}
   137  	return e.pow
   138  }
   139  
   140  func (e *Envelope) calculatePoW(diff uint32) {
   141  	buf := make([]byte, 64)
   142  	h := crypto.Keccak256(e.rlpWithoutNonce())
   143  	copy(buf[:32], h)
   144  	binary.BigEndian.PutUint64(buf[56:], e.EnvNonce)
   145  	d := new(big.Int).SetBytes(crypto.Keccak256(buf))
   146  	firstBit := math.FirstBitSet(d)
   147  	x := gmath.Pow(2, float64(firstBit))
   148  	x /= float64(e.size())
   149  	x /= float64(e.TTL + diff)
   150  	e.pow = x
   151  }
   152  
   153  func (e *Envelope) powToFirstBit(pow float64) int {
   154  	x := pow
   155  	x *= float64(e.size())
   156  	x *= float64(e.TTL)
   157  	bits := gmath.Log2(x)
   158  	bits = gmath.Ceil(bits)
   159  	return int(bits)
   160  }
   161  
   162  // Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
   163  func (e *Envelope) Hash() common.Hash {
   164  	if (e.hash == common.Hash{}) {
   165  		encoded, _ := rlp.EncodeToBytes(e)
   166  		e.hash = crypto.Keccak256Hash(encoded)
   167  	}
   168  	return e.hash
   169  }
   170  
   171  // DecodeRLP decodes an Envelope from an RLP data stream.
   172  func (e *Envelope) DecodeRLP(s *rlp.Stream) error {
   173  	raw, err := s.Raw()
   174  	if err != nil {
   175  		return err
   176  	}
   177  	// The decoding of Envelope uses the struct fields but also needs
   178  	// to compute the hash of the whole RLP-encoded envelope. This
   179  	// type has the same structure as Envelope but is not an
   180  	// rlp.Decoder (does not implement DecodeRLP function).
   181  	// Only public members will be encoded.
   182  	type rlpenv Envelope
   183  	if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil {
   184  		return err
   185  	}
   186  	e.hash = crypto.Keccak256Hash(raw)
   187  	return nil
   188  }
   189  
   190  // OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
   191  func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) {
   192  	message := &ReceivedMessage{Raw: e.Data}
   193  	err := message.decryptAsymmetric(key)
   194  	switch err {
   195  	case nil:
   196  		return message, nil
   197  	case ecies.ErrInvalidPublicKey: // addressed to somebody else
   198  		return nil, err
   199  	default:
   200  		return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err)
   201  	}
   202  }
   203  
   204  // OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
   205  func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
   206  	msg = &ReceivedMessage{Raw: e.Data}
   207  	err = msg.decryptSymmetric(key, e.AESNonce)
   208  	if err != nil {
   209  		msg = nil
   210  	}
   211  	return msg, err
   212  }
   213  
   214  // Open tries to decrypt an envelope, and populates the message fields in case of success.
   215  func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
   216  	if e.isAsymmetric() {
   217  		msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
   218  		if msg != nil {
   219  			msg.Dst = &watcher.KeyAsym.PublicKey
   220  		}
   221  	} else if e.IsSymmetric() {
   222  		msg, _ = e.OpenSymmetric(watcher.KeySym)
   223  		if msg != nil {
   224  			msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
   225  		}
   226  	}
   227  
   228  	if msg != nil {
   229  		ok := msg.Validate()
   230  		if !ok {
   231  			return nil
   232  		}
   233  		msg.Topic = e.Topic
   234  		msg.PoW = e.PoW()
   235  		msg.TTL = e.TTL
   236  		msg.Sent = e.Expiry - e.TTL
   237  		msg.EnvelopeHash = e.Hash()
   238  		msg.EnvelopeVersion = e.Ver()
   239  	}
   240  	return msg
   241  }