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 }