From a4c8e947b0f4a07e851f7c8b420874ac411fdb69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 10 Apr 2015 13:42:49 +0300 Subject: [PATCH 1/6] whisper: make the test app runnable & do something inside --- whisper/main.go | 83 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/whisper/main.go b/whisper/main.go index 9f35dbb8d0..643644ee6c 100644 --- a/whisper/main.go +++ b/whisper/main.go @@ -1,37 +1,90 @@ // +build none +// Contains a simple whisper peer setup and self messaging to allow playing +// around with the protocol and API without a fancy client implementation. + package main import ( "fmt" "log" "os" + "time" - "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/whisper" ) func main() { logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel)) - pub, _ := secp256k1.GenerateKeyPair() - - whisper := whisper.New() - - srv := p2p.Server{ - MaxPeers: 10, - Identity: p2p.NewSimpleClientIdentity("whisper-go", "1.0", "", string(pub)), - ListenAddr: ":30300", - NAT: p2p.UPNP(), - - Protocols: []p2p.Protocol{whisper.Protocol()}, + // Generate the peer identity + key, err := crypto.GenerateKey() + if err != nil { + fmt.Printf("Failed to generate peer key: %v.\n", err) + os.Exit(-1) } - if err := srv.Start(); err != nil { - fmt.Println("could not start server:", err) + name := common.MakeName("whisper-go", "1.0") + shh := whisper.New() + + // Create an Ethereum peer to communicate through + server := p2p.Server{ + PrivateKey: key, + MaxPeers: 10, + Name: name, + Protocols: []p2p.Protocol{shh.Protocol()}, + ListenAddr: ":30300", + NAT: nat.Any(), + } + fmt.Println("Starting Ethereum peer...") + if err := server.Start(); err != nil { + fmt.Printf("Failed to start Ethereum peer: %v.\n", err) os.Exit(1) } - select {} + // Send a message to self to check that something works + payload := fmt.Sprintf("Hello world, this is %v. In case you're wondering, the time is %v", name, time.Now()) + if err := selfSend(shh, []byte(payload)); err != nil { + fmt.Printf("Failed to self message: %v.\n", err) + os.Exit(-1) + } +} + +// SendSelf wraps a payload into a Whisper envelope and forwards it to itself. +func selfSend(shh *whisper.Whisper, payload []byte) error { + ok := make(chan struct{}) + + // Start watching for self messages, output any arrivals + id := shh.NewIdentity() + shh.Watch(whisper.Filter{ + To: &id.PublicKey, + Fn: func(msg *whisper.Message) { + fmt.Printf("Message received: %s, signed with 0x%x.\n", string(msg.Payload), msg.Signature) + close(ok) + }, + }) + // Wrap the payload and encrypt it + msg := whisper.NewMessage(payload) + envelope, err := msg.Seal(whisper.DefaultPow, whisper.Opts{ + Ttl: whisper.DefaultTtl, + From: id, + To: &id.PublicKey, + }) + if err != nil { + return fmt.Errorf("failed to seal message: %v", err) + } + // Dump the message into the system and wait for it to pop back out + if err := shh.Send(envelope); err != nil { + return fmt.Errorf("failed to send self-message: %v", err) + } + select { + case <-ok: + case <-time.After(time.Second): + return fmt.Errorf("failed to receive message in time") + } + return nil } From 7e54a9c07f10ff16aabfe8bd3944c9309d4efc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 10 Apr 2015 13:46:08 +0300 Subject: [PATCH 2/6] whisper: rename test file according to Go style --- whisper/{messages_test.go => message_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename whisper/{messages_test.go => message_test.go} (100%) diff --git a/whisper/messages_test.go b/whisper/message_test.go similarity index 100% rename from whisper/messages_test.go rename to whisper/message_test.go From 7d8ce53eca90a6e93e49d584a2cff5e39aea40c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 10 Apr 2015 15:53:21 +0300 Subject: [PATCH 3/6] whisper: polish the messages, fix some bugs, tests Bugs fixed: - Use randomly generated flags as the spec required. - During envelope opening check the first bit only for signature. --- whisper/envelope.go | 34 +++++----- whisper/main.go | 4 +- whisper/message.go | 123 ++++++++++++++++++++++-------------- whisper/message_test.go | 136 +++++++++++++++++++++++++++++++++------- whisper/whisper.go | 4 +- whisper/whisper_test.go | 4 +- 6 files changed, 213 insertions(+), 92 deletions(-) diff --git a/whisper/envelope.go b/whisper/envelope.go index 20e3e6d395..65dc899362 100644 --- a/whisper/envelope.go +++ b/whisper/envelope.go @@ -40,7 +40,7 @@ func NewEnvelope(ttl time.Duration, topics [][]byte, data *Message) *Envelope { Expiry: uint32(exp.Unix()), TTL: uint32(ttl.Seconds()), Topics: topics, - Data: data.Bytes(), + Data: data.bytes(), Nonce: 0, } } @@ -49,32 +49,32 @@ func (self *Envelope) Seal(pow time.Duration) { self.proveWork(pow) } -func (self *Envelope) Open(prv *ecdsa.PrivateKey) (msg *Message, err error) { +func (self *Envelope) Open(key *ecdsa.PrivateKey) (msg *Message, err error) { data := self.Data - var message Message - dataStart := 1 - if data[0] > 0 { - if len(data) < 66 { - return nil, fmt.Errorf("unable to open envelope. First bit set but len(data) < 66") - } - dataStart = 66 - message.Flags = data[0] - message.Signature = data[1:66] - } - payload := data[dataStart:] - if prv != nil { - message.Payload, err = crypto.Decrypt(prv, payload) + message := Message{ + Flags: data[0], + } + data = data[1:] + + if message.Flags&128 == 128 { + if len(data) < 65 { + return nil, fmt.Errorf("unable to open envelope. First bit set but len(data) < 65") + } + message.Signature, data = data[:65], data[65:] + } + message.Payload = data + + if key != nil { + message.Payload, err = crypto.Decrypt(key, message.Payload) switch err { case nil: // OK case ecies.ErrInvalidPublicKey: // Payload isn't encrypted - message.Payload = payload return &message, err default: return nil, fmt.Errorf("unable to open envelope. Decrypt failed: %v", err) } } - return &message, nil } diff --git a/whisper/main.go b/whisper/main.go index 643644ee6c..6a089f8940 100644 --- a/whisper/main.go +++ b/whisper/main.go @@ -69,10 +69,10 @@ func selfSend(shh *whisper.Whisper, payload []byte) error { }) // Wrap the payload and encrypt it msg := whisper.NewMessage(payload) - envelope, err := msg.Seal(whisper.DefaultPow, whisper.Opts{ - Ttl: whisper.DefaultTtl, + envelope, err := msg.Wrap(whisper.DefaultPow, whisper.Options{ From: id, To: &id.PublicKey, + TTL: whisper.DefaultTimeToLive, }) if err != nil { return fmt.Errorf("failed to seal message: %v", err) diff --git a/whisper/message.go b/whisper/message.go index ad6a1bcff6..3bee83f39f 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 it 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. +// +// Pov (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 + return } -func (self *Message) Bytes() []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...)) +} + +// 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...)...) } - -type Opts struct { - From *ecdsa.PrivateKey - To *ecdsa.PublicKey - Ttl time.Duration - Topics [][]byte -} - -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 -} diff --git a/whisper/message_test.go b/whisper/message_test.go index 93caa31b36..ed24d37d8d 100644 --- a/whisper/message_test.go +++ b/whisper/message_test.go @@ -3,48 +3,136 @@ package whisper import ( "bytes" "crypto/elliptic" - "fmt" "testing" "github.com/ethereum/go-ethereum/crypto" ) -func TestSign(t *testing.T) { - prv, _ := crypto.GenerateKey() - msg := NewMessage([]byte("hello world")) - msg.sign(prv) +// Tests whether a message can be wrapped without any identity or encryption. +func TestMessageSimpleWrap(t *testing.T) { + payload := []byte("hello world") + + msg := NewMessage(payload) + if _, err := msg.Wrap(DefaultPow, Options{}); err != nil { + t.Fatalf("failed to wrap message: %v", err) + } + if msg.Flags&128 != 0 { + t.Fatalf("signature flag mismatch: have %d, want %d", (msg.Flags&128)>>7, 0) + } + if len(msg.Signature) != 0 { + t.Fatalf("signature found for simple wrapping: 0x%x", msg.Signature) + } + if bytes.Compare(msg.Payload, payload) != 0 { + t.Fatalf("payload mismatch after wrapping: have 0x%x, want 0x%x", msg.Payload, payload) + } +} + +// Tests whether a message can be signed, and wrapped in plain-text. +func TestMessageCleartextSignRecover(t *testing.T) { + key, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("failed to create crypto key: %v", err) + } + payload := []byte("hello world") + + msg := NewMessage(payload) + if _, err := msg.Wrap(DefaultPow, Options{ + From: key, + }); err != nil { + t.Fatalf("failed to sign message: %v", err) + } + if msg.Flags&128 != 128 { + t.Fatalf("signature flag mismatch: have %d, want %d", (msg.Flags&128)>>7, 1) + } + if bytes.Compare(msg.Payload, payload) != 0 { + t.Fatalf("payload mismatch after signing: have 0x%x, want 0x%x", msg.Payload, payload) + } pubKey := msg.Recover() - p1 := elliptic.Marshal(crypto.S256(), prv.PublicKey.X, prv.PublicKey.Y) + if pubKey == nil { + t.Fatalf("failed to recover public key") + } + p1 := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y) p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) - if !bytes.Equal(p1, p2) { - t.Error("recovered pub key did not match") + t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) } } -func TestMessageEncryptDecrypt(t *testing.T) { - prv1, _ := crypto.GenerateKey() - prv2, _ := crypto.GenerateKey() +// Tests whether a message can be encrypted and decrypted using an anonymous +// sender (i.e. no signature). +func TestMessageAnonymousEncryptDecrypt(t *testing.T) { + key, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("failed to create recipient crypto key: %v", err) + } + payload := []byte("hello world") - data := []byte("hello world") - msg := NewMessage(data) - envelope, err := msg.Seal(DefaultPow, Opts{ - From: prv1, - To: &prv2.PublicKey, + msg := NewMessage(payload) + envelope, err := msg.Wrap(DefaultPow, Options{ + To: &key.PublicKey, }) if err != nil { - fmt.Println(err) - t.FailNow() + t.Fatalf("failed to encrypt message: %v", err) + } + if msg.Flags&128 != 0 { + t.Fatalf("signature flag mismatch: have %d, want %d", (msg.Flags&128)>>7, 0) + } + if len(msg.Signature) != 0 { + t.Fatalf("signature found for anonymous message: 0x%x", msg.Signature) } - msg1, err := envelope.Open(prv2) + out, err := envelope.Open(key) if err != nil { - t.Error(err) - t.FailNow() + t.Fatalf("failed to open encrypted message: %v", err) } - - if !bytes.Equal(msg1.Payload, data) { - t.Error("encryption error. data did not match") + if !bytes.Equal(out.Payload, payload) { + t.Error("payload mismatch: have 0x%x, want 0x%x", out.Payload, payload) + } +} + +// Tests whether a message can be properly signed and encrypted. +func TestMessageFullCrypto(t *testing.T) { + fromKey, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("failed to create sender crypto key: %v", err) + } + toKey, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("failed to create recipient crypto key: %v", err) + } + + payload := []byte("hello world") + msg := NewMessage(payload) + envelope, err := msg.Wrap(DefaultPow, Options{ + From: fromKey, + To: &toKey.PublicKey, + }) + if err != nil { + t.Fatalf("failed to encrypt message: %v", err) + } + if msg.Flags&128 != 128 { + t.Fatalf("signature flag mismatch: have %d, want %d", (msg.Flags&128)>>7, 1) + } + if len(msg.Signature) == 0 { + t.Fatalf("no signature found for signed message") + } + + out, err := envelope.Open(toKey) + if err != nil { + t.Fatalf("failed to open encrypted message: %v", err) + } + if !bytes.Equal(out.Payload, payload) { + t.Error("payload mismatch: have 0x%x, want 0x%x", out.Payload, payload) + } + + pubKey := out.Recover() + if pubKey == nil { + t.Fatalf("failed to recover public key") + } + p1 := elliptic.Marshal(crypto.S256(), fromKey.PublicKey.X, fromKey.PublicKey.Y) + p2 := elliptic.Marshal(crypto.S256(), pubKey.X, pubKey.Y) + if !bytes.Equal(p1, p2) { + t.Fatalf("public key mismatch: have 0x%x, want 0x%x", p2, p1) } } diff --git a/whisper/whisper.go b/whisper/whisper.go index 00dcb19321..3cb92a07a8 100644 --- a/whisper/whisper.go +++ b/whisper/whisper.go @@ -28,7 +28,9 @@ type MessageEvent struct { Message *Message } -const DefaultTtl = 50 * time.Second +const ( + DefaultTimeToLive = 50 * time.Second +) type Whisper struct { protocol p2p.Protocol diff --git a/whisper/whisper_test.go b/whisper/whisper_test.go index 3e3945a0a6..a3e0e03d2d 100644 --- a/whisper/whisper_test.go +++ b/whisper/whisper_test.go @@ -18,8 +18,8 @@ func TestEvent(t *testing.T) { }) msg := NewMessage([]byte(fmt.Sprintf("Hello world. This is whisper-go. Incase you're wondering; the time is %v", time.Now()))) - envelope, err := msg.Seal(DefaultPow, Opts{ - Ttl: DefaultTtl, + envelope, err := msg.Wrap(DefaultPow, Options{ + TTL: DefaultTimeToLive, From: id, To: &id.PublicKey, }) From 0e4f21fc376fae88b59cb33a06a1ddd36a11a592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 10 Apr 2015 16:52:01 +0300 Subject: [PATCH 4/6] whisper: polish the Envelope a bit, prep for tests. --- whisper/envelope.go | 123 +++++++++++++++++++++------------------- whisper/main.go | 2 +- whisper/message.go | 2 +- whisper/message_test.go | 8 +-- whisper/whisper.go | 3 +- whisper/whisper_test.go | 2 +- 6 files changed, 75 insertions(+), 65 deletions(-) diff --git a/whisper/envelope.go b/whisper/envelope.go index 65dc899362..6ad48dd250 100644 --- a/whisper/envelope.go +++ b/whisper/envelope.go @@ -1,3 +1,6 @@ +// Contains the Whisper protocol Envelope element. For formal details please see +// the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#envelopes. + package whisper import ( @@ -12,10 +15,8 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -const ( - DefaultPow = 50 * time.Millisecond -) - +// Envelope represents a clear-text data packet to transmit through the Whisper +// network. Its contents may or may not be encrypted and signed. type Envelope struct { Expiry uint32 // Whisper protocol specifies int32, really should be int64 TTL uint32 // ^^^^^^ @@ -26,33 +27,60 @@ type Envelope struct { hash common.Hash } -func (self *Envelope) Hash() common.Hash { - if (self.hash == common.Hash{}) { - enc, _ := rlp.EncodeToBytes(self) - self.hash = crypto.Sha3Hash(enc) - } - return self.hash -} - -func NewEnvelope(ttl time.Duration, topics [][]byte, data *Message) *Envelope { - exp := time.Now().Add(ttl) +// NewEnvelope wraps a Whisper message with expiration and destination data +// included into an envelope for network forwarding. +func NewEnvelope(ttl time.Duration, topics [][]byte, msg *Message) *Envelope { return &Envelope{ - Expiry: uint32(exp.Unix()), + Expiry: uint32(time.Now().Add(ttl).Unix()), TTL: uint32(ttl.Seconds()), Topics: topics, - Data: data.bytes(), + Data: msg.bytes(), Nonce: 0, } } +// Seal closes the envelope by spending the requested amount of time as a proof +// of work on hashing the data. func (self *Envelope) Seal(pow time.Duration) { - self.proveWork(pow) + d := make([]byte, 64) + copy(d[:32], self.rlpWithoutNonce()) + + finish, bestBit := time.Now().Add(pow).UnixNano(), 0 + for nonce := uint32(0); time.Now().UnixNano() < finish; { + for i := 0; i < 1024; i++ { + binary.BigEndian.PutUint32(d[60:], nonce) + + firstBit := common.FirstBitSet(common.BigD(crypto.Sha3(d))) + if firstBit > bestBit { + self.Nonce, bestBit = nonce, firstBit + } + nonce++ + } + } } +// Valid checks whether the claimed proof of work was indeed executed. +// TODO: Is this really useful? Isn't this always true? +func (self *Envelope) valid() bool { + d := make([]byte, 64) + copy(d[:32], self.rlpWithoutNonce()) + binary.BigEndian.PutUint32(d[60:], self.Nonce) + + return common.FirstBitSet(common.BigD(crypto.Sha3(d))) > 0 +} + +// RlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. +func (self *Envelope) rlpWithoutNonce() []byte { + enc, _ := rlp.EncodeToBytes([]interface{}{self.Expiry, self.TTL, self.Topics, self.Data}) + return enc +} + +// Open extracts the message contained within a potentially encrypted envelope. func (self *Envelope) Open(key *ecdsa.PrivateKey) (msg *Message, err error) { + // Split open the payload into a message construct data := self.Data - message := Message{ + message := &Message{ Flags: data[0], } data = data[1:] @@ -65,57 +93,38 @@ func (self *Envelope) Open(key *ecdsa.PrivateKey) (msg *Message, err error) { } message.Payload = data - if key != nil { - message.Payload, err = crypto.Decrypt(key, message.Payload) - switch err { - case nil: // OK - case ecies.ErrInvalidPublicKey: // Payload isn't encrypted - return &message, err - default: - return nil, fmt.Errorf("unable to open envelope. Decrypt failed: %v", err) - } + // Short circuit if the encryption was requested + if key == nil { + return message, nil } - return &message, nil -} + // Otherwise try to decrypt the message + message.Payload, err = crypto.Decrypt(key, message.Payload) + switch err { + case nil: + return message, nil -func (self *Envelope) proveWork(dura time.Duration) { - var bestBit int - d := make([]byte, 64) - enc, _ := rlp.EncodeToBytes(self.withoutNonce()) - copy(d[:32], enc) + case ecies.ErrInvalidPublicKey: // Payload isn't encrypted + return message, err - then := time.Now().Add(dura).UnixNano() - for n := uint32(0); time.Now().UnixNano() < then; { - for i := 0; i < 1024; i++ { - binary.BigEndian.PutUint32(d[60:], n) - - fbs := common.FirstBitSet(common.BigD(crypto.Sha3(d))) - if fbs > bestBit { - bestBit = fbs - self.Nonce = n - } - - n++ - } + default: + return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err) } } -func (self *Envelope) valid() bool { - d := make([]byte, 64) - enc, _ := rlp.EncodeToBytes(self.withoutNonce()) - copy(d[:32], enc) - binary.BigEndian.PutUint32(d[60:], self.Nonce) - return common.FirstBitSet(common.BigD(crypto.Sha3(d))) > 0 -} - -func (self *Envelope) withoutNonce() interface{} { - return []interface{}{self.Expiry, self.TTL, self.Topics, self.Data} +// Hash returns the SHA3 hash of the envelope, calculating it if not yet done. +func (self *Envelope) Hash() common.Hash { + if (self.hash == common.Hash{}) { + enc, _ := rlp.EncodeToBytes(self) + self.hash = crypto.Sha3Hash(enc) + } + return self.hash } // rlpenv is an Envelope but is not an rlp.Decoder. // It is used for decoding because we need to type rlpenv Envelope +// DecodeRLP decodes an Envelope from an RLP data stream. func (self *Envelope) DecodeRLP(s *rlp.Stream) error { raw, err := s.Raw() if err != nil { diff --git a/whisper/main.go b/whisper/main.go index 6a089f8940..422f0fa3bc 100644 --- a/whisper/main.go +++ b/whisper/main.go @@ -69,7 +69,7 @@ func selfSend(shh *whisper.Whisper, payload []byte) error { }) // Wrap the payload and encrypt it msg := whisper.NewMessage(payload) - envelope, err := msg.Wrap(whisper.DefaultPow, whisper.Options{ + envelope, err := msg.Wrap(whisper.DefaultProofOfWork, whisper.Options{ From: id, To: &id.PublicKey, TTL: whisper.DefaultTimeToLive, diff --git a/whisper/message.go b/whisper/message.go index 3bee83f39f..255352ee8a 100644 --- a/whisper/message.go +++ b/whisper/message.go @@ -17,7 +17,7 @@ import ( // protocol. These are wrapped into Envelopes that need not be understood by // intermediate nodes, just forwarded. type Message struct { - Flags byte // First bit it signature presence, rest reserved and should be random + Flags byte // First bit is signature presence, rest reserved and should be random Signature []byte Payload []byte Sent int64 diff --git a/whisper/message_test.go b/whisper/message_test.go index ed24d37d8d..8d4c5e9907 100644 --- a/whisper/message_test.go +++ b/whisper/message_test.go @@ -13,7 +13,7 @@ func TestMessageSimpleWrap(t *testing.T) { payload := []byte("hello world") msg := NewMessage(payload) - if _, err := msg.Wrap(DefaultPow, Options{}); err != nil { + if _, err := msg.Wrap(DefaultProofOfWork, Options{}); err != nil { t.Fatalf("failed to wrap message: %v", err) } if msg.Flags&128 != 0 { @@ -36,7 +36,7 @@ func TestMessageCleartextSignRecover(t *testing.T) { payload := []byte("hello world") msg := NewMessage(payload) - if _, err := msg.Wrap(DefaultPow, Options{ + if _, err := msg.Wrap(DefaultProofOfWork, Options{ From: key, }); err != nil { t.Fatalf("failed to sign message: %v", err) @@ -69,7 +69,7 @@ func TestMessageAnonymousEncryptDecrypt(t *testing.T) { payload := []byte("hello world") msg := NewMessage(payload) - envelope, err := msg.Wrap(DefaultPow, Options{ + envelope, err := msg.Wrap(DefaultProofOfWork, Options{ To: &key.PublicKey, }) if err != nil { @@ -104,7 +104,7 @@ func TestMessageFullCrypto(t *testing.T) { payload := []byte("hello world") msg := NewMessage(payload) - envelope, err := msg.Wrap(DefaultPow, Options{ + envelope, err := msg.Wrap(DefaultProofOfWork, Options{ From: fromKey, To: &toKey.PublicKey, }) diff --git a/whisper/whisper.go b/whisper/whisper.go index 3cb92a07a8..d803e27d46 100644 --- a/whisper/whisper.go +++ b/whisper/whisper.go @@ -29,7 +29,8 @@ type MessageEvent struct { } const ( - DefaultTimeToLive = 50 * time.Second + DefaultTimeToLive = 50 * time.Second + DefaultProofOfWork = 50 * time.Millisecond ) type Whisper struct { diff --git a/whisper/whisper_test.go b/whisper/whisper_test.go index a3e0e03d2d..b29e34a5e3 100644 --- a/whisper/whisper_test.go +++ b/whisper/whisper_test.go @@ -18,7 +18,7 @@ func TestEvent(t *testing.T) { }) msg := NewMessage([]byte(fmt.Sprintf("Hello world. This is whisper-go. Incase you're wondering; the time is %v", time.Now()))) - envelope, err := msg.Wrap(DefaultPow, Options{ + envelope, err := msg.Wrap(DefaultProofOfWork, Options{ TTL: DefaultTimeToLive, From: id, To: &id.PublicKey, From f8a4cd7ec17774ad841f85241855820424ccfd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 10 Apr 2015 17:03:08 +0300 Subject: [PATCH 5/6] xeth/whisper, ui/qt/qwhisper: fix API update breaks. --- ui/qt/qwhisper/whisper.go | 4 ++-- xeth/whisper.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/qt/qwhisper/whisper.go b/ui/qt/qwhisper/whisper.go index bf165bbccf..3c2d0a4b91 100644 --- a/ui/qt/qwhisper/whisper.go +++ b/ui/qt/qwhisper/whisper.go @@ -37,8 +37,8 @@ func (self *Whisper) Post(payload []string, to, from string, topics []string, pr pk := crypto.ToECDSAPub(common.FromHex(from)) if key := self.Whisper.GetIdentity(pk); key != nil { msg := whisper.NewMessage(data) - envelope, err := msg.Seal(time.Duration(priority*100000), whisper.Opts{ - Ttl: time.Duration(ttl) * time.Second, + envelope, err := msg.Wrap(time.Duration(priority*100000), whisper.Options{ + TTL: time.Duration(ttl) * time.Second, To: crypto.ToECDSAPub(common.FromHex(to)), From: key, Topics: whisper.TopicsFromString(topics...), diff --git a/xeth/whisper.go b/xeth/whisper.go index 72e1ee04f4..51caec8d62 100644 --- a/xeth/whisper.go +++ b/xeth/whisper.go @@ -32,8 +32,8 @@ func (self *Whisper) Post(payload string, to, from string, topics []string, prio pk := crypto.ToECDSAPub(common.FromHex(from)) if key := self.Whisper.GetIdentity(pk); key != nil || len(from) == 0 { msg := whisper.NewMessage(common.FromHex(payload)) - envelope, err := msg.Seal(time.Duration(priority*100000), whisper.Opts{ - Ttl: time.Duration(ttl) * time.Second, + envelope, err := msg.Wrap(time.Duration(priority*100000), whisper.Options{ + TTL: time.Duration(ttl) * time.Second, To: crypto.ToECDSAPub(common.FromHex(to)), From: key, Topics: whisper.TopicsFromString(topics...), From 5467e7b312a29f492ce3063686711ea0bea6dbc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Sun, 12 Apr 2015 14:34:53 +0300 Subject: [PATCH 6/6] whisper: fix comment entity capitalizations --- whisper/envelope.go | 4 ++-- whisper/message.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/whisper/envelope.go b/whisper/envelope.go index 6ad48dd250..f35a40a42e 100644 --- a/whisper/envelope.go +++ b/whisper/envelope.go @@ -59,7 +59,7 @@ func (self *Envelope) Seal(pow time.Duration) { } } -// Valid checks whether the claimed proof of work was indeed executed. +// valid checks whether the claimed proof of work was indeed executed. // TODO: Is this really useful? Isn't this always true? func (self *Envelope) valid() bool { d := make([]byte, 64) @@ -69,7 +69,7 @@ func (self *Envelope) valid() bool { return common.FirstBitSet(common.BigD(crypto.Sha3(d))) > 0 } -// RlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. +// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. func (self *Envelope) rlpWithoutNonce() []byte { enc, _ := rlp.EncodeToBytes([]interface{}{self.Expiry, self.TTL, self.Topics, self.Data}) return enc diff --git a/whisper/message.go b/whisper/message.go index 255352ee8a..2666ee6e00 100644 --- a/whisper/message.go +++ b/whisper/message.go @@ -48,7 +48,7 @@ func NewMessage(payload []byte) *Message { // Wrap bundles the message into an Envelope to transmit over the network. // -// Pov (Proof Of Work) controls how much time to spend on hashing the message, +// 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). // @@ -81,7 +81,7 @@ func (self *Message) Wrap(pow time.Duration, options Options) (*Envelope, error) return envelope, nil } -// Sign calculates and sets the cryptographic signature for the message , also +// 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 << 7 @@ -101,18 +101,18 @@ func (self *Message) Recover() *ecdsa.PublicKey { return pub } -// Encrypt encrypts a message payload with a public key. +// 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) return } -// Hash calculates the SHA3 checksum of the message flags and payload. +// 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 +// 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...)...)