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