2019-06-03 14:29:14 +00:00
|
|
|
package publisher
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2019-07-01 09:39:51 +00:00
|
|
|
"path/filepath"
|
2019-06-03 14:29:14 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
2019-07-01 09:39:51 +00:00
|
|
|
"github.com/status-im/status-go/messaging/chat"
|
2019-06-26 18:17:41 +00:00
|
|
|
"github.com/status-im/status-go/messaging/filter"
|
2019-07-03 19:13:11 +00:00
|
|
|
"github.com/status-im/status-go/messaging/multidevice"
|
|
|
|
"github.com/status-im/status-go/messaging/sharedsecret"
|
2019-06-03 14:29:14 +00:00
|
|
|
"github.com/status-im/status-go/services/shhext/whisperutils"
|
|
|
|
whisper "github.com/status-im/whisper/whisperv6"
|
|
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestServiceTestSuite(t *testing.T) {
|
|
|
|
suite.Run(t, new(ServiceTestSuite))
|
|
|
|
}
|
|
|
|
|
|
|
|
type TestKey struct {
|
|
|
|
privateKey *ecdsa.PrivateKey
|
|
|
|
keyID string
|
|
|
|
publicKeyBytes hexutil.Bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
type ServiceTestSuite struct {
|
|
|
|
suite.Suite
|
2019-07-01 09:39:51 +00:00
|
|
|
alice *Publisher
|
|
|
|
bob *Publisher
|
2019-06-03 14:29:14 +00:00
|
|
|
aliceKey *TestKey
|
|
|
|
bobKey *TestKey
|
|
|
|
}
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
func (s *ServiceTestSuite) createPublisher(installationID string) (*Publisher, *TestKey) {
|
|
|
|
dir, err := ioutil.TempDir("", "publisher-test")
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
config := Config{PFSEnabled: true}
|
2019-06-03 14:29:14 +00:00
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
whisper := whisper.New(nil)
|
|
|
|
err = whisper.SetMinimumPoW(0)
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
publisher := New(whisper, config)
|
2019-06-03 14:29:14 +00:00
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
pk, err := crypto.GenerateKey()
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
keyID, err := whisper.AddKeyPair(pk)
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
testKey := TestKey{
|
|
|
|
privateKey: pk,
|
|
|
|
keyID: keyID,
|
|
|
|
publicKeyBytes: crypto.FromECDSAPub(&pk.PublicKey),
|
2019-06-03 14:29:14 +00:00
|
|
|
}
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
persistence, err := chat.NewSQLLitePersistence(filepath.Join(dir, "db1.sql"), "pass")
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
sharedSecretService := sharedsecret.NewService(persistence.GetSharedSecretStorage())
|
2019-06-03 14:29:14 +00:00
|
|
|
|
2019-07-01 10:00:46 +00:00
|
|
|
filterService := filter.New(whisper, filter.NewSQLLitePersistence(persistence.DB), sharedSecretService, func([]*filter.Messages) {})
|
2019-06-03 14:29:14 +00:00
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
multideviceConfig := &multidevice.Config{
|
|
|
|
InstallationID: installationID,
|
|
|
|
ProtocolVersion: chat.ProtocolVersion,
|
|
|
|
MaxInstallations: 3,
|
2019-06-03 14:29:14 +00:00
|
|
|
}
|
2019-07-01 09:39:51 +00:00
|
|
|
multideviceService := multidevice.New(multideviceConfig, persistence.GetMultideviceStorage())
|
2019-06-03 14:29:14 +00:00
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
protocolService := chat.NewProtocolService(
|
|
|
|
chat.NewEncryptionService(
|
|
|
|
persistence,
|
|
|
|
chat.DefaultEncryptionServiceConfig(installationID)),
|
|
|
|
sharedSecretService,
|
|
|
|
multideviceService,
|
|
|
|
func(addedBundles []*multidevice.Installation) {},
|
|
|
|
func(sharedSecrets []*sharedsecret.Secret) {
|
|
|
|
for _, sharedSecret := range sharedSecrets {
|
|
|
|
_, _ = filterService.ProcessNegotiatedSecret(sharedSecret)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
2019-06-03 14:29:14 +00:00
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
publisher.Init(persistence.DB, protocolService, filterService)
|
2019-06-03 14:29:14 +00:00
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
err = publisher.Start(func() bool { return true }, false)
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
return publisher, &testKey
|
|
|
|
}
|
2019-06-03 14:29:14 +00:00
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
func (s *ServiceTestSuite) SetupTest() {
|
|
|
|
s.alice, s.aliceKey = s.createPublisher("installation-1")
|
|
|
|
_, err := s.alice.LoadFilters([]*filter.Chat{})
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
s.bob, s.bobKey = s.createPublisher("installation-2")
|
|
|
|
_, err = s.bob.LoadFilters([]*filter.Chat{})
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ServiceTestSuite) TestCreateDirectMessage() {
|
2019-07-03 19:13:11 +00:00
|
|
|
newMessage, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello"))
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
message := &whisper.Message{
|
|
|
|
Sig: s.aliceKey.publicKeyBytes,
|
|
|
|
Topic: newMessage.Topic,
|
|
|
|
Payload: newMessage.Payload,
|
|
|
|
Dst: newMessage.PublicKey,
|
|
|
|
}
|
|
|
|
|
2019-07-01 09:39:51 +00:00
|
|
|
err = s.bob.ProcessMessage(message, []byte("1"))
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal([]byte("hello"), message.Payload)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ServiceTestSuite) TestTopic() {
|
|
|
|
// We build an initial message
|
2019-07-03 19:13:11 +00:00
|
|
|
newMessage1, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello"))
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
message1 := &whisper.Message{
|
|
|
|
Sig: s.aliceKey.publicKeyBytes,
|
|
|
|
Topic: newMessage1.Topic,
|
|
|
|
Payload: newMessage1.Payload,
|
|
|
|
Dst: newMessage1.PublicKey,
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have no information, it should use the discovery topic
|
|
|
|
s.Require().Equal(whisperutils.DiscoveryTopicBytes, message1.Topic)
|
|
|
|
|
|
|
|
// We build a contact code from user 2
|
|
|
|
newMessage2, err := s.bob.sendContactCode()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().NotNil(newMessage2)
|
|
|
|
|
|
|
|
message2 := &whisper.Message{
|
|
|
|
Sig: s.bobKey.publicKeyBytes,
|
|
|
|
Topic: newMessage2.Topic,
|
|
|
|
Payload: newMessage2.Payload,
|
|
|
|
Dst: newMessage2.PublicKey,
|
|
|
|
}
|
|
|
|
|
|
|
|
// We receive the contact code
|
2019-07-01 09:39:51 +00:00
|
|
|
err = s.alice.ProcessMessage(message2, []byte("1"))
|
|
|
|
s.Require().EqualError(err, chat.ErrNoPayload.Error())
|
2019-06-03 14:29:14 +00:00
|
|
|
|
|
|
|
// We build another message, this time it should use the partitioned topic
|
2019-07-03 19:13:11 +00:00
|
|
|
newMessage3, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello"))
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
message3 := &whisper.Message{
|
|
|
|
Sig: s.aliceKey.publicKeyBytes,
|
|
|
|
Topic: newMessage3.Topic,
|
|
|
|
Payload: newMessage3.Payload,
|
|
|
|
Dst: newMessage3.PublicKey,
|
|
|
|
}
|
|
|
|
expectedTopic3 := whisper.BytesToTopic(filter.PublicKeyToPartitionedTopicBytes(&s.bobKey.privateKey.PublicKey))
|
|
|
|
|
|
|
|
s.Require().Equal(expectedTopic3, message3.Topic)
|
|
|
|
|
|
|
|
// We receive the message
|
2019-07-01 09:39:51 +00:00
|
|
|
err = s.bob.ProcessMessage(message3, []byte("1"))
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
// We build another message, this time it should use the negotiated topic
|
2019-07-03 19:13:11 +00:00
|
|
|
newMessage4, err := s.bob.CreateDirectMessage(s.bobKey.privateKey, &s.aliceKey.privateKey.PublicKey, false, []byte("hello"))
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
message4 := &whisper.Message{
|
|
|
|
Sig: s.bobKey.publicKeyBytes,
|
|
|
|
Topic: newMessage4.Topic,
|
|
|
|
Payload: newMessage4.Payload,
|
|
|
|
Dst: newMessage4.PublicKey,
|
|
|
|
}
|
|
|
|
sharedSecret, err := ecies.ImportECDSA(s.bobKey.privateKey).GenerateShared(
|
|
|
|
ecies.ImportECDSAPublic(&s.aliceKey.privateKey.PublicKey),
|
|
|
|
16,
|
|
|
|
16)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
keyString := fmt.Sprintf("%x", sharedSecret)
|
|
|
|
|
|
|
|
negotiatedTopic := whisper.BytesToTopic(filter.ToTopic(keyString))
|
|
|
|
|
|
|
|
s.Require().Equal(negotiatedTopic, message4.Topic)
|
|
|
|
|
|
|
|
// We receive the message
|
2019-07-01 09:39:51 +00:00
|
|
|
err = s.alice.ProcessMessage(message4, []byte("1"))
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
// Alice sends another message to Bob, this time it should use the negotiated topic
|
2019-07-03 19:13:11 +00:00
|
|
|
newMessage5, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello"))
|
2019-06-03 14:29:14 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
message5 := &whisper.Message{
|
|
|
|
Sig: s.aliceKey.publicKeyBytes,
|
|
|
|
Topic: newMessage5.Topic,
|
|
|
|
Payload: newMessage5.Payload,
|
|
|
|
Dst: newMessage5.PublicKey,
|
|
|
|
}
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal(negotiatedTopic, message5.Topic)
|
|
|
|
|
|
|
|
}
|