Add asymmetric key support for MailServer requests (#1075)

* add Asymmetric Key support for MailServer requests

* remove deprecated notice

* fix linter

* refactoring Whisper config related to MailServer

* fix race condition
This commit is contained in:
Adam Babik 2018-07-04 11:30:57 +02:00 committed by Adrià Cidre
parent 93210061ad
commit 38a60135b2
9 changed files with 340 additions and 140 deletions

View File

@ -28,7 +28,7 @@ func whisperConfig(nodeConfig *params.NodeConfig) (*params.NodeConfig, error) {
return nil, fmt.Errorf("password file: %v", err)
}
whisperConfig.Password = string(password)
whisperConfig.MailServerPassword = string(password)
}
// firebase configuration

View File

@ -20,6 +20,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"sync"
"time"
@ -43,8 +44,8 @@ const (
)
var (
errDirectoryNotProvided = errors.New("data directory not provided")
errPasswordNotProvided = errors.New("password is not specified")
errDirectoryNotProvided = errors.New("data directory not provided")
errDecryptionMethodNotProvided = errors.New("decryption method is not provided")
// By default go-ethereum/metrics creates dummy metrics that don't register anything.
// Real metrics are collected only if -metrics flag is set
requestProcessTimer = metrics.NewRegisteredTimer("mailserver/requestProcessTime", nil)
@ -82,12 +83,14 @@ type dbImpl interface {
// WMailServer whisper mailserver.
type WMailServer struct {
db dbImpl
w *whisper.Whisper
pow float64
key []byte
limit *limiter
tick *ticker
db dbImpl
w *whisper.Whisper
pow float64
filter *whisper.Filter
muLimiter sync.RWMutex
limiter *limiter
tick *ticker
}
// DBKey key to be stored on db.
@ -116,24 +119,26 @@ func (s *WMailServer) Init(shh *whisper.Whisper, config *params.WhisperConfig) e
return errDirectoryNotProvided
}
if len(config.Password) == 0 {
return errPasswordNotProvided
if len(config.MailServerPassword) == 0 && config.MailServerAsymKey == nil {
return errDecryptionMethodNotProvided
}
db, err := db.Open(config.DataDir, nil)
if err != nil {
return fmt.Errorf("open DB: %s", err)
}
s.db = db
s.w = shh
s.pow = config.MinimumPoW
if err := s.setupWhisperIdentity(config); err != nil {
if err := s.setupRequestMessageDecryptor(config); err != nil {
return err
}
s.setupLimiter(time.Duration(config.MailServerRateLimit) * time.Second)
// Open database in the last step in order not to init with error
// and leave the database open by accident.
database, err := db.Open(config.DataDir, nil)
if err != nil {
return fmt.Errorf("open DB: %s", err)
}
s.db = database
return nil
}
@ -141,23 +146,33 @@ func (s *WMailServer) Init(shh *whisper.Whisper, config *params.WhisperConfig) e
// limit db cleanup.
func (s *WMailServer) setupLimiter(limit time.Duration) {
if limit > 0 {
s.limit = newLimiter(limit)
s.limiter = newLimiter(limit)
s.setupMailServerCleanup(limit)
}
}
// setupWhisperIdentity setup the whisper identity (symkey) for the current mail
// server.
func (s *WMailServer) setupWhisperIdentity(config *params.WhisperConfig) error {
MailServerKeyID, err := s.w.AddSymKeyFromPassword(config.Password)
if err != nil {
return fmt.Errorf("create symmetric key: %s", err)
// setupRequestMessageDecryptor setup a Whisper filter to decrypt
// incoming Whisper requests.
func (s *WMailServer) setupRequestMessageDecryptor(config *params.WhisperConfig) error {
var filter whisper.Filter
if config.MailServerPassword != "" {
keyID, err := s.w.AddSymKeyFromPassword(config.MailServerPassword)
if err != nil {
return fmt.Errorf("create symmetric key: %v", err)
}
symKey, err := s.w.GetSymKey(keyID)
if err != nil {
return fmt.Errorf("save symmetric key: %v", err)
}
filter = whisper.Filter{KeySym: symKey}
} else if config.MailServerAsymKey != nil {
filter = whisper.Filter{KeyAsym: config.MailServerAsymKey}
}
s.key, err = s.w.GetSymKey(MailServerKeyID)
if err != nil {
return fmt.Errorf("save symmetric key: %s", err)
}
s.filter = &filter
return nil
}
@ -168,7 +183,7 @@ func (s *WMailServer) setupMailServerCleanup(period time.Duration) {
if s.tick == nil {
s.tick = &ticker{}
}
go s.tick.run(period, s.limit.deleteExpired)
go s.tick.run(period, s.limiter.deleteExpired)
}
// Close the mailserver and its associated db connection.
@ -244,13 +259,16 @@ func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope)
// exceedsPeerRequests in case limit its been setup on the current server and limit
// allows the query, it will store/update new query time for the current peer.
func (s *WMailServer) exceedsPeerRequests(peer []byte) bool {
if s.limit != nil {
s.muLimiter.RLock()
defer s.muLimiter.RUnlock()
if s.limiter != nil {
peerID := string(peer)
if !s.limit.isAllowed(peerID) {
if !s.limiter.isAllowed(peerID) {
log.Info("peerID exceeded the number of requests per second")
return true
}
s.limit.add(peerID)
s.limiter.add(peerID)
}
return false
}
@ -345,8 +363,7 @@ func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope)
return false, 0, 0, nil, 0, nil
}
f := whisper.Filter{KeySym: s.key}
decrypted := request.Open(&f)
decrypted := request.Open(s.filter)
if decrypted == nil {
log.Warn("Failed to decrypt p2p request")
return false, 0, 0, nil, 0, nil

View File

@ -23,6 +23,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
@ -48,8 +49,6 @@ type ServerTestParams struct {
key *ecdsa.PrivateKey
}
const dataDirPrefix = "whisper-server-test"
func TestMailserverSuite(t *testing.T) {
suite.Run(t, new(MailserverSuite))
}
@ -67,86 +66,159 @@ func (s *MailserverSuite) SetupTest() {
s.shh = whisper.New(&whisper.DefaultConfig)
s.shh.RegisterServer(s.server)
var err error
s.dataDir, err = ioutil.TempDir("/tmp", dataDirPrefix)
if err != nil {
s.FailNow("failed creating tmp data dir", err)
}
tmpDir, err := ioutil.TempDir("", "mailserver-test")
s.Require().NoError(err)
s.dataDir = tmpDir
// required files to validate mail server decryption method
asymKeyFile := filepath.Join(tmpDir, "asymkey")
passwordFile := filepath.Join(tmpDir, "password")
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
err = crypto.SaveECDSA(asymKeyFile, privateKey)
s.Require().NoError(err)
err = ioutil.WriteFile(passwordFile, []byte("testpassword"), os.ModePerm)
s.Require().NoError(err)
s.config = &params.WhisperConfig{
DataDir: s.dataDir,
Password: "pwd",
MailServerRateLimit: 5,
DataDir: tmpDir,
MailServerAsymKeyFile: asymKeyFile,
MailServerPasswordFile: passwordFile,
}
}
func (s *MailserverSuite) TearDownTest() {
if s.dataDir != "" {
if err := os.RemoveAll(s.dataDir); err != nil {
fmt.Printf("couldn't remove temporary data dir %s: %v", s.dataDir, err)
}
}
s.Require().NoError(os.RemoveAll(s.config.DataDir))
}
func (s *MailserverSuite) TestInit() {
asymKey, err := crypto.GenerateKey()
s.Require().NoError(err)
testCases := []struct {
config params.WhisperConfig
expectedError error
limiterActive bool
info string
}{
{
config: params.WhisperConfig{DataDir: ""},
expectedError: errDirectoryNotProvided,
limiterActive: false,
info: "Initializing a mail server with a config with empty DataDir",
},
{
config: params.WhisperConfig{DataDir: s.dataDir, Password: ""},
expectedError: errPasswordNotProvided,
limiterActive: false,
info: "Initializing a mail server with a config with an empty password",
},
{
config: params.WhisperConfig{DataDir: "/invalid-path", Password: "pwd"},
expectedError: errors.New("open DB: mkdir /invalid-path: permission denied"),
limiterActive: false,
info: "Initializing a mail server with a config with an unexisting DataDir",
},
{
config: *s.config,
expectedError: nil,
limiterActive: true,
info: "Initializing a mail server with a config with correct config and active limiter",
info: "config with empty DataDir",
},
{
config: params.WhisperConfig{
DataDir: s.dataDir,
Password: "pwd",
DataDir: "/invalid-path",
MailServerPassword: "pwd",
},
expectedError: errors.New("open DB: mkdir /invalid-path: permission denied"),
info: "config with an unexisting DataDir",
},
{
config: params.WhisperConfig{
DataDir: s.config.DataDir,
MailServerPassword: "",
MailServerAsymKey: nil,
},
expectedError: errDecryptionMethodNotProvided,
info: "config with an empty password and empty asym key",
},
{
config: params.WhisperConfig{
DataDir: s.config.DataDir,
MailServerPassword: "pwd",
},
expectedError: nil,
limiterActive: false,
info: "Initializing a mail server with a config with empty DataDir and inactive limiter",
info: "config with correct DataDir and Password",
},
{
config: params.WhisperConfig{
DataDir: s.config.DataDir,
MailServerAsymKey: asymKey,
},
expectedError: nil,
info: "config with correct DataDir and AsymKey",
},
{
config: params.WhisperConfig{
DataDir: s.config.DataDir,
MailServerAsymKey: asymKey,
MailServerPassword: "pwd",
},
expectedError: nil,
info: "config with both asym key and password",
},
{
config: params.WhisperConfig{
DataDir: s.config.DataDir,
MailServerPassword: "pwd",
MailServerRateLimit: 5,
},
expectedError: nil,
info: "config with rate limit",
},
}
for _, tc := range testCases {
s.T().Run(tc.info, func(*testing.T) {
s.server.limit = nil
err := s.server.Init(s.shh, &tc.config)
s.server.tick = nil
s.server.Close()
mailServer := &WMailServer{}
shh := whisper.New(&whisper.DefaultConfig)
shh.RegisterServer(mailServer)
err := mailServer.Init(shh, &tc.config)
s.Equal(tc.expectedError, err)
s.Equal(tc.limiterActive, (s.server.limit != nil))
defer mailServer.Close()
// db should be open only if there was no error
if tc.expectedError == nil {
s.NotNil(mailServer.db)
} else {
s.Nil(mailServer.db)
}
if tc.config.MailServerRateLimit > 0 {
s.NotNil(mailServer.limiter)
}
})
}
}
func (s *MailserverSuite) TestSetupRequestMessageDecryptor() {
// without configured Password and AsymKey
config := *s.config
s.Error(errDecryptionMethodNotProvided, s.server.Init(s.shh, &config))
// Password should work ok
config = *s.config
s.NoError(config.ReadMailServerPasswordFile())
s.NoError(s.server.Init(s.shh, &config))
s.NotNil(s.server.filter.KeySym)
s.Nil(s.server.filter.KeyAsym)
s.server.Close()
// AsymKey can also be used
config = *s.config
s.NoError(config.ReadMailServerAsymKeyFile())
s.NoError(s.server.Init(s.shh, &config))
s.Nil(s.server.filter.KeySym) // important: symmetric key should be nil
s.Equal(config.MailServerAsymKey, s.server.filter.KeyAsym)
s.server.Close()
// when both Password and AsymKey are set, Password has a preference
config = *s.config
s.NoError(config.ReadMailServerPasswordFile())
s.NoError(config.ReadMailServerAsymKeyFile())
s.NoError(s.server.Init(s.shh, &config))
s.NotNil(s.server.filter.KeySym)
s.Nil(s.server.filter.KeyAsym)
s.server.Close()
}
func (s *MailserverSuite) TestArchive() {
err := s.server.Init(s.shh, s.config)
s.server.tick = nil
s.NoError(err)
err := s.config.ReadMailServerPasswordFile()
s.Require().NoError(err)
err = s.server.Init(s.shh, s.config)
s.Require().NoError(err)
defer s.server.Close()
env, err := generateEnvelope(time.Now())
@ -163,15 +235,15 @@ func (s *MailserverSuite) TestArchive() {
}
func (s *MailserverSuite) TestManageLimits() {
s.server.limit = newLimiter(time.Duration(5) * time.Millisecond)
s.server.limiter = newLimiter(time.Duration(5) * time.Millisecond)
s.False(s.server.exceedsPeerRequests([]byte("peerID")))
s.Equal(1, len(s.server.limit.db))
firstSaved := s.server.limit.db["peerID"]
s.Equal(1, len(s.server.limiter.db))
firstSaved := s.server.limiter.db["peerID"]
// second call when limit is not accomplished does not store a new limit
s.True(s.server.exceedsPeerRequests([]byte("peerID")))
s.Equal(1, len(s.server.limit.db))
s.Equal(firstSaved, s.server.limit.db["peerID"])
s.Equal(1, len(s.server.limiter.db))
s.Equal(firstSaved, s.server.limiter.db["peerID"])
}
func (s *MailserverSuite) TestDBKey() {
@ -392,7 +464,11 @@ func (s *MailserverSuite) setupServer(server *WMailServer) {
s.shh = whisper.New(&whisper.DefaultConfig)
s.shh.RegisterServer(server)
err := server.Init(s.shh, &params.WhisperConfig{DataDir: s.dataDir, Password: password, MinimumPoW: powRequirement})
err := server.Init(s.shh, &params.WhisperConfig{
DataDir: s.dataDir,
MailServerPassword: password,
MinimumPoW: powRequirement,
})
if err != nil {
s.T().Fatal(err)
}

View File

@ -1,8 +1,12 @@
package mailserver
import "time"
import (
"sync"
"time"
)
type ticker struct {
mu sync.RWMutex
timeTicker *time.Ticker
}
@ -11,14 +15,19 @@ func (t *ticker) run(period time.Duration, fn func()) {
return
}
t.timeTicker = time.NewTicker(period)
tt := time.NewTicker(period)
t.mu.Lock()
t.timeTicker = tt
t.mu.Unlock()
go func() {
for range t.timeTicker.C {
for range tt.C {
fn()
}
}()
}
func (t *ticker) stop() {
t.mu.RLock()
t.timeTicker.Stop()
t.mu.RUnlock()
}

View File

@ -189,6 +189,28 @@ func activateStatusService(stack *node.Node, config *params.NodeConfig) error {
})
}
func registerMailServer(whisperService *whisper.Whisper, config *params.WhisperConfig) (err error) {
// if the Password is already set, do not override it
if config.MailServerPassword == "" && config.MailServerPasswordFile != "" {
err = config.ReadMailServerPasswordFile()
if err != nil {
return
}
}
// similarly, do not override already configured AsymKey
if config.MailServerAsymKey == nil && config.MailServerAsymKeyFile != "" {
err = config.ReadMailServerAsymKeyFile()
if err != nil {
return
}
}
var mailServer mailserver.WMailServer
whisperService.RegisterServer(&mailServer)
return mailServer.Init(whisperService, config)
}
// activateShhService configures Whisper and adds it to the given node.
func activateShhService(stack *node.Node, config *params.NodeConfig, db *leveldb.DB) (err error) {
if config.WhisperConfig == nil || !config.WhisperConfig.Enabled {
@ -223,19 +245,8 @@ func activateShhService(stack *node.Node, config *params.NodeConfig, db *leveldb
// enable mail service
if config.WhisperConfig.EnableMailServer {
if config.WhisperConfig.Password == "" {
if err := config.WhisperConfig.ReadPasswordFile(); err != nil {
return nil, err
}
}
logger.Info("Register MailServer")
var mailServer mailserver.WMailServer
whisperService.RegisterServer(&mailServer)
err := mailServer.Init(whisperService, config.WhisperConfig)
if err != nil {
return nil, err
if err := registerMailServer(whisperService, config.WhisperConfig); err != nil {
return nil, fmt.Errorf("failed to register MailServer: %v", err)
}
}

View File

@ -2,6 +2,7 @@ package params
import (
"bytes"
"crypto/ecdsa"
"encoding/json"
"errors"
"fmt"
@ -11,8 +12,10 @@ import (
"strings"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/status-im/status-go/static"
)
@ -85,13 +88,6 @@ type WhisperConfig struct {
// Enabled flag specifies whether protocol is enabled
Enabled bool
// PasswordFile contains a password for symmetric encryption with MailServer.
PasswordFile string
// Password for symmetric encryption with MailServer.
// (if no account file selected, then this password is used for symmetric encryption).
Password string
// LightClient should be true if the node should start with an empty bloom filter and not forward messages from other nodes
LightClient bool
@ -105,6 +101,19 @@ type WhisperConfig struct {
// MinimumPoW minimum PoW for Whisper messages
MinimumPoW float64
// MailServerPasswordFile contains a password for symmetric encryption with MailServer.
MailServerPasswordFile string
// MailServerPassword for symmetric encryption with MailServer.
// (if no account file selected, then this password is used for symmetric encryption).
MailServerPassword string
// MailServerAsymKeyFile is a file with an asymmetric key to decrypt messages sent to MailServer.
MailServerAsymKeyFile string
// MailServerAsymKey is an asymmetric key to decrypt messages sent to MailServer.
MailServerAsymKey *ecdsa.PrivateKey
// RateLimit minimum time between queries to mail server per peer
MailServerRateLimit int
@ -121,13 +130,13 @@ type WhisperConfig struct {
EnableNTPSync bool
}
// ReadPasswordFile reads and returns content of the password file
func (c *WhisperConfig) ReadPasswordFile() error {
if len(c.PasswordFile) == 0 {
// ReadMailServerPasswordFile reads and returns content of the password file
func (c *WhisperConfig) ReadMailServerPasswordFile() error {
if len(c.MailServerPasswordFile) == 0 {
return ErrNoPasswordFileValueSet
}
password, err := ioutil.ReadFile(c.PasswordFile)
password, err := ioutil.ReadFile(c.MailServerPasswordFile)
if err != nil {
return err
}
@ -137,11 +146,17 @@ func (c *WhisperConfig) ReadPasswordFile() error {
return ErrEmptyPasswordFile
}
c.Password = string(password)
c.MailServerPassword = string(password)
return nil
}
// ReadMailServerAsymKeyFile reads and returns a private key from a given file.
func (c *WhisperConfig) ReadMailServerAsymKeyFile() (err error) {
c.MailServerAsymKey, err = crypto.LoadECDSA(c.MailServerAsymKeyFile)
return
}
// String dumps config object as nicely indented JSON
func (c *WhisperConfig) String() string {
data, _ := json.MarshalIndent(c, "", " ") // nolint: gas

View File

@ -28,6 +28,9 @@ var (
ErrInvalidMailServerPeer = errors.New("invalid mailServerPeer value")
// ErrInvalidSymKeyID is returned when it fails to get a symmetric key.
ErrInvalidSymKeyID = errors.New("invalid symKeyID value")
// ErrInvalidPublicKey is returned when public key can't be extracted
// from MailServer's nodeID.
ErrInvalidPublicKey = errors.New("can't extract public key")
)
// -----
@ -59,6 +62,9 @@ type MessagesRequest struct {
// SymKeyID is an ID of a symmetric key to authenticate to MailServer.
// It's derived from MailServer password.
//
// It's also possible to authenticate request with MailServerPeer
// public key.
SymKeyID string `json:"symKeyID"`
// Timeout is the time to live of the request specified in seconds.
@ -128,17 +134,38 @@ func (api *PublicAPI) RequestMessages(_ context.Context, r MessagesRequest) (hex
return nil, fmt.Errorf("Query range is invalid: from > to (%d > %d)", r.From, r.To)
}
var err error
mailServerNode, err := discover.ParseNode(r.MailServerPeer)
if err != nil {
return nil, fmt.Errorf("%v: %v", ErrInvalidMailServerPeer, err)
}
symKey, err := shh.GetSymKey(r.SymKeyID)
if err != nil {
return nil, fmt.Errorf("%v: %v", ErrInvalidSymKeyID, err)
var (
symKey []byte
publicKey *ecdsa.PublicKey
)
if r.SymKeyID != "" {
symKey, err = shh.GetSymKey(r.SymKeyID)
if err != nil {
return nil, fmt.Errorf("%v: %v", ErrInvalidSymKeyID, err)
}
} else {
publicKey, err = mailServerNode.ID.Pubkey()
if err != nil {
return nil, fmt.Errorf("%v: %v", ErrInvalidPublicKey, err)
}
}
envelope, err := makeEnvelop(makePayload(r), symKey, api.service.nodeID, shh.MinPow(), now)
envelope, err := makeEnvelop(
makePayload(r),
symKey,
publicKey,
api.service.nodeID,
shh.MinPow(),
now,
)
if err != nil {
return nil, err
}
@ -175,14 +202,27 @@ func (api *PublicAPI) ConfirmMessagesProcessed(messages []*whisper.Message) erro
// makeEnvelop makes an envelop for a historic messages request.
// Symmetric key is used to authenticate to MailServer.
// PK is the current node ID.
func makeEnvelop(payload []byte, symKey []byte, nodeID *ecdsa.PrivateKey, pow float64, now time.Time) (*whisper.Envelope, error) {
func makeEnvelop(
payload []byte,
symKey []byte,
publicKey *ecdsa.PublicKey,
nodeID *ecdsa.PrivateKey,
pow float64,
now time.Time,
) (*whisper.Envelope, error) {
params := whisper.MessageParams{
PoW: pow,
Payload: payload,
KeySym: symKey,
WorkTime: defaultWorkTime,
Src: nodeID,
}
// Either symKey or public key is required.
// This condition is verified in `message.Wrap()` method.
if len(symKey) > 0 {
params.KeySym = symKey
} else if publicKey != nil {
params.Dst = publicKey
}
message, err := whisper.NewSentMessage(&params)
if err != nil {
return nil, err

View File

@ -12,6 +12,7 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"github.com/status-im/status-go/t/helpers"
"github.com/stretchr/testify/suite"
)
@ -141,7 +142,7 @@ func (s *ShhExtSuite) TestWaitMessageExpired() {
}
}
func (s *ShhExtSuite) TestRequestMessages() {
func (s *ShhExtSuite) TestRequestMessagesErrors() {
var err error
shh := whisper.New(nil)
@ -152,17 +153,14 @@ func (s *ShhExtSuite) TestRequestMessages() {
},
}) // in-memory node as no data dir
s.NoError(err)
err = aNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
err = aNode.Register(func(*node.ServiceContext) (node.Service, error) {
return shh, nil
})
s.NoError(err)
err = aNode.Start()
s.NoError(err)
defer func() {
err := aNode.Stop()
s.NoError(err)
}()
defer func() { s.NoError(aNode.Stop()) }()
mock := newHandlerMock(1)
service := New(shh, mock, nil, false)
@ -182,6 +180,7 @@ func (s *ShhExtSuite) TestRequestMessages() {
// non-existent symmetric key
hash, err = api.RequestMessages(context.TODO(), MessagesRequest{
MailServerPeer: mailServerPeer,
SymKeyID: "invalid-sym-key-id",
})
s.Nil(hash)
s.EqualError(err, "invalid symKeyID value: non-existent key ID")
@ -193,16 +192,39 @@ func (s *ShhExtSuite) TestRequestMessages() {
MailServerPeer: mailServerPeer,
SymKeyID: symKeyID,
})
s.Contains(err.Error(), "Could not find peer with ID")
s.Nil(hash)
s.Contains(err.Error(), "Could not find peer with ID")
// from is greater than to
hash, err = api.RequestMessages(context.TODO(), MessagesRequest{
From: 10,
To: 5,
})
s.Contains(err.Error(), "Query range is invalid: from > to (10 > 5)")
s.Nil(hash)
s.Contains(err.Error(), "Query range is invalid: from > to (10 > 5)")
}
func (s *ShhExtSuite) TestRequestMessagesSuccess() {
var err error
shh := whisper.New(nil)
aNode, err := node.New(&node.Config{
P2P: p2p.Config{
MaxPeers: math.MaxInt32,
NoDiscovery: true,
},
}) // in-memory node as no data dir
s.NoError(err)
err = aNode.Register(func(*node.ServiceContext) (node.Service, error) { return shh, nil })
s.NoError(err)
err = aNode.Start()
s.NoError(err)
defer func() { err := aNode.Stop(); s.NoError(err) }()
mock := newHandlerMock(1)
service := New(shh, mock, nil, false)
api := NewPublicAPI(service)
// with a peer acting as a mailserver
// prepare a node first
@ -214,29 +236,39 @@ func (s *ShhExtSuite) TestRequestMessages() {
},
}) // in-memory node as no data dir
s.NoError(err)
err = mailNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
err = mailNode.Register(func(*node.ServiceContext) (node.Service, error) {
return whisper.New(nil), nil
})
s.NoError(err)
err = mailNode.Start()
s.NoError(err)
defer func() {
err := mailNode.Stop()
s.NoError(err)
}()
defer func() { s.NoError(mailNode.Stop()) }()
// add mailPeer as a peer
aNode.Server().AddPeer(mailNode.Server().Self())
time.Sleep(time.Second) // wait for the peer to be added
waitErr := helpers.WaitForPeerAsync(aNode.Server(), mailNode.Server().Self().String(), p2p.PeerEventTypeAdd, time.Second)
s.NoError(<-waitErr)
// send a request
var hash []byte
// send a request with a symmetric key
symKeyID, symKeyErr := shh.AddSymKeyFromPassword("some-pass")
s.NoError(symKeyErr)
hash, err = api.RequestMessages(context.TODO(), MessagesRequest{
MailServerPeer: mailNode.Server().Self().String(),
SymKeyID: symKeyID,
})
s.NoError(err)
s.NotNil(hash)
s.Contains(api.service.tracker.cache, common.BytesToHash(hash))
// Send a request without a symmetric key. In this case,
// a public key extracted from MailServerPeer will be used.
hash, err = api.RequestMessages(context.TODO(), MessagesRequest{
MailServerPeer: mailNode.Server().Self().String(),
})
s.NoError(err)
s.NotNil(hash)
s.Contains(api.service.tracker.cache, common.BytesToHash(hash))
}

View File

@ -558,7 +558,7 @@ func (s *WhisperMailboxSuite) startMailboxBackend() (*api.StatusBackend, func())
mailboxConfig.WhisperConfig.Enabled = true
mailboxConfig.KeyStoreDir = datadir
mailboxConfig.WhisperConfig.EnableMailServer = true
mailboxConfig.WhisperConfig.PasswordFile = filepath.Join(RootDir, "/static/keys/wnodepassword")
mailboxConfig.WhisperConfig.MailServerPasswordFile = filepath.Join(RootDir, "/static/keys/wnodepassword")
mailboxConfig.WhisperConfig.DataDir = filepath.Join(datadir, "data")
mailboxConfig.DataDir = datadir