mirror of
https://github.com/status-im/status-go.git
synced 2025-02-18 01:37:22 +00:00
Implemented testing to check rekeying is happening
This commit is contained in:
parent
1e09a4bc37
commit
2536d9c8ba
@ -1041,3 +1041,8 @@ func (s *MessageSender) StartDatasync() {
|
|||||||
|
|
||||||
s.datasync = ds
|
s.datasync = ds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetKeyIDsForGroup returns a slice of key IDs belonging to a given group ID
|
||||||
|
func (s *MessageSender) GetKeyIDsForGroup(groupID []byte) ([]uint32, error) {
|
||||||
|
return s.protocol.GetKeyIDsForGroup(groupID)
|
||||||
|
}
|
||||||
|
@ -84,7 +84,7 @@ type Manager struct {
|
|||||||
torrentTasks map[string]metainfo.Hash
|
torrentTasks map[string]metainfo.Hash
|
||||||
historyArchiveDownloadTasks map[string]*HistoryArchiveDownloadTask
|
historyArchiveDownloadTasks map[string]*HistoryArchiveDownloadTask
|
||||||
stopped bool
|
stopped bool
|
||||||
RekeyInterval *time.Duration
|
RekeyInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type HistoryArchiveDownloadTask struct {
|
type HistoryArchiveDownloadTask struct {
|
||||||
|
@ -481,7 +481,7 @@ func (s *PersistenceSuite) TestGetRekeyedAtClock() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Len(communities, 1)
|
s.Require().Len(communities, 1)
|
||||||
|
|
||||||
community := Community{
|
community := &Community{
|
||||||
config: &Config{
|
config: &Config{
|
||||||
PrivateKey: key,
|
PrivateKey: key,
|
||||||
ID: &key.PublicKey,
|
ID: &key.PublicKey,
|
||||||
@ -491,22 +491,23 @@ func (s *PersistenceSuite) TestGetRekeyedAtClock() {
|
|||||||
CommunityDescription: &protobuf.CommunityDescription{},
|
CommunityDescription: &protobuf.CommunityDescription{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
s.Require().NoError(s.db.SaveCommunity(&community))
|
s.Require().NoError(s.db.SaveCommunity(community))
|
||||||
|
|
||||||
communities, err = s.db.AllCommunities(&key.PublicKey)
|
communities, err = s.db.AllCommunities(&key.PublicKey)
|
||||||
|
c := communities[1]
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Len(communities, 2)
|
s.Require().Len(communities, 2)
|
||||||
s.Equal(types.HexBytes(crypto.CompressPubkey(&key.PublicKey)), communities[1].ID())
|
s.Equal(types.HexBytes(crypto.CompressPubkey(&key.PublicKey)), c.ID())
|
||||||
s.True(communities[1].Joined())
|
s.True(c.Joined())
|
||||||
s.True(communities[1].Spectated())
|
s.True(c.Spectated())
|
||||||
s.True(communities[1].Verified())
|
s.True(c.Verified())
|
||||||
s.Zero(communities[1].config.RekeyedAt.Unix())
|
s.Zero(c.config.RekeyedAt.Unix())
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
err = s.db.SetRekeyedAtClock(communities[1].ID(), now)
|
err = s.db.SetRekeyedAtClock(c.ID(), now)
|
||||||
s.NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
then, err := s.db.GetRekeyedAtClock(communities[0].ID())
|
then, err := s.db.GetRekeyedAtClock(c.ID())
|
||||||
s.NoError(err)
|
s.Require().NoError(err)
|
||||||
now.Equal(then)
|
s.Require().True(now.Equal(then))
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,9 @@ func (s *MessengerCommunitiesSuite) SetupTest() {
|
|||||||
s.admin = s.newMessenger()
|
s.admin = s.newMessenger()
|
||||||
s.bob = s.newMessenger()
|
s.bob = s.newMessenger()
|
||||||
s.alice = s.newMessenger()
|
s.alice = s.newMessenger()
|
||||||
|
|
||||||
|
s.admin.communitiesManager.RekeyInterval = 50 * time.Millisecond
|
||||||
|
|
||||||
_, err := s.admin.Start()
|
_, err := s.admin.Start()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
_, err = s.bob.Start()
|
_, err = s.bob.Start()
|
||||||
@ -3422,3 +3425,68 @@ func (s *MessengerCommunitiesSuite) TestGetCommunityIdFromKey() {
|
|||||||
communityID = s.bob.GetCommunityIDFromKey(privateKey)
|
communityID = s.bob.GetCommunityIDFromKey(privateKey)
|
||||||
s.Require().Equal(communityID, publicKey)
|
s.Require().Equal(communityID, publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MessengerCommunitiesSuite) TestStartCommunityRekeyLoop() {
|
||||||
|
// Create a new community
|
||||||
|
response, err := s.admin.CreateCommunity(
|
||||||
|
&requests.CreateCommunity{
|
||||||
|
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
|
||||||
|
Name: "status",
|
||||||
|
Color: "#57a7e5",
|
||||||
|
Description: "status community description",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().Len(response.Communities(), 1)
|
||||||
|
|
||||||
|
// Check community is present in the DB and has default values we care about
|
||||||
|
c, err := s.admin.GetCommunityByID(response.Communities()[0].ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().False(c.Encrypted())
|
||||||
|
s.Require().Zero(c.RekeyedAt().Unix())
|
||||||
|
|
||||||
|
// Update the community to use encryption and check the values
|
||||||
|
err = s.admin.UpdateCommunityEncryption(c, true)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
c, err = s.admin.GetCommunityByID(c.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(c.Encrypted())
|
||||||
|
|
||||||
|
// Add Alice and Bob to the community
|
||||||
|
response, err = s.admin.InviteUsersToCommunity(
|
||||||
|
&requests.InviteUsersToCommunity{
|
||||||
|
CommunityID: c.ID(),
|
||||||
|
Users: []types.HexBytes{
|
||||||
|
common.PubkeyToHexBytes(&s.alice.identity.PublicKey),
|
||||||
|
common.PubkeyToHexBytes(&s.bob.identity.PublicKey),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().Len(response.Communities(), 1)
|
||||||
|
|
||||||
|
// Check the Alice and Bob are members of the community
|
||||||
|
c, err = s.admin.GetCommunityByID(c.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(c.HasMember(&s.alice.identity.PublicKey))
|
||||||
|
s.Require().True(c.HasMember(&s.bob.identity.PublicKey))
|
||||||
|
|
||||||
|
// Check the keys in the database
|
||||||
|
keys, err := s.admin.sender.GetKeyIDsForGroup(c.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
keyCount := len(keys)
|
||||||
|
|
||||||
|
// Check that rekeying is occurring by counting the number of keyIDs in the encryptor's DB
|
||||||
|
// This test could be flaky, as the rekey function may not be finished before RekeyInterval * 2 has passed
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
time.Sleep(s.admin.communitiesManager.RekeyInterval * 2)
|
||||||
|
keys, err = s.admin.sender.GetKeyIDsForGroup(c.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Greater(len(keys), keyCount)
|
||||||
|
keyCount = len(keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -245,6 +245,11 @@ func (p *Protocol) GetAllHREncodedKeys(groupID []byte) ([]byte, error) {
|
|||||||
return p.GetHREncodedKeys(groupID, keyIDs)
|
return p.GetHREncodedKeys(groupID, keyIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetKeyIDsForGroup returns a slice of key IDs belonging to a given group ID
|
||||||
|
func (p *Protocol) GetKeyIDsForGroup(groupID []byte) ([]uint32, error) {
|
||||||
|
return p.encryptor.persistence.GetKeyIDsForGroup(groupID)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Protocol) GetHREncodedKeys(groupID []byte, keyIDs []uint32) ([]byte, error) {
|
func (p *Protocol) GetHREncodedKeys(groupID []byte, keyIDs []uint32) ([]byte, error) {
|
||||||
keys := &HRKeys{}
|
keys := &HRKeys{}
|
||||||
for _, keyID := range keyIDs {
|
for _, keyID := range keyIDs {
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
_errors "errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -237,7 +236,8 @@ func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscripti
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sub.MemberPermissionsCheckedSignal != nil {
|
if sub.MemberPermissionsCheckedSignal != nil {
|
||||||
err := m.UpdateCommunityEncryption(sub.Community)
|
becomeMemberPermissions := sub.Community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
|
||||||
|
err := m.UpdateCommunityEncryption(sub.Community, len(becomeMemberPermissions) > 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Warn("failed to update community encryption", zap.Error(err))
|
m.logger.Warn("failed to update community encryption", zap.Error(err))
|
||||||
}
|
}
|
||||||
@ -3315,7 +3315,7 @@ func (m *Messenger) RequestImportDiscordCommunity(request *requests.ImportDiscor
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
m.cleanUpImport(communityID)
|
m.cleanUpImport(communityID)
|
||||||
errmsg := err.Error()
|
errmsg := err.Error()
|
||||||
if _errors.Is(err, communities.ErrInvalidCommunityDescriptionDuplicatedName) {
|
if errors.Is(err, communities.ErrInvalidCommunityDescriptionDuplicatedName) {
|
||||||
errmsg = fmt.Sprintf("Couldn't create channel '%s': %s", communityChat.Identity.DisplayName, err.Error())
|
errmsg = fmt.Sprintf("Couldn't create channel '%s': %s", communityChat.Identity.DisplayName, err.Error())
|
||||||
}
|
}
|
||||||
importProgress.AddTaskError(discord.ChannelsCreationTask, discord.Error(errmsg))
|
importProgress.AddTaskError(discord.ChannelsCreationTask, discord.Error(errmsg))
|
||||||
@ -4081,26 +4081,25 @@ func (m *Messenger) UpdateCommunityTokenSupply(chainID int, contractAddress stri
|
|||||||
return m.communitiesManager.UpdateCommunityTokenSupply(chainID, contractAddress, supply)
|
return m.communitiesManager.UpdateCommunityTokenSupply(chainID, contractAddress, supply)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCommunityEncryption takes a community and encrypts / decrypts the community
|
// UpdateCommunityEncryption takes a communityID string and an encryption state, then finds the community and
|
||||||
// based on TokenPermission. Community is republished along with any keys if needed.
|
// encrypts / decrypts the community. Community is republished along with any keys if needed.
|
||||||
//
|
//
|
||||||
// Note: This function cannot decrypt previously encrypted messages, and it cannot encrypt previous unencrypted messages.
|
// Note: This function cannot decrypt previously encrypted messages, and it cannot encrypt previous unencrypted messages.
|
||||||
// This functionality introduces some race conditions:
|
// This functionality introduces some race conditions:
|
||||||
// - community description is processed by members before the receiving the key exchange messages
|
// - community description is processed by members before the receiving the key exchange messages
|
||||||
// - members maybe sending encrypted messages after the community description is updated and a new member joins
|
// - members maybe sending encrypted messages after the community description is updated and a new member joins
|
||||||
func (m *Messenger) UpdateCommunityEncryption(community *communities.Community) error {
|
func (m *Messenger) UpdateCommunityEncryption(community *communities.Community, useEncryption bool) error {
|
||||||
if community == nil {
|
if community == nil {
|
||||||
return errors.New("community is nil")
|
return errors.New("community is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
|
// Check isEncrypted is different to Community's value
|
||||||
isEncrypted := len(becomeMemberPermissions) > 0
|
// If not different return
|
||||||
|
if community.Encrypted() == useEncryption {
|
||||||
if community.Encrypted() == isEncrypted {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if isEncrypted {
|
if useEncryption {
|
||||||
// 🪄 The magic that encrypts a community
|
// 🪄 The magic that encrypts a community
|
||||||
_, err := m.encryptor.GenerateHashRatchetKey(community.ID())
|
_, err := m.encryptor.GenerateHashRatchetKey(community.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -4116,7 +4115,7 @@ func (m *Messenger) UpdateCommunityEncryption(community *communities.Community)
|
|||||||
// 🧙 There is no magic that decrypts a community, we just need to tell everyone to not use encryption
|
// 🧙 There is no magic that decrypts a community, we just need to tell everyone to not use encryption
|
||||||
|
|
||||||
// Republish the community.
|
// Republish the community.
|
||||||
community.SetEncrypted(isEncrypted)
|
community.SetEncrypted(useEncryption)
|
||||||
err := m.communitiesManager.UpdateCommunity(community)
|
err := m.communitiesManager.UpdateCommunity(community)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -4229,7 +4228,14 @@ func chunkAttachmentsByByteSize(slice []*protobuf.DiscordMessageAttachment, maxF
|
|||||||
func (m *Messenger) startCommunityRekeyLoop() {
|
func (m *Messenger) startCommunityRekeyLoop() {
|
||||||
logger := m.logger.Named("CommunityRekeyLoop")
|
logger := m.logger.Named("CommunityRekeyLoop")
|
||||||
|
|
||||||
ticker := time.NewTicker(5 * time.Minute)
|
var d time.Duration
|
||||||
|
if m.communitiesManager.RekeyInterval != 0 {
|
||||||
|
d = m.communitiesManager.RekeyInterval / 10
|
||||||
|
} else {
|
||||||
|
d = 5 * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(d)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -4256,10 +4262,10 @@ func (m *Messenger) rekeyAllCommunities(logger *zap.Logger) {
|
|||||||
// default to one hour
|
// default to one hour
|
||||||
// TODO in future perhaps have a community level rki rather than a global rki
|
// TODO in future perhaps have a community level rki rather than a global rki
|
||||||
var rki time.Duration
|
var rki time.Duration
|
||||||
if m.communitiesManager.RekeyInterval == nil {
|
if m.communitiesManager.RekeyInterval == 0 {
|
||||||
rki = time.Hour
|
rki = time.Hour
|
||||||
} else {
|
} else {
|
||||||
rki = *m.communitiesManager.RekeyInterval
|
rki = m.communitiesManager.RekeyInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get and loop over all communities in persistence
|
// Get and loop over all communities in persistence
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE communities_communities ADD COLUMN rekeyed_at TIMESTAMP DEFAULT 0 NOT NULL;
|
Loading…
x
Reference in New Issue
Block a user