github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/whisper/whisperv5/message.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 Message element. 13 14 package whisperv5 15 16 import ( 17 "crypto/aes" 18 "crypto/cipher" 19 "crypto/ecdsa" 20 crand "crypto/rand" 21 "encoding/binary" 22 "errors" 23 "strconv" 24 25 "github.com/Sberex/go-sberex/common" 26 "github.com/Sberex/go-sberex/crypto" 27 "github.com/Sberex/go-sberex/crypto/ecies" 28 "github.com/Sberex/go-sberex/log" 29 ) 30 31 // Options specifies the exact way a message should be wrapped into an Envelope. 32 type MessageParams struct { 33 TTL uint32 34 Src *ecdsa.PrivateKey 35 Dst *ecdsa.PublicKey 36 KeySym []byte 37 Topic TopicType 38 WorkTime uint32 39 PoW float64 40 Payload []byte 41 Padding []byte 42 } 43 44 // SentMessage represents an end-user data packet to transmit through the 45 // Whisper protocol. These are wrapped into Envelopes that need not be 46 // understood by intermediate nodes, just forwarded. 47 type sentMessage struct { 48 Raw []byte 49 } 50 51 // ReceivedMessage represents a data packet to be received through the 52 // Whisper protocol. 53 type ReceivedMessage struct { 54 Raw []byte 55 56 Payload []byte 57 Padding []byte 58 Signature []byte 59 60 PoW float64 // Proof of work as described in the Whisper spec 61 Sent uint32 // Time when the message was posted into the network 62 TTL uint32 // Maximum time to live allowed for the message 63 Src *ecdsa.PublicKey // Message recipient (identity used to decode the message) 64 Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message) 65 Topic TopicType 66 67 SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic 68 EnvelopeHash common.Hash // Message envelope hash to act as a unique id 69 EnvelopeVersion uint64 70 } 71 72 func isMessageSigned(flags byte) bool { 73 return (flags & signatureFlag) != 0 74 } 75 76 func (msg *ReceivedMessage) isSymmetricEncryption() bool { 77 return msg.SymKeyHash != common.Hash{} 78 } 79 80 func (msg *ReceivedMessage) isAsymmetricEncryption() bool { 81 return msg.Dst != nil 82 } 83 84 // NewMessage creates and initializes a non-signed, non-encrypted Whisper message. 85 func NewSentMessage(params *MessageParams) (*sentMessage, error) { 86 msg := sentMessage{} 87 msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit) 88 msg.Raw[0] = 0 // set all the flags to zero 89 err := msg.appendPadding(params) 90 if err != nil { 91 return nil, err 92 } 93 msg.Raw = append(msg.Raw, params.Payload...) 94 return &msg, nil 95 } 96 97 // getSizeOfLength returns the number of bytes necessary to encode the entire size padding (including these bytes) 98 func getSizeOfLength(b []byte) (sz int, err error) { 99 sz = intSize(len(b)) // first iteration 100 sz = intSize(len(b) + sz) // second iteration 101 if sz > 3 { 102 err = errors.New("oversized padding parameter") 103 } 104 return sz, err 105 } 106 107 // sizeOfIntSize returns minimal number of bytes necessary to encode an integer value 108 func intSize(i int) (s int) { 109 for s = 1; i >= 256; s++ { 110 i /= 256 111 } 112 return s 113 } 114 115 // appendPadding appends the pseudorandom padding bytes and sets the padding flag. 116 // The last byte contains the size of padding (thus, its size must not exceed 256). 117 func (msg *sentMessage) appendPadding(params *MessageParams) error { 118 rawSize := len(params.Payload) + 1 119 if params.Src != nil { 120 rawSize += signatureLength 121 } 122 odd := rawSize % padSizeLimit 123 124 if len(params.Padding) != 0 { 125 padSize := len(params.Padding) 126 padLengthSize, err := getSizeOfLength(params.Padding) 127 if err != nil { 128 return err 129 } 130 totalPadSize := padSize + padLengthSize 131 buf := make([]byte, 8) 132 binary.LittleEndian.PutUint32(buf, uint32(totalPadSize)) 133 buf = buf[:padLengthSize] 134 msg.Raw = append(msg.Raw, buf...) 135 msg.Raw = append(msg.Raw, params.Padding...) 136 msg.Raw[0] |= byte(padLengthSize) // number of bytes indicating the padding size 137 } else if odd != 0 { 138 totalPadSize := padSizeLimit - odd 139 if totalPadSize > 255 { 140 // this algorithm is only valid if padSizeLimit < 256. 141 // if padSizeLimit will ever change, please fix the algorithm 142 // (please see also ReceivedMessage.extractPadding() function). 143 panic("please fix the padding algorithm before releasing new version") 144 } 145 buf := make([]byte, totalPadSize) 146 _, err := crand.Read(buf[1:]) 147 if err != nil { 148 return err 149 } 150 if totalPadSize > 6 && !validateSymmetricKey(buf) { 151 return errors.New("failed to generate random padding of size " + strconv.Itoa(totalPadSize)) 152 } 153 buf[0] = byte(totalPadSize) 154 msg.Raw = append(msg.Raw, buf...) 155 msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size 156 } 157 return nil 158 } 159 160 // sign calculates and sets the cryptographic signature for the message, 161 // also setting the sign flag. 162 func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error { 163 if isMessageSigned(msg.Raw[0]) { 164 // this should not happen, but no reason to panic 165 log.Error("failed to sign the message: already signed") 166 return nil 167 } 168 169 msg.Raw[0] |= signatureFlag 170 hash := crypto.Keccak256(msg.Raw) 171 signature, err := crypto.Sign(hash, key) 172 if err != nil { 173 msg.Raw[0] &= ^signatureFlag // clear the flag 174 return err 175 } 176 msg.Raw = append(msg.Raw, signature...) 177 return nil 178 } 179 180 // encryptAsymmetric encrypts a message with a public key. 181 func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { 182 if !ValidatePublicKey(key) { 183 return errors.New("invalid public key provided for asymmetric encryption") 184 } 185 encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil) 186 if err == nil { 187 msg.Raw = encrypted 188 } 189 return err 190 } 191 192 // encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. 193 // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). 194 func (msg *sentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) { 195 if !validateSymmetricKey(key) { 196 return nil, errors.New("invalid key provided for symmetric encryption") 197 } 198 199 block, err := aes.NewCipher(key) 200 if err != nil { 201 return nil, err 202 } 203 aesgcm, err := cipher.NewGCM(block) 204 if err != nil { 205 return nil, err 206 } 207 208 // never use more than 2^32 random nonces with a given key 209 nonce = make([]byte, aesgcm.NonceSize()) 210 _, err = crand.Read(nonce) 211 if err != nil { 212 return nil, err 213 } else if !validateSymmetricKey(nonce) { 214 return nil, errors.New("crypto/rand failed to generate nonce") 215 } 216 217 msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil) 218 return nonce, nil 219 } 220 221 // Wrap bundles the message into an Envelope to transmit over the network. 222 func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { 223 if options.TTL == 0 { 224 options.TTL = DefaultTTL 225 } 226 if options.Src != nil { 227 if err = msg.sign(options.Src); err != nil { 228 return nil, err 229 } 230 } 231 var nonce []byte 232 if options.Dst != nil { 233 err = msg.encryptAsymmetric(options.Dst) 234 } else if options.KeySym != nil { 235 nonce, err = msg.encryptSymmetric(options.KeySym) 236 } else { 237 err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") 238 } 239 if err != nil { 240 return nil, err 241 } 242 243 envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg) 244 if err = envelope.Seal(options); err != nil { 245 return nil, err 246 } 247 return envelope, nil 248 } 249 250 // decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. 251 // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). 252 func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error { 253 block, err := aes.NewCipher(key) 254 if err != nil { 255 return err 256 } 257 aesgcm, err := cipher.NewGCM(block) 258 if err != nil { 259 return err 260 } 261 if len(nonce) != aesgcm.NonceSize() { 262 log.Error("decrypting the message", "AES nonce size", len(nonce)) 263 return errors.New("wrong AES nonce size") 264 } 265 decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil) 266 if err != nil { 267 return err 268 } 269 msg.Raw = decrypted 270 return nil 271 } 272 273 // decryptAsymmetric decrypts an encrypted payload with a private key. 274 func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { 275 decrypted, err := ecies.ImportECDSA(key).Decrypt(crand.Reader, msg.Raw, nil, nil) 276 if err == nil { 277 msg.Raw = decrypted 278 } 279 return err 280 } 281 282 // Validate checks the validity and extracts the fields in case of success 283 func (msg *ReceivedMessage) Validate() bool { 284 end := len(msg.Raw) 285 if end < 1 { 286 return false 287 } 288 289 if isMessageSigned(msg.Raw[0]) { 290 end -= signatureLength 291 if end <= 1 { 292 return false 293 } 294 msg.Signature = msg.Raw[end:] 295 msg.Src = msg.SigToPubKey() 296 if msg.Src == nil { 297 return false 298 } 299 } 300 301 padSize, ok := msg.extractPadding(end) 302 if !ok { 303 return false 304 } 305 306 msg.Payload = msg.Raw[1+padSize : end] 307 return true 308 } 309 310 // extractPadding extracts the padding from raw message. 311 // although we don't support sending messages with padding size 312 // exceeding 255 bytes, such messages are perfectly valid, and 313 // can be successfully decrypted. 314 func (msg *ReceivedMessage) extractPadding(end int) (int, bool) { 315 paddingSize := 0 316 sz := int(msg.Raw[0] & paddingMask) // number of bytes indicating the entire size of padding (including these bytes) 317 // could be zero -- it means no padding 318 if sz != 0 { 319 paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz])) 320 if paddingSize < sz || paddingSize+1 > end { 321 return 0, false 322 } 323 msg.Padding = msg.Raw[1+sz : 1+paddingSize] 324 } 325 return paddingSize, true 326 } 327 328 // Recover retrieves the public key of the message signer. 329 func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { 330 defer func() { recover() }() // in case of invalid signature 331 332 pub, err := crypto.SigToPub(msg.hash(), msg.Signature) 333 if err != nil { 334 log.Error("failed to recover public key from signature", "err", err) 335 return nil 336 } 337 return pub 338 } 339 340 // hash calculates the SHA3 checksum of the message flags, payload and padding. 341 func (msg *ReceivedMessage) hash() []byte { 342 if isMessageSigned(msg.Raw[0]) { 343 sz := len(msg.Raw) - signatureLength 344 return crypto.Keccak256(msg.Raw[:sz]) 345 } 346 return crypto.Keccak256(msg.Raw) 347 }