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 }