From cc8f237f833049bda6b0da3118678fae034255f6 Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 24 Oct 2016 23:25:15 +0200 Subject: [PATCH] whisper: peer & api tests added --- whisper/shhapi/api.go | 8 +- whisper/whisperv5/filter.go | 2 +- whisper/whisperv5/filter_test.go | 6 +- whisper/whisperv5/peer_test.go | 44 ++++ whisper/whisperv5/whisper.go | 22 +- whisper/whisperv5/whisper_test.go | 381 ++++++++++++++++++++++++++++++ 6 files changed, 445 insertions(+), 18 deletions(-) create mode 100644 whisper/whisperv5/peer_test.go create mode 100644 whisper/whisperv5/whisper_test.go diff --git a/whisper/shhapi/api.go b/whisper/shhapi/api.go index 74d819e4d7..50a8eb34a7 100644 --- a/whisper/shhapi/api.go +++ b/whisper/shhapi/api.go @@ -39,7 +39,7 @@ type PublicWhisperAPI struct { // NewPublicWhisperAPI create a new RPC whisper service. func NewPublicWhisperAPI() *PublicWhisperAPI { - w := whisperv5.New(nil) + w := whisperv5.NewWhisper(nil) return &PublicWhisperAPI{whisper: w} } @@ -89,7 +89,7 @@ func (api *PublicWhisperAPI) HasIdentity(identity string) (bool, error) { if api.whisper == nil { return false, whisperOffLineErr } - return api.whisper.HasIdentity(crypto.ToECDSAPub(common.FromHex(identity))), nil + return api.whisper.HasIdentity(identity), nil } // DeleteIdentity deletes the specifies key if it exists. @@ -201,7 +201,7 @@ func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (*rpc.HexNumber, glog.V(logger.Error).Infof(info) return nil, errors.New(info) } - filter.KeyAsym = api.whisper.GetIdentity(dst) + filter.KeyAsym = api.whisper.GetIdentity(string(args.To)) if filter.KeyAsym == nil { info := "NewFilter: non-existent identity provided" glog.V(logger.Error).Infof(info) @@ -275,7 +275,7 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error { glog.V(logger.Error).Infof(info) return errors.New(info) } - params.Src = api.whisper.GetIdentity(pub) + params.Src = api.whisper.GetIdentity(string(args.From)) if params.Src == nil { info := "Post: non-existent identity provided" glog.V(logger.Error).Infof(info) diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go index a19e5ebee2..eaa1254cfe 100644 --- a/whisper/whisperv5/filter.go +++ b/whisper/whisperv5/filter.go @@ -96,7 +96,7 @@ func (fs *Filters) NotifyWatchers(env *Envelope, messageCode uint64) { } fs.mutex.RUnlock() // we need to unlock before calling addDecryptedMessage - if msg != nil && fs.whisper != nil { + if msg != nil { fs.whisper.addDecryptedMessage(msg) } } diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go index abf8a654c1..8c25b05190 100644 --- a/whisper/whisperv5/filter_test.go +++ b/whisper/whisperv5/filter_test.go @@ -96,7 +96,8 @@ func TestInstallFilters(x *testing.T) { InitSingleTest() const SizeTestFilters = 256 - filters := NewFilters(nil) + w := NewWhisper(nil) + filters := NewFilters(w) tst := generateTestCases(x, SizeTestFilters) var j int @@ -560,7 +561,8 @@ func TestWatchers(x *testing.T) { var i, j int var e *Envelope - filters := NewFilters(nil) + w := NewWhisper(nil) + filters := NewFilters(w) tst := generateTestCases(x, NumFilters) for i = 0; i < NumFilters; i++ { tst[i].f.Src = nil diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go new file mode 100644 index 0000000000..6e860e472c --- /dev/null +++ b/whisper/whisperv5/peer_test.go @@ -0,0 +1,44 @@ +// 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" + +func TestPeerBasic(x *testing.T) { + InitSingleTest() + + params, err := generateMessageParams() + if err != nil { + x.Errorf("failed 1 with seed %d.", seed) + return + } + + params.PoW = 0.001 + msg := NewSentMessage(params) + env, err := msg.Wrap(params) + if err != nil { + x.Errorf("failed 2 with seed %d.", seed) + return + } + + p := newPeer(nil, nil, nil) + p.mark(env) + if !p.marked(env) { + x.Errorf("failed 3 with seed %d.", seed) + return + } +} diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index b8eac6684c..29ad747581 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -60,7 +60,7 @@ type Whisper struct { // New creates a Whisper client ready to communicate through the Ethereum P2P network. // Param s should be passed if you want to implement mail server, otherwise nil. -func New(server MailServer) *Whisper { +func NewWhisper(server MailServer) *Whisper { whisper := &Whisper{ privateKeys: make(map[string]*ecdsa.PrivateKey), symKeys: make(map[string][]byte), @@ -94,10 +94,6 @@ func (w *Whisper) Version() uint { return w.protocol.Version } -func (w *Whisper) GetFilter(id int) *Filter { - return w.filters.Get(id) -} - func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { w.peerMu.Lock() defer w.peerMu.Unlock() @@ -157,7 +153,7 @@ func (w *Whisper) NewIdentity() *ecdsa.PrivateKey { return key } -// DeleteIdentity deletes the specifies key if it exists. +// DeleteIdentity deletes the specified key if it exists. func (w *Whisper) DeleteIdentity(key string) { w.keyMu.Lock() defer w.keyMu.Unlock() @@ -166,17 +162,17 @@ func (w *Whisper) DeleteIdentity(key string) { // HasIdentity checks if the the whisper node is configured with the private key // of the specified public pair. -func (w *Whisper) HasIdentity(key *ecdsa.PublicKey) bool { +func (w *Whisper) HasIdentity(pubKey string) bool { w.keyMu.RLock() defer w.keyMu.RUnlock() - return w.privateKeys[string(crypto.FromECDSAPub(key))] != nil + return w.privateKeys[pubKey] != nil } // GetIdentity retrieves the private key of the specified public identity. -func (w *Whisper) GetIdentity(key *ecdsa.PublicKey) *ecdsa.PrivateKey { +func (w *Whisper) GetIdentity(pubKey string) *ecdsa.PrivateKey { w.keyMu.RLock() defer w.keyMu.RUnlock() - return w.privateKeys[string(crypto.FromECDSAPub(key))] + return w.privateKeys[pubKey] } func (w *Whisper) GenerateSymKey(name string) error { @@ -238,6 +234,10 @@ func (w *Whisper) Watch(f *Filter) int { return w.filters.Install(f) } +func (w *Whisper) GetFilter(id int) *Filter { + return w.filters.Get(id) +} + // Unwatch removes an installed message handler. func (w *Whisper) Unwatch(id int) { w.filters.Uninstall(id) @@ -491,7 +491,7 @@ func (w *Whisper) Envelopes() []*Envelope { return all } -// Messages retrieves all the currently pooled messages matching a filter id. +// Messages retrieves all the decrypted messages matching a filter id. func (w *Whisper) Messages(id int) []*ReceivedMessage { w.poolMu.RLock() defer w.poolMu.RUnlock() diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go new file mode 100644 index 0000000000..ebd786ac05 --- /dev/null +++ b/whisper/whisperv5/whisper_test.go @@ -0,0 +1,381 @@ +// 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" + "testing" + + "github.com/ethereum/go-ethereum/crypto" +) + +func TestWhisperBasic(x *testing.T) { + w := NewWhisper(nil) + p := w.Protocols() + shh := p[0] + if shh.Name != ProtocolName { + x.Errorf("failed Protocol Name: %v.", shh.Name) + return + } + if uint64(shh.Version) != ProtocolVersion { + x.Errorf("failed Protocol Version: %v.", shh.Version) + return + } + if shh.Length != NumberOfMessageCodes { + x.Errorf("failed Protocol Length: %v.", shh.Length) + return + } + if shh.Run == nil { + x.Errorf("failed shh.Run.") + return + } + if uint64(w.Version()) != ProtocolVersion { + x.Errorf("failed whisper Version: %v.", shh.Version) + return + } + if w.GetFilter(0) != nil { + x.Errorf("failed GetFilter.") + return + } + + peerID := make([]byte, 64) + randomize(peerID) + peer, err := w.getPeer(peerID) + if peer != nil { + x.Errorf("failed GetPeer.") + return + } + err = w.MarkPeerTrusted(peerID) + if err == nil { + x.Errorf("failed MarkPeerTrusted.") + return + } + err = w.RequestHistoricMessages(peerID, peerID) + if err == nil { + x.Errorf("failed RequestHistoricMessages.") + return + } + err = w.SendP2PMessage(peerID, nil) + if err == nil { + x.Errorf("failed SendP2PMessage.") + return + } + exist := w.HasSymKey("non-existing") + if exist { + x.Errorf("failed HasSymKey.") + return + } + key := w.GetSymKey("non-existing") + if key != nil { + x.Errorf("failed GetSymKey.") + return + } + mail := w.Envelopes() + if len(mail) != 0 { + x.Errorf("failed w.Envelopes().") + return + } + m := w.Messages(0) + if len(m) != 0 { + x.Errorf("failed w.Messages.") + return + } + + var derived []byte + ver := uint64(0xDEADBEEF) + derived, err = deriveKeyMaterial(peerID, ver) + if err != unknownVersionError(ver) { + x.Errorf("failed deriveKeyMaterial 1 with param = %v: %s.", peerID, err) + return + } + derived, err = deriveKeyMaterial(peerID, 0) + if err != nil { + x.Errorf("failed deriveKeyMaterial 2 with param = %v: %s.", peerID, err) + return + } + if !validateSymmetricKey(derived) { + x.Errorf("failed validateSymmetricKey with param = %v.", derived) + return + } + if containsOnlyZeros(derived) { + x.Errorf("failed containsOnlyZeros with param = %v.", derived) + return + } + + buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0} + le := bytesToIntLittleEndian(buf) + be := BytesToIntBigEndian(buf) + if le != uint64(0x280e5ff) { + x.Errorf("failed bytesToIntLittleEndian: %d.", le) + return + } + if be != uint64(0xffe5800200) { + x.Errorf("failed BytesToIntBigEndian: %d.", be) + return + } + + pk := w.NewIdentity() + if !validatePrivateKey(pk) { + x.Errorf("failed validatePrivateKey: %v.", pk) + return + } + if !ValidatePublicKey(&pk.PublicKey) { + x.Errorf("failed ValidatePublicKey: %v.", pk) + return + } +} + +func TestWhisperIdentityManagement(x *testing.T) { + w := NewWhisper(nil) + id1 := w.NewIdentity() + id2 := w.NewIdentity() + pub1 := string(crypto.FromECDSAPub(&id1.PublicKey)) + pub2 := string(crypto.FromECDSAPub(&id2.PublicKey)) + pk1 := w.GetIdentity(pub1) + pk2 := w.GetIdentity(pub2) + if !w.HasIdentity(pub1) { + x.Errorf("failed HasIdentity 1.") + return + } + if !w.HasIdentity(pub2) { + x.Errorf("failed HasIdentity 2.") + return + } + if pk1 != id1 { + x.Errorf("failed GetIdentity 3.") + return + } + if pk2 != id2 { + x.Errorf("failed GetIdentity 4.") + return + } + + // Delete one identity + w.DeleteIdentity(pub1) + pk1 = w.GetIdentity(pub1) + pk2 = w.GetIdentity(pub2) + if w.HasIdentity(pub1) { + x.Errorf("failed HasIdentity 11.") + return + } + if !w.HasIdentity(pub2) { + x.Errorf("failed HasIdentity 12.") + return + } + if pk1 != nil { + x.Errorf("failed GetIdentity 13.") + return + } + if pk2 != id2 { + x.Errorf("failed GetIdentity 14.") + return + } + + // Delete again non-existing identity + w.DeleteIdentity(pub1) + pk1 = w.GetIdentity(pub1) + pk2 = w.GetIdentity(pub2) + if w.HasIdentity(pub1) { + x.Errorf("failed HasIdentity 21.") + return + } + if !w.HasIdentity(pub2) { + x.Errorf("failed HasIdentity 22.") + return + } + if pk1 != nil { + x.Errorf("failed GetIdentity 23.") + return + } + if pk2 != id2 { + x.Errorf("failed GetIdentity 24.") + return + } + + // Delete second identity + w.DeleteIdentity(pub2) + pk1 = w.GetIdentity(pub1) + pk2 = w.GetIdentity(pub2) + if w.HasIdentity(pub1) { + x.Errorf("failed HasIdentity 31.") + return + } + if w.HasIdentity(pub2) { + x.Errorf("failed HasIdentity 32.") + return + } + if pk1 != nil { + x.Errorf("failed GetIdentity 33.") + return + } + if pk2 != nil { + x.Errorf("failed GetIdentity 34.") + return + } +} + +func TestWhisperSymKeyManagement(x *testing.T) { + InitSingleTest() + + var k1, k2 []byte + w := NewWhisper(nil) + id1 := string("arbitrary-string-1") + id2 := string("arbitrary-string-2") + + err := w.GenerateSymKey(id1) + if err != nil { + x.Errorf("failed test case 1 with seed %d: %s.", seed, err) + return + } + + k1 = w.GetSymKey(id1) + k2 = w.GetSymKey(id2) + if !w.HasSymKey(id1) { + x.Errorf("failed HasIdentity 2.") + return + } + if w.HasSymKey(id2) { + x.Errorf("failed HasIdentity 3.") + return + } + if k1 == nil { + x.Errorf("failed GetIdentity 4.") + return + } + if k2 != nil { + x.Errorf("failed GetIdentity 5.") + return + } + + // add existing id, nothing should change + randomKey := make([]byte, 16) + randomize(randomKey) + err = w.AddSymKey(id1, randomKey) + if err == nil { + x.Errorf("failed test case 10 with seed %d.", seed) + return + } + + k1 = w.GetSymKey(id1) + k2 = w.GetSymKey(id2) + if !w.HasSymKey(id1) { + x.Errorf("failed HasIdentity 12.") + return + } + if w.HasSymKey(id2) { + x.Errorf("failed HasIdentity 13.") + return + } + if k1 == nil { + x.Errorf("failed GetIdentity 14.") + return + } + if bytes.Compare(k1, randomKey) == 0 { + x.Errorf("failed GetIdentity 15: k1 == randomKey.") + return + } + if k2 != nil { + x.Errorf("failed GetIdentity 16.") + return + } + + err = w.AddSymKey(id2, randomKey) // add non-existing (yet) + if err != nil { + x.Errorf("failed test case 21 with seed %d: %s.", seed, err) + return + } + k1 = w.GetSymKey(id1) + k2 = w.GetSymKey(id2) + if !w.HasSymKey(id1) { + x.Errorf("failed HasIdentity 22.") + return + } + if !w.HasSymKey(id2) { + x.Errorf("failed HasIdentity 23.") + return + } + if k1 == nil { + x.Errorf("failed GetIdentity 24.") + return + } + if k2 == nil { + x.Errorf("failed GetIdentity 25.") + return + } + if bytes.Compare(k1, k2) == 0 { + x.Errorf("failed GetIdentity 26.") + return + } + if bytes.Compare(k1, randomKey) == 0 { + x.Errorf("failed GetIdentity 27.") + return + } + if len(k1) != aesKeyLength { + x.Errorf("failed GetIdentity 28.") + return + } + if len(k2) != aesKeyLength { + x.Errorf("failed GetIdentity 29.") + return + } + + w.DeleteSymKey(id1) + k1 = w.GetSymKey(id1) + k2 = w.GetSymKey(id2) + if w.HasSymKey(id1) { + x.Errorf("failed HasIdentity 31.") + return + } + if !w.HasSymKey(id2) { + x.Errorf("failed HasIdentity 32.") + return + } + if k1 != nil { + x.Errorf("failed GetIdentity 33.") + return + } + if k2 == nil { + x.Errorf("failed GetIdentity 34.") + return + } + + w.DeleteSymKey(id1) + w.DeleteSymKey(id2) + k1 = w.GetSymKey(id1) + k2 = w.GetSymKey(id2) + if w.HasSymKey(id1) { + x.Errorf("failed HasIdentity 41.") + return + } + if w.HasSymKey(id2) { + x.Errorf("failed HasIdentity 42.") + return + } + if k1 != nil { + x.Errorf("failed GetIdentity 43.") + return + } + if k2 != nil { + x.Errorf("failed GetIdentity 44.") + return + } +} + +func TestDebug(x *testing.T) { + fmt.Printf("Protocols: %v \n") +}