github.com/avence12/go-ethereum@v1.5.10-0.20170320123548-1dfd65f6d047/whisper/whisperv5/envelope.go (about)

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