github.com/status-im/status-go@v1.1.0/waku/common/message.go (about) 1 // Copyright 2019 The Waku Library Authors. 2 // 3 // The Waku library is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Lesser General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // The Waku library is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty off 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Lesser General Public License for more details. 12 // 13 // You should have received a copy of the GNU Lesser General Public License 14 // along with the Waku library. If not, see <http://www.gnu.org/licenses/>. 15 // 16 // This software uses the go-ethereum library, which is licensed 17 // under the GNU Lesser General Public Library, version 3 or any later. 18 19 package common 20 21 import ( 22 "crypto/aes" 23 "crypto/cipher" 24 "crypto/ecdsa" 25 crand "crypto/rand" 26 "encoding/binary" 27 "errors" 28 "fmt" 29 "strconv" 30 "sync" 31 "time" 32 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/crypto" 35 "github.com/ethereum/go-ethereum/crypto/ecies" 36 "github.com/ethereum/go-ethereum/log" 37 ) 38 39 // MessageParams specifies the exact way a message should be wrapped 40 // into an Envelope. 41 type MessageParams struct { 42 TTL uint32 43 Src *ecdsa.PrivateKey 44 Dst *ecdsa.PublicKey 45 KeySym []byte 46 Topic TopicType 47 WorkTime uint32 48 PoW float64 49 Payload []byte 50 Padding []byte 51 } 52 53 // SentMessage represents an end-user data packet to transmit through the 54 // Waku protocol. These are wrapped into Envelopes that need not be 55 // understood by intermediate nodes, just forwarded. 56 type SentMessage struct { 57 Raw []byte 58 } 59 60 // ReceivedMessage represents a data packet to be received through the 61 // Waku protocol and successfully decrypted. 62 type ReceivedMessage struct { 63 Raw []byte 64 65 Payload []byte 66 Padding []byte 67 Signature []byte 68 Salt []byte 69 70 PoW float64 // Proof of work as described in the Waku spec 71 Sent uint32 // Time when the message was posted into the network 72 TTL uint32 // Maximum time to live allowed for the message 73 Src *ecdsa.PublicKey // Message recipient (identity used to decode the message) 74 Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message) 75 Topic TopicType 76 77 SymKeyHash common.Hash // The Keccak256Hash of the key 78 EnvelopeHash common.Hash // Message envelope hash to act as a unique id 79 80 P2P bool // is set to true if this message was received from mail server. 81 } 82 83 // MessagesRequest contains details of a request for historic messages. 84 type MessagesRequest struct { 85 // ID of the request. The current implementation requires ID to be 32-byte array, 86 // however, it's not enforced for future implementation. 87 ID []byte `json:"id"` 88 89 // From is a lower bound of time range. 90 From uint32 `json:"from"` 91 92 // To is a upper bound of time range. 93 To uint32 `json:"to"` 94 95 // Limit determines the number of messages sent by the mail server 96 // for the current paginated request. 97 Limit uint32 `json:"limit"` 98 99 // Cursor is used as starting point for paginated requests. 100 Cursor []byte `json:"cursor"` 101 102 // Bloom is a filter to match requested messages. 103 Bloom []byte `json:"bloom"` 104 105 // Topics is a list of topics. A returned message should 106 // belong to one of the topics from the list. 107 Topics [][]byte `json:"topics"` 108 } 109 110 func (r MessagesRequest) Validate() error { 111 if len(r.ID) != common.HashLength { 112 return errors.New("invalid 'ID', expected a 32-byte slice") 113 } 114 115 if r.From > r.To { 116 return errors.New("invalid 'From' value which is greater than To") 117 } 118 119 if r.Limit > MaxLimitInMessagesRequest { 120 return fmt.Errorf("invalid 'Limit' value, expected value lower than %d", MaxLimitInMessagesRequest) 121 } 122 123 if len(r.Bloom) == 0 && len(r.Topics) == 0 { 124 return errors.New("invalid 'Bloom' or 'Topics', one must be non-empty") 125 } 126 127 return nil 128 } 129 130 // MessagesResponse sent as a response after processing batch of envelopes. 131 type MessagesResponse struct { 132 // Hash is a hash of all envelopes sent in the single batch. 133 Hash common.Hash 134 // Per envelope error. 135 Errors []EnvelopeError 136 } 137 138 func IsMessageSigned(flags byte) bool { 139 return (flags & signatureFlag) != 0 140 } 141 142 func (msg *ReceivedMessage) isSymmetricEncryption() bool { 143 return msg.SymKeyHash != common.Hash{} 144 } 145 146 func (msg *ReceivedMessage) isAsymmetricEncryption() bool { 147 return msg.Dst != nil 148 } 149 150 // NewSentMessage creates and initializes a non-signed, non-encrypted Waku message. 151 func NewSentMessage(params *MessageParams) (*SentMessage, error) { 152 const payloadSizeFieldMaxSize = 4 153 msg := SentMessage{} 154 msg.Raw = make([]byte, 1, 155 flagsLength+payloadSizeFieldMaxSize+len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit) 156 msg.Raw[0] = 0 // set all the flags to zero 157 msg.addPayloadSizeField(params.Payload) 158 msg.Raw = append(msg.Raw, params.Payload...) 159 err := msg.appendPadding(params) 160 return &msg, err 161 } 162 163 // addPayloadSizeField appends the auxiliary field containing the size of payload 164 func (msg *SentMessage) addPayloadSizeField(payload []byte) { 165 fieldSize := getSizeOfPayloadSizeField(payload) 166 field := make([]byte, 4) 167 binary.LittleEndian.PutUint32(field, uint32(len(payload))) 168 field = field[:fieldSize] 169 msg.Raw = append(msg.Raw, field...) 170 msg.Raw[0] |= byte(fieldSize) 171 } 172 173 // getSizeOfPayloadSizeField returns the number of bytes necessary to encode the size of payload 174 func getSizeOfPayloadSizeField(payload []byte) int { 175 s := 1 176 for i := len(payload); i >= 256; i /= 256 { 177 s++ 178 } 179 return s 180 } 181 182 // appendPadding appends the padding specified in params. 183 // If no padding is provided in params, then random padding is generated. 184 func (msg *SentMessage) appendPadding(params *MessageParams) error { 185 if len(params.Padding) != 0 { 186 // padding data was provided by the Dapp, just use it as is 187 msg.Raw = append(msg.Raw, params.Padding...) 188 return nil 189 } 190 191 rawSize := flagsLength + getSizeOfPayloadSizeField(params.Payload) + len(params.Payload) 192 if params.Src != nil { 193 rawSize += signatureLength 194 } 195 odd := rawSize % padSizeLimit 196 paddingSize := padSizeLimit - odd 197 pad := make([]byte, paddingSize) 198 _, err := crand.Read(pad) 199 if err != nil { 200 return err 201 } 202 if !ValidateDataIntegrity(pad, paddingSize) { 203 return errors.New("failed to generate random padding of size " + strconv.Itoa(paddingSize)) 204 } 205 msg.Raw = append(msg.Raw, pad...) 206 return nil 207 } 208 209 // sign calculates and sets the cryptographic signature for the message, 210 // also setting the sign flag. 211 func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error { 212 if IsMessageSigned(msg.Raw[0]) { 213 // this should not happen, but no reason to panic 214 log.Error("failed to sign the message: already signed") 215 return nil 216 } 217 218 msg.Raw[0] |= signatureFlag // it is important to set this flag before signing 219 hash := crypto.Keccak256(msg.Raw) 220 signature, err := crypto.Sign(hash, key) 221 if err != nil { 222 msg.Raw[0] &= 0xFF ^ signatureFlag // clear the flag 223 return err 224 } 225 msg.Raw = append(msg.Raw, signature...) 226 return nil 227 } 228 229 // encryptAsymmetric encrypts a message with a public key. 230 func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { 231 if !ValidatePublicKey(key) { 232 return errors.New("invalid public key provided for asymmetric encryption") 233 } 234 encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil) 235 if err == nil { 236 msg.Raw = encrypted 237 } 238 return err 239 } 240 241 // encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. 242 // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). 243 func (msg *SentMessage) encryptSymmetric(key []byte) (err error) { 244 if !ValidateDataIntegrity(key, AESKeyLength) { 245 return errors.New("invalid key provided for symmetric encryption, size: " + strconv.Itoa(len(key))) 246 } 247 block, err := aes.NewCipher(key) 248 if err != nil { 249 return err 250 } 251 aesgcm, err := cipher.NewGCM(block) 252 if err != nil { 253 return err 254 } 255 salt, err := GenerateSecureRandomData(aesNonceLength) // never use more than 2^32 random nonces with a given key 256 if err != nil { 257 return err 258 } 259 encrypted := aesgcm.Seal(nil, salt, msg.Raw, nil) 260 msg.Raw = append(encrypted, salt...) 261 return nil 262 } 263 264 // Wrap bundles the message into an Envelope to transmit over the network. 265 func (msg *SentMessage) Wrap(options *MessageParams, now time.Time) (envelope *Envelope, err error) { 266 if options.TTL == 0 { 267 options.TTL = DefaultTTL 268 } 269 if options.Src != nil { 270 if err = msg.sign(options.Src); err != nil { 271 return nil, err 272 } 273 } 274 if options.Dst != nil { 275 err = msg.encryptAsymmetric(options.Dst) 276 } else if options.KeySym != nil { 277 err = msg.encryptSymmetric(options.KeySym) 278 } else { 279 err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") 280 } 281 if err != nil { 282 return nil, err 283 } 284 285 envelope = NewEnvelope(options.TTL, options.Topic, msg, now) 286 if err = envelope.Seal(options); err != nil { 287 return nil, err 288 } 289 return envelope, nil 290 } 291 292 // decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. 293 // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). 294 func (msg *ReceivedMessage) decryptSymmetric(key []byte) error { 295 // symmetric messages are expected to contain the 12-byte nonce at the end of the payload 296 if len(msg.Raw) < aesNonceLength { 297 return errors.New("missing salt or invalid payload in symmetric message") 298 } 299 salt := msg.Raw[len(msg.Raw)-aesNonceLength:] 300 301 block, err := aes.NewCipher(key) 302 if err != nil { 303 return err 304 } 305 aesgcm, err := cipher.NewGCM(block) 306 if err != nil { 307 return err 308 } 309 decrypted, err := aesgcm.Open(nil, salt, msg.Raw[:len(msg.Raw)-aesNonceLength], nil) 310 if err != nil { 311 return err 312 } 313 msg.Raw = decrypted 314 msg.Salt = salt 315 return nil 316 } 317 318 // decryptAsymmetric decrypts an encrypted payload with a private key. 319 func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { 320 decrypted, err := ecies.ImportECDSA(key).Decrypt(msg.Raw, nil, nil) 321 if err == nil { 322 msg.Raw = decrypted 323 } 324 return err 325 } 326 327 // ValidateAndParse checks the message validity and extracts the fields in case of success. 328 func (msg *ReceivedMessage) ValidateAndParse() bool { 329 end := len(msg.Raw) 330 if end < 1 { 331 return false 332 } 333 334 if IsMessageSigned(msg.Raw[0]) { 335 end -= signatureLength 336 if end <= 1 { 337 return false 338 } 339 msg.Signature = msg.Raw[end : end+signatureLength] 340 msg.Src = msg.SigToPubKey() 341 if msg.Src == nil { 342 return false 343 } 344 } 345 346 beg := 1 347 payloadSize := 0 348 sizeOfPayloadSizeField := int(msg.Raw[0] & SizeMask) // number of bytes indicating the size of payload 349 if sizeOfPayloadSizeField != 0 { 350 if end < beg+sizeOfPayloadSizeField { 351 return false 352 } 353 payloadSize = int(BytesToUintLittleEndian(msg.Raw[beg : beg+sizeOfPayloadSizeField])) 354 beg += sizeOfPayloadSizeField 355 if beg+payloadSize > end { 356 return false 357 } 358 msg.Payload = msg.Raw[beg : beg+payloadSize] 359 } 360 361 beg += payloadSize 362 msg.Padding = msg.Raw[beg:end] 363 return true 364 } 365 366 // SigToPubKey returns the public key associated to the message's 367 // signature. 368 func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { 369 // in case of invalid signature 370 defer func() { recover() }() // nolint: errcheck 371 372 pub, err := crypto.SigToPub(msg.hash(), msg.Signature) 373 if err != nil { 374 log.Error("failed to recover public key from signature", "err", err) 375 return nil 376 } 377 return pub 378 } 379 380 // hash calculates the SHA3 checksum of the message flags, payload size field, payload and padding. 381 func (msg *ReceivedMessage) hash() []byte { 382 if IsMessageSigned(msg.Raw[0]) { 383 sz := len(msg.Raw) - signatureLength 384 return crypto.Keccak256(msg.Raw[:sz]) 385 } 386 return crypto.Keccak256(msg.Raw) 387 } 388 389 // MessageStore defines interface for temporary message store. 390 type MessageStore interface { 391 Add(*ReceivedMessage) error 392 Pop() ([]*ReceivedMessage, error) 393 } 394 395 // NewMemoryMessageStore returns pointer to an instance of the MemoryMessageStore. 396 func NewMemoryMessageStore() *MemoryMessageStore { 397 return &MemoryMessageStore{ 398 messages: map[common.Hash]*ReceivedMessage{}, 399 } 400 } 401 402 // MemoryMessageStore represents messages stored in a memory hash table. 403 type MemoryMessageStore struct { 404 mu sync.Mutex 405 messages map[common.Hash]*ReceivedMessage 406 } 407 408 // Add adds message to store. 409 func (store *MemoryMessageStore) Add(msg *ReceivedMessage) error { 410 store.mu.Lock() 411 defer store.mu.Unlock() 412 if _, exist := store.messages[msg.EnvelopeHash]; !exist { 413 store.messages[msg.EnvelopeHash] = msg 414 } 415 return nil 416 } 417 418 // Pop returns all available messages and cleans the store. 419 func (store *MemoryMessageStore) Pop() ([]*ReceivedMessage, error) { 420 store.mu.Lock() 421 defer store.mu.Unlock() 422 all := make([]*ReceivedMessage, 0, len(store.messages)) 423 for hash, msg := range store.messages { 424 delete(store.messages, hash) 425 all = append(all, msg) 426 } 427 return all, nil 428 }