github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv2/message.go (about) 1 // Copyright 2014 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 Message element. For formal details please see 18 // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#messages. 19 20 package whisperv2 21 22 import ( 23 "crypto/ecdsa" 24 crand "crypto/rand" 25 "fmt" 26 "math/rand" 27 "time" 28 29 "github.com/SmartMeshFoundation/Spectrum/common" 30 "github.com/SmartMeshFoundation/Spectrum/crypto" 31 "github.com/SmartMeshFoundation/Spectrum/crypto/ecies" 32 "github.com/SmartMeshFoundation/Spectrum/log" 33 ) 34 35 // Message represents an end-user data packet to transmit through the Whisper 36 // protocol. These are wrapped into Envelopes that need not be understood by 37 // intermediate nodes, just forwarded. 38 type Message struct { 39 Flags byte // First bit is signature presence, rest reserved and should be random 40 Signature []byte 41 Payload []byte 42 43 Sent time.Time // Time when the message was posted into the network 44 TTL time.Duration // Maximum time to live allowed for the message 45 46 To *ecdsa.PublicKey // Message recipient (identity used to decode the message) 47 Hash common.Hash // Message envelope hash to act as a unique id 48 } 49 50 // Options specifies the exact way a message should be wrapped into an Envelope. 51 type Options struct { 52 From *ecdsa.PrivateKey 53 To *ecdsa.PublicKey 54 TTL time.Duration 55 Topics []Topic 56 } 57 58 // NewMessage creates and initializes a non-signed, non-encrypted Whisper message. 59 func NewMessage(payload []byte) *Message { 60 // Construct an initial flag set: no signature, rest random 61 flags := byte(rand.Intn(256)) 62 flags &= ^signatureFlag 63 64 // Assemble and return the message 65 return &Message{ 66 Flags: flags, 67 Payload: payload, 68 Sent: time.Now(), 69 } 70 } 71 72 // Wrap bundles the message into an Envelope to transmit over the network. 73 // 74 // pow (Proof Of Work) controls how much time to spend on hashing the message, 75 // inherently controlling its priority through the network (smaller hash, bigger 76 // priority). 77 // 78 // The user can control the amount of identity, privacy and encryption through 79 // the options parameter as follows: 80 // - options.From == nil && options.To == nil: anonymous broadcast 81 // - options.From != nil && options.To == nil: signed broadcast (known sender) 82 // - options.From == nil && options.To != nil: encrypted anonymous message 83 // - options.From != nil && options.To != nil: encrypted signed message 84 func (self *Message) Wrap(pow time.Duration, options Options) (*Envelope, error) { 85 // Use the default TTL if non was specified 86 if options.TTL == 0 { 87 options.TTL = DefaultTTL 88 } 89 self.TTL = options.TTL 90 91 // Sign and encrypt the message if requested 92 if options.From != nil { 93 if err := self.sign(options.From); err != nil { 94 return nil, err 95 } 96 } 97 if options.To != nil { 98 if err := self.encrypt(options.To); err != nil { 99 return nil, err 100 } 101 } 102 // Wrap the processed message, seal it and return 103 envelope := NewEnvelope(options.TTL, options.Topics, self) 104 envelope.Seal(pow) 105 106 return envelope, nil 107 } 108 109 // sign calculates and sets the cryptographic signature for the message , also 110 // setting the sign flag. 111 func (self *Message) sign(key *ecdsa.PrivateKey) (err error) { 112 self.Flags |= signatureFlag 113 self.Signature, err = crypto.Sign(self.hash(), key) 114 return 115 } 116 117 // Recover retrieves the public key of the message signer. 118 func (self *Message) Recover() *ecdsa.PublicKey { 119 defer func() { recover() }() // in case of invalid signature 120 121 // Short circuit if no signature is present 122 if self.Signature == nil { 123 return nil 124 } 125 // Otherwise try and recover the signature 126 pub, err := crypto.SigToPub(self.hash(), self.Signature) 127 if err != nil { 128 log.Error(fmt.Sprintf("Could not get public key from signature: %v", err)) 129 return nil 130 } 131 return pub 132 } 133 134 // encrypt encrypts a message payload with a public key. 135 func (self *Message) encrypt(key *ecdsa.PublicKey) (err error) { 136 self.Payload, err = ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), self.Payload, nil, nil) 137 return 138 } 139 140 // decrypt decrypts an encrypted payload with a private key. 141 func (self *Message) decrypt(key *ecdsa.PrivateKey) error { 142 cleartext, err := ecies.ImportECDSA(key).Decrypt(crand.Reader, self.Payload, nil, nil) 143 if err == nil { 144 self.Payload = cleartext 145 } 146 return err 147 } 148 149 // hash calculates the SHA3 checksum of the message flags and payload. 150 func (self *Message) hash() []byte { 151 return crypto.Keccak256(append([]byte{self.Flags}, self.Payload...)) 152 } 153 154 // bytes flattens the message contents (flags, signature and payload) into a 155 // single binary blob. 156 func (self *Message) bytes() []byte { 157 return append([]byte{self.Flags}, append(self.Signature, self.Payload...)...) 158 }