diff options
Diffstat (limited to 'whisper/message.go')
-rw-r--r-- | whisper/message.go | 121 |
1 files changed, 76 insertions, 45 deletions
diff --git a/whisper/message.go b/whisper/message.go index ad6a1bcff..2666ee6e0 100644 --- a/whisper/message.go +++ b/whisper/message.go @@ -1,7 +1,11 @@ +// Contains the Whisper protocol Message element. For formal details please see +// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#messages. + package whisper import ( "crypto/ecdsa" + "math/rand" "time" "github.com/ethereum/go-ethereum/crypto" @@ -9,8 +13,11 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" ) +// Message represents an end-user data packet to trasmit through the Whisper +// protocol. These are wrapped into Envelopes that need not be understood by +// intermediate nodes, just forwarded. type Message struct { - Flags byte + Flags byte // First bit is signature presence, rest reserved and should be random Signature []byte Payload []byte Sent int64 @@ -18,71 +25,95 @@ type Message struct { To *ecdsa.PublicKey } +// Options specifies the exact way a message should be wrapped into an Envelope. +type Options struct { + From *ecdsa.PrivateKey + To *ecdsa.PublicKey + TTL time.Duration + Topics [][]byte +} + +// NewMessage creates and initializes a non-signed, non-encrypted Whisper message. func NewMessage(payload []byte) *Message { - return &Message{Flags: 0, Payload: payload, Sent: time.Now().Unix()} + // Construct an initial flag set: bit #1 = 0 (no signature), rest random + flags := byte(rand.Intn(128)) + + // Assemble and return the message + return &Message{ + Flags: flags, + Payload: payload, + Sent: time.Now().Unix(), + } } -func (self *Message) hash() []byte { - return crypto.Sha3(append([]byte{self.Flags}, self.Payload...)) +// Wrap bundles the message into an Envelope to transmit over the network. +// +// pow (Proof Of Work) controls how much time to spend on hashing the message, +// inherently controlling its priority through the network (smaller hash, bigger +// priority). +// +// The user can control the amount of identity, privacy and encryption through +// the options parameter as follows: +// - options.From == nil && options.To == nil: anonymous broadcast +// - options.From != nil && options.To == nil: signed broadcast (known sender) +// - options.From == nil && options.To != nil: encrypted anonymous message +// - options.From != nil && options.To != nil: encrypted signed message +func (self *Message) Wrap(pow time.Duration, options Options) (*Envelope, error) { + // Use the default TTL if non was specified + if options.TTL == 0 { + options.TTL = DefaultTimeToLive + } + // Sign and encrypt the message if requested + if options.From != nil { + if err := self.sign(options.From); err != nil { + return nil, err + } + } + if options.To != nil { + if err := self.encrypt(options.To); err != nil { + return nil, err + } + } + // Wrap the processed message, seal it and return + envelope := NewEnvelope(options.TTL, options.Topics, self) + envelope.Seal(pow) + + return envelope, nil } +// sign calculates and sets the cryptographic signature for the message , also +// setting the sign flag. func (self *Message) sign(key *ecdsa.PrivateKey) (err error) { - self.Flags = 1 + self.Flags |= 1 << 7 self.Signature, err = crypto.Sign(self.hash(), key) return } +// Recover retrieves the public key of the message signer. func (self *Message) Recover() *ecdsa.PublicKey { - defer func() { recover() }() // in case of invalid sig + defer func() { recover() }() // in case of invalid signature + pub, err := crypto.SigToPub(self.hash(), self.Signature) if err != nil { - glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err) + glog.V(logger.Error).Infof("Could not get public key from signature: %v", err) return nil } return pub } -func (self *Message) Encrypt(to *ecdsa.PublicKey) (err error) { +// encrypt encrypts a message payload with a public key. +func (self *Message) encrypt(to *ecdsa.PublicKey) (err error) { self.Payload, err = crypto.Encrypt(to, self.Payload) - if err != nil { - return err - } - - return nil -} - -func (self *Message) Bytes() []byte { - return append([]byte{self.Flags}, append(self.Signature, self.Payload...)...) + return } -type Opts struct { - From *ecdsa.PrivateKey - To *ecdsa.PublicKey - Ttl time.Duration - Topics [][]byte +// hash calculates the SHA3 checksum of the message flags and payload. +func (self *Message) hash() []byte { + return crypto.Sha3(append([]byte{self.Flags}, self.Payload...)) } -func (self *Message) Seal(pow time.Duration, opts Opts) (*Envelope, error) { - if opts.From != nil { - err := self.sign(opts.From) - if err != nil { - return nil, err - } - } - - if opts.To != nil { - err := self.Encrypt(opts.To) - if err != nil { - return nil, err - } - } - - if opts.Ttl == 0 { - opts.Ttl = DefaultTtl - } - - envelope := NewEnvelope(opts.Ttl, opts.Topics, self) - envelope.Seal(pow) - - return envelope, nil +// bytes flattens the message contents (flags, signature and payload) into a +// single binary blob. +func (self *Message) bytes() []byte { + return append([]byte{self.Flags}, append(self.Signature, self.Payload...)...) } |