message.go 5.09 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Copyright 2014 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum 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.
//
// go-ethereum 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 go-ethereum.  If not, see <http://www.gnu.org/licenses/>.

17 18 19
// 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.

20 21
package whisper

22
import (
23
	"crypto/ecdsa"
24
	"math/rand"
25 26
	"time"

27
	"github.com/ethereum/go-ethereum/common"
28
	"github.com/ethereum/go-ethereum/crypto"
29
	"github.com/ethereum/go-ethereum/logger"
30
	"github.com/ethereum/go-ethereum/logger/glog"
31 32
)

33
// Message represents an end-user data packet to transmit through the Whisper
34 35
// protocol. These are wrapped into Envelopes that need not be understood by
// intermediate nodes, just forwarded.
36
type Message struct {
37
	Flags     byte // First bit is signature presence, rest reserved and should be random
38 39
	Signature []byte
	Payload   []byte
40 41 42

	Sent time.Time     // Time when the message was posted into the network
	TTL  time.Duration // Maximum time to live allowed for the message
obscuren's avatar
obscuren committed
43

44
	To   *ecdsa.PublicKey // Message recipient (identity used to decode the message)
45
	Hash common.Hash      // Message envelope hash to act as a unique id
46 47
}

48 49 50 51 52
// 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
53
	Topics []Topic
54 55 56
}

// NewMessage creates and initializes a non-signed, non-encrypted Whisper message.
57
func NewMessage(payload []byte) *Message {
58 59 60
	// Construct an initial flag set: no signature, rest random
	flags := byte(rand.Intn(256))
	flags &= ^signatureFlag
61 62 63 64 65

	// Assemble and return the message
	return &Message{
		Flags:   flags,
		Payload: payload,
66
		Sent:    time.Now(),
67
	}
68 69
}

70 71
// Wrap bundles the message into an Envelope to transmit over the network.
//
72
// pow (Proof Of Work) controls how much time to spend on hashing the message,
73 74 75 76 77 78 79 80 81 82 83 84
// 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 {
85
		options.TTL = DefaultTTL
86
	}
87 88
	self.TTL = options.TTL

89 90 91 92 93 94 95 96 97 98 99 100
	// 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
101
	envelope := NewEnvelope(options.TTL, options.Topics, self)
102 103 104
	envelope.Seal(pow)

	return envelope, nil
105 106
}

107
// sign calculates and sets the cryptographic signature for the message , also
108
// setting the sign flag.
109
func (self *Message) sign(key *ecdsa.PrivateKey) (err error) {
110
	self.Flags |= signatureFlag
111 112 113 114
	self.Signature, err = crypto.Sign(self.hash(), key)
	return
}

115
// Recover retrieves the public key of the message signer.
116
func (self *Message) Recover() *ecdsa.PublicKey {
117 118
	defer func() { recover() }() // in case of invalid signature

119 120 121 122 123
	// Short circuit if no signature is present
	if self.Signature == nil {
		return nil
	}
	// Otherwise try and recover the signature
124 125
	pub, err := crypto.SigToPub(self.hash(), self.Signature)
	if err != nil {
126
		glog.V(logger.Error).Infof("Could not get public key from signature: %v", err)
127 128 129
		return nil
	}
	return pub
130 131
}

132
// encrypt encrypts a message payload with a public key.
133 134 135 136 137 138
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.
139 140 141 142 143 144
func (self *Message) decrypt(key *ecdsa.PrivateKey) error {
	cleartext, err := crypto.Decrypt(key, self.Payload)
	if err == nil {
		self.Payload = cleartext
	}
	return err
145
}
146

147
// hash calculates the SHA3 checksum of the message flags and payload.
148 149
func (self *Message) hash() []byte {
	return crypto.Sha3(append([]byte{self.Flags}, self.Payload...))
150 151
}

152
// bytes flattens the message contents (flags, signature and payload) into a
153 154 155
// single binary blob.
func (self *Message) bytes() []byte {
	return append([]byte{self.Flags}, append(self.Signature, self.Payload...)...)
156
}