diff --git a/whisper/shhapi/api.go b/whisper/shhapi/api.go
index 01be0e9a9b..f33ede14e3 100644
--- a/whisper/shhapi/api.go
+++ b/whisper/shhapi/api.go
@@ -346,7 +346,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
// encrypt and send
message := whisperv5.NewSentMessage(¶ms)
- envelope, err := message.Wrap(params)
+ envelope, err := message.Wrap(¶ms)
if err != nil {
glog.V(logger.Error).Infof(err.Error())
return err
@@ -497,7 +497,7 @@ func NewWhisperMessage(message *whisperv5.ReceivedMessage) WhisperMessage {
return WhisperMessage{
Payload: common.ToHex(message.Payload),
Padding: common.ToHex(message.Padding),
- From: common.ToHex(crypto.FromECDSAPub(message.Recover())),
+ From: common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())),
To: common.ToHex(crypto.FromECDSAPub(message.Dst)),
Sent: message.Sent,
TTL: message.TTL,
diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go
index f3b5458d72..5c6b882c68 100644
--- a/whisper/whisperv5/envelope.go
+++ b/whisper/whisperv5/envelope.go
@@ -85,7 +85,7 @@ func (e *Envelope) Ver() uint64 {
// Seal closes the envelope by spending the requested amount of time as a proof
// of work on hashing the data.
-func (e *Envelope) Seal(options MessageParams) {
+func (e *Envelope) Seal(options *MessageParams) {
var target int
if options.PoW == 0 {
// adjust for the duration of Seal() execution only if execution time is predefined unconditionally
diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go
index b86110849e..057e5741a0 100644
--- a/whisper/whisperv5/filter.go
+++ b/whisper/whisperv5/filter.go
@@ -136,13 +136,13 @@ func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
return false
}
- if f.Src != nil && !isEqual(msg.Src, f.Src) {
+ if f.Src != nil && !isPubKeyEqual(msg.Src, f.Src) {
return false
}
if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
// if Dst match, ignore the topic
- return isEqual(f.Dst, msg.Dst)
+ return isPubKeyEqual(f.Dst, msg.Dst)
} else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
// check if that both the key and the topic match
if f.SymKeyHash == msg.SymKeyHash {
@@ -183,7 +183,7 @@ func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
return false
}
-func isEqual(a, b *ecdsa.PublicKey) bool {
+func isPubKeyEqual(a, b *ecdsa.PublicKey) bool {
if !ValidatePublicKey(a) {
return false
} else if !ValidatePublicKey(b) {
diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go
new file mode 100644
index 0000000000..e92b528d3e
--- /dev/null
+++ b/whisper/whisperv5/filter_test.go
@@ -0,0 +1,40 @@
+// Copyright 2016 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 .
+
+package whisperv5
+
+/*
+import "testing"
+
+var topicXxxxxxxxxxxTests = []struct {
+ topic TopicType
+ str string
+}{
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, str: "0x00000000"},
+ {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, str: "0x007f80ff"},
+ {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, str: "0xff807f00"},
+ {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, str: "0xf26e7779"},
+}
+
+func TestXxxxxxxxxxxx(x *testing.T) {
+ for i, tst := range topicStringTests {
+ s := tst.topic.String()
+ if s != tst.str {
+ x.Errorf("failed test %d: have %s, want %s.", i, s, tst.str)
+ }
+ }
+}
+*/
diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go
index f192131484..176676caca 100644
--- a/whisper/whisperv5/message.go
+++ b/whisper/whisperv5/message.go
@@ -148,13 +148,15 @@ func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error {
glog.V(logger.Error).Infof("Trying to sign a message which was already signed")
return nil
}
+
+ msg.Raw[0] |= signatureFlag
hash := crypto.Keccak256(msg.Raw)
signature, err := crypto.Sign(hash, key)
if err != nil {
- msg.Raw = append(msg.Raw, signature...)
- msg.Raw[0] |= signatureFlag
+ msg.Raw[0] &= ^signatureFlag // clear the flag
return err
}
+ msg.Raw = append(msg.Raw, signature...)
return nil
}
@@ -223,12 +225,13 @@ func (msg *SentMessage) encryptSymmetric(key []byte) (salt []byte, nonce []byte,
// - 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 (msg *SentMessage) Wrap(options MessageParams) (envelope *Envelope, err error) {
+func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) {
if options.TTL == 0 {
options.TTL = DefaultTTL
}
if options.Src != nil {
- if err = msg.sign(options.Src); err != nil {
+ err = msg.sign(options.Src)
+ if err != nil {
return nil, err
}
}
@@ -305,7 +308,7 @@ func (msg *ReceivedMessage) Validate() bool {
return false
}
msg.Signature = msg.Raw[end:]
- msg.Src = msg.Recover()
+ msg.Src = msg.SigToPubKey()
if msg.Src == nil {
return false
}
@@ -317,7 +320,7 @@ func (msg *ReceivedMessage) Validate() bool {
}
msg.Payload = msg.Raw[1+padSize : end]
- return msg.isSymmetricEncryption() != msg.isAsymmetricEncryption()
+ return true
}
// extractPadding extracts the padding from raw message.
@@ -338,7 +341,7 @@ func (msg *ReceivedMessage) extractPadding(end int) (int, bool) {
}
// Recover retrieves the public key of the message signer.
-func (msg *ReceivedMessage) Recover() *ecdsa.PublicKey {
+func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
defer func() { recover() }() // in case of invalid signature
pub, err := crypto.SigToPub(msg.hash(), msg.Signature)
diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go
new file mode 100644
index 0000000000..2d4e8e2b13
--- /dev/null
+++ b/whisper/whisperv5/message_test.go
@@ -0,0 +1,246 @@
+// Copyright 2016 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 .
+
+package whisperv5
+
+import (
+ "bytes"
+ "fmt"
+ "math/rand"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+func copyFromBuf(dst []byte, src []byte, beg int) int {
+ copy(dst, src[beg:])
+ return beg + len(dst)
+}
+
+func generateMessageParams(seed int64) (*MessageParams, error) {
+ var err error
+ var p MessageParams
+
+ buf := make([]byte, 1024)
+ _, err = rand.Read(buf)
+ if err != nil {
+ return nil, err
+ }
+
+ p.TTL = uint32(rand.Intn(1024))
+ sz := rand.Intn(400)
+ p.Payload = make([]byte, sz)
+ p.Padding = make([]byte, padSizeLimitUpper)
+ p.KeySym = make([]byte, aesKeyLength)
+
+ var b int
+ b = copyFromBuf(p.Payload, buf, b)
+ b = copyFromBuf(p.Padding, buf, b)
+ b = copyFromBuf(p.KeySym, buf, b)
+ p.Topic = BytesToTopic(buf[b:])
+
+ p.Src, err = crypto.GenerateKey()
+ if err != nil {
+ return nil, err
+ }
+
+ // p.Dst, p.PoW, p.WorkTime are not set
+ return &p, nil
+}
+
+func singleMessageTest(x *testing.T, seed int64, symmetric bool) {
+ params, err := generateMessageParams(seed)
+ if err != nil {
+ x.Errorf("failed generateMessageParams with seed %d: %s.", seed, err)
+ return
+ }
+
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ x.Errorf("failed GenerateKey with seed %d: %s.", seed, err)
+ return
+ }
+
+ if !symmetric {
+ params.KeySym = nil
+ params.Dst = &key.PublicKey
+ }
+
+ text := make([]byte, 0, 512)
+ steg := make([]byte, 0, 512)
+ raw := make([]byte, 0, 1024)
+ text = append(text, params.Payload...)
+ steg = append(steg, params.Padding...)
+ raw = append(raw, params.Padding...)
+
+ msg := NewSentMessage(params)
+ env, err := msg.Wrap(params)
+ if err != nil {
+ x.Errorf("failed Wrap with seed %d: %s.", seed, err)
+ return
+ }
+
+ var decrypted *ReceivedMessage
+ if symmetric {
+ decrypted, err = env.OpenSymmetric(params.KeySym)
+ } else {
+ decrypted, err = env.OpenAsymmetric(key)
+ }
+
+ if err != nil {
+ x.Errorf("failed to encrypt with seed %d: %s.", seed, err)
+ return
+ }
+
+ if !decrypted.Validate() {
+ x.Errorf("failed to validate with seed %d.", seed)
+ return
+ }
+
+ padsz := len(decrypted.Padding)
+ if bytes.Compare(steg[:padsz], decrypted.Padding) != 0 {
+ x.Errorf("failed with seed %d: compare padding.", seed)
+ return
+ }
+ if bytes.Compare(text, decrypted.Payload) != 0 {
+ x.Errorf("failed with seed %d: compare payload.", seed)
+ return
+ }
+ if len(decrypted.Signature) != signatureLength {
+ x.Errorf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature))
+ return
+ }
+ if !isPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) {
+ x.Errorf("failed with seed %d: signature mismatch.", seed)
+ return
+ }
+}
+
+func TestMessageEncryption(x *testing.T) {
+ seed := time.Now().Unix()
+ rand.Seed(seed)
+ var symmetric bool
+
+ for i := 0; i < 256; i++ {
+ singleMessageTest(x, seed, symmetric)
+ symmetric = !symmetric
+ }
+}
+
+func DebugSingleMessageTest(x *testing.T) {
+ seed := int64(1476726903)
+ symmetric := seed%2 != 0
+
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ x.Errorf("failed GenerateKey with seed %d: %s.", seed, err)
+ return
+ }
+
+ params, err := generateMessageParams(seed)
+ if err != nil {
+ x.Errorf("failed generateMessageParams with seed %d: %s.", seed, err)
+ return
+ }
+ msg := NewSentMessage(params)
+
+ text := make([]byte, 0, 512)
+ steg := make([]byte, 0, 512)
+ raw := make([]byte, 0, 1024)
+ text = append(text, params.Payload...)
+ steg = append(steg, params.Padding...)
+ raw = append(raw, params.Padding...)
+
+ if !symmetric {
+ params.KeySym = nil
+ params.Dst = &key.PublicKey
+ }
+
+ if len(params.KeySym) == 0 {
+ x.Errorf("failed key 12 with seed %d.", seed)
+ return
+ }
+
+ env, err := msg.Wrap(params)
+ if err != nil {
+ x.Errorf("failed Wrap with seed %d: %s.", seed, err)
+ return
+ }
+
+ var decrypted *ReceivedMessage
+ if symmetric {
+ decrypted, err = env.OpenSymmetric(params.KeySym)
+ } else {
+ decrypted, err = env.OpenAsymmetric(key)
+ }
+
+ if err != nil {
+ x.Errorf("failed to encrypt with seed %d: %s.", seed, err)
+ return
+ }
+
+ ok := decrypted.Validate()
+ if !ok {
+ x.Errorf("failed to validate with seed %d.", seed)
+ return
+ }
+
+ sz := len(decrypted.Padding)
+ if bytes.Compare(steg[:sz], decrypted.Padding) != 0 {
+ x.Errorf("failed with seed %d: compare padding.", seed)
+ return
+ }
+ if bytes.Compare(text, decrypted.Payload) != 0 {
+ x.Errorf("failed with seed %d: compare payload.", seed)
+ return
+ }
+ if len(decrypted.Signature) != signatureLength {
+ x.Errorf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature))
+ return
+ }
+ if !isPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) {
+ x.Errorf("failed with seed %d: signature mismatch:\n%v and \n%v.", seed, *decrypted.Src, params.Src.PublicKey)
+ return
+ }
+}
+
+func TestMessageSeal(x *testing.T) {
+ seed := int64(1476726903)
+ rand.Seed(seed)
+
+ params, err := generateMessageParams(seed)
+ if err != nil {
+ x.Errorf("failed generateMessageParams with seed %d: %s.", seed, err)
+ return
+ }
+
+ msg := NewSentMessage(params)
+ params.TTL = 1
+ params.WorkTime = 3
+ env, err := msg.Wrap(params)
+ if err != nil {
+ x.Errorf("failed Wrap with seed %d: %s.", seed, err)
+ return
+ }
+
+ pow := env.PoW()
+ h := env.Hash()
+ bits := common.FirstBitSet(common.BigD(h.Bytes()))
+ fmt.Printf("pow = %f, bits = %d, datalen = %d, hash = %x \n", pow, bits, len(env.Data), h)
+
+}
diff --git a/whisper/whisperv5/topic_test.go b/whisper/whisperv5/topic_test.go
new file mode 100644
index 0000000000..c2a940b79a
--- /dev/null
+++ b/whisper/whisperv5/topic_test.go
@@ -0,0 +1,136 @@
+// Copyright 2016 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 .
+
+package whisperv5
+
+import "testing"
+
+var topicStringTests = []struct {
+ topic TopicType
+ str string
+}{
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, str: "0x00000000"},
+ {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, str: "0x007f80ff"},
+ {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, str: "0xff807f00"},
+ {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, str: "0xf26e7779"},
+}
+
+func TestTopicString(x *testing.T) {
+ for i, tst := range topicStringTests {
+ s := tst.topic.String()
+ if s != tst.str {
+ x.Errorf("failed test %d: have %s, want %s.", i, s, tst.str)
+ }
+ }
+}
+
+var bytesToTopicTests = []struct {
+ data []byte
+ topic TopicType
+}{
+ {topic: TopicType{0x8f, 0x9a, 0x2b, 0x7d}, data: []byte{0x8f, 0x9a, 0x2b, 0x7d}},
+ {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte{0x00, 0x7f, 0x80, 0xff}},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00, 0x00}},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00}},
+ {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte{0x01}},
+ {topic: TopicType{0x00, 0xfe, 0x00, 0x00}, data: []byte{0x00, 0xfe}},
+ {topic: TopicType{0xea, 0x1d, 0x43, 0x00}, data: []byte{0xea, 0x1d, 0x43}},
+ {topic: TopicType{0x6f, 0x3c, 0xb0, 0xdd}, data: []byte{0x6f, 0x3c, 0xb0, 0xdd, 0x0f, 0x00, 0x90}},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{}},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil},
+}
+
+func TestBytesToTopic(x *testing.T) {
+ for i, tst := range bytesToTopicTests {
+ t := BytesToTopic(tst.data)
+ if t != tst.topic {
+ x.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic)
+ }
+ }
+}
+
+var unmarshalTestsGood = []struct {
+ topic TopicType
+ data []byte
+}{
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x00000000")},
+ {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte("0x007f80ff")},
+ {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte("0xff807f00")},
+ {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte("0xf26e7779")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("00000000")},
+ {topic: TopicType{0x00, 0x80, 0x01, 0x00}, data: []byte("00800100")},
+ {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte("007f80ff")},
+ {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte("ff807f00")},
+ {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte("f26e7779")},
+}
+
+var unmarshalTestsBad = []struct {
+ topic TopicType
+ data []byte
+}{
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x000000")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x0000000")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x000000000")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x0000000000")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("000000")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0000000")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("000000000")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0000000000")},
+ {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("abcdefg0")},
+}
+
+var unmarshalTestsUgly = []struct {
+ topic TopicType
+ data []byte
+}{
+ {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte("00000001")},
+}
+
+func TestUnmarshalTestsGood(x *testing.T) {
+ for i, tst := range unmarshalTestsGood {
+ var t TopicType
+ err := t.UnmarshalJSON(tst.data)
+ if err != nil {
+ x.Errorf("failed test %d. input: %v.", i, tst.data)
+ } else if t != tst.topic {
+ x.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic)
+ }
+ }
+}
+
+func TestUnmarshalTestsBad(x *testing.T) {
+ // in this test UnmarshalJSON() is supposed to fail
+ for i, tst := range unmarshalTestsBad {
+ var t TopicType
+ err := t.UnmarshalJSON(tst.data)
+ if err == nil {
+ x.Errorf("failed test %d. input: %v.", i, tst.data)
+ }
+ }
+}
+
+func TestUnmarshalTestsUgly(x *testing.T) {
+ // in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong
+ for i, tst := range unmarshalTestsUgly {
+ var t TopicType
+ err := t.UnmarshalJSON(tst.data)
+ if err != nil {
+ x.Errorf("failed test %d. input: %v.", i, tst.data)
+ } else if t == tst.topic {
+ x.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic)
+ }
+ }
+}
diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go
index 1183db78e6..b8eac6684c 100644
--- a/whisper/whisperv5/whisper.go
+++ b/whisper/whisperv5/whisper.go
@@ -533,10 +533,10 @@ func validateSymmetricKey(k []byte) bool {
func containsOnlyZeros(data []byte) bool {
for _, b := range data {
if b != 0 {
- return true
+ return false
}
}
- return false
+ return true
}
func bytesToIntLittleEndian(b []byte) (res uint64) {