diff options
Diffstat (limited to 'whisper/whisperv2/message.go')
-rw-r--r-- | whisper/whisperv2/message.go | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/whisper/whisperv2/message.go b/whisper/whisperv2/message.go new file mode 100644 index 000000000..7ef9d0912 --- /dev/null +++ b/whisper/whisperv2/message.go @@ -0,0 +1,156 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// 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 whisperv2 + +import ( + "crypto/ecdsa" + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" +) + +// Message represents an end-user data packet to transmit through the Whisper +// protocol. These are wrapped into Envelopes that need not be understood by +// intermediate nodes, just forwarded. +type Message struct { + Flags byte // First bit is signature presence, rest reserved and should be random + Signature []byte + Payload []byte + + Sent time.Time // Time when the message was posted into the network + TTL time.Duration // Maximum time to live allowed for the message + + To *ecdsa.PublicKey // Message recipient (identity used to decode the message) + Hash common.Hash // Message envelope hash to act as a unique id +} + +// 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 []Topic +} + +// NewMessage creates and initializes a non-signed, non-encrypted Whisper message. +func NewMessage(payload []byte) *Message { + // Construct an initial flag set: no signature, rest random + flags := byte(rand.Intn(256)) + flags &= ^signatureFlag + + // Assemble and return the message + return &Message{ + Flags: flags, + Payload: payload, + Sent: time.Now(), + } +} + +// 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 = DefaultTTL + } + self.TTL = options.TTL + + // 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 |= signatureFlag + 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 signature + + // Short circuit if no signature is present + if self.Signature == nil { + return nil + } + // Otherwise try and recover the signature + pub, err := crypto.SigToPub(self.hash(), self.Signature) + if err != nil { + glog.V(logger.Error).Infof("Could not get public key from signature: %v", err) + return nil + } + return pub +} + +// encrypt encrypts a message payload with a public key. +func (self *Message) encrypt(key *ecdsa.PublicKey) (err error) { + self.Payload, err = crypto.Encrypt(key, self.Payload) + return +} + +// decrypt decrypts an encrypted payload with a private key. +func (self *Message) decrypt(key *ecdsa.PrivateKey) error { + cleartext, err := crypto.Decrypt(key, self.Payload) + if err == nil { + self.Payload = cleartext + } + return err +} + +// hash calculates the SHA3 checksum of the message flags and payload. +func (self *Message) hash() []byte { + return crypto.Keccak256(append([]byte{self.Flags}, self.Payload...)) +} + +// 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...)...) +} |