// 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 . // 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/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.Sha3(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...)...) }