chore: enable community rekey loop
This commit is contained in:
parent
a38b34ae49
commit
e304fe3344
|
@ -1547,6 +1547,10 @@ func (o *Community) HasTokenPermissions() bool {
|
|||
return len(o.tokenPermissions()) > 0
|
||||
}
|
||||
|
||||
func (o *Community) ChannelEncrypted(channelID string) bool {
|
||||
return o.ChannelHasTokenPermissions(o.IDString() + channelID)
|
||||
}
|
||||
|
||||
func (o *Community) ChannelHasTokenPermissions(chatID string) bool {
|
||||
o.mutex.Lock()
|
||||
defer o.mutex.Unlock()
|
||||
|
|
|
@ -3311,7 +3311,8 @@ func (m *Manager) IsChannelEncrypted(communityID string, chatID string) (bool, e
|
|||
return false, err
|
||||
}
|
||||
|
||||
return community.ChannelHasTokenPermissions(chatID), nil
|
||||
channelID := strings.TrimPrefix(chatID, communityID)
|
||||
return community.ChannelEncrypted(channelID), nil
|
||||
}
|
||||
|
||||
func (m *Manager) ShouldHandleSyncCommunity(community *protobuf.SyncInstallationCommunity) (bool, error) {
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
|
||||
type CommunitiesKeyDistributor interface {
|
||||
Distribute(community *communities.Community, keyActions *communities.EncryptionKeyActions) error
|
||||
Rekey(community *communities.Community) error
|
||||
}
|
||||
|
||||
type CommunitiesKeyDistributorImpl struct {
|
||||
|
@ -41,32 +40,6 @@ func (ckd *CommunitiesKeyDistributorImpl) Distribute(community *communities.Comm
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ckd *CommunitiesKeyDistributorImpl) Rekey(community *communities.Community) error {
|
||||
if !community.IsControlNode() {
|
||||
return communities.ErrNotControlNode
|
||||
}
|
||||
|
||||
err := ckd.distributeKey(community, community.ID(), &communities.EncryptionKeyAction{
|
||||
ActionType: communities.EncryptionKeyRekey,
|
||||
Members: community.Members(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for channelID, channel := range community.Chats() {
|
||||
err := ckd.distributeKey(community, []byte(community.IDString()+channelID), &communities.EncryptionKeyAction{
|
||||
ActionType: communities.EncryptionKeyRekey,
|
||||
Members: channel.Members,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ckd *CommunitiesKeyDistributorImpl) distributeKey(community *communities.Community, hashRatchetGroupID []byte, keyAction *communities.EncryptionKeyAction) error {
|
||||
pubkeys := make([]*ecdsa.PublicKey, len(keyAction.Members))
|
||||
i := 0
|
||||
|
@ -77,15 +50,7 @@ func (ckd *CommunitiesKeyDistributorImpl) distributeKey(community *communities.C
|
|||
|
||||
switch keyAction.ActionType {
|
||||
case communities.EncryptionKeyAdd:
|
||||
_, err := ckd.encryptor.GenerateHashRatchetKey(hashRatchetGroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ckd.sendKeyExchangeMessage(community, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case communities.EncryptionKeyRekey:
|
||||
err := ckd.sendKeyExchangeMessage(community, hashRatchetGroupID, pubkeys, common.KeyExMsgRekey)
|
||||
|
|
|
@ -63,7 +63,6 @@ func (s *MessengerCommunitiesSuite) SetupTest() {
|
|||
s.bob = s.newMessenger()
|
||||
s.alice = s.newMessenger()
|
||||
|
||||
enableRekeyLoop = true
|
||||
s.owner.communitiesManager.RekeyInterval = 50 * time.Millisecond
|
||||
|
||||
_, err := s.owner.Start()
|
||||
|
@ -3352,28 +3351,12 @@ func (t *testPermissionChecker) CheckPermissions(permissions []*communities.Comm
|
|||
}
|
||||
|
||||
func (s *MessengerCommunitiesSuite) TestStartCommunityRekeyLoop() {
|
||||
// Create a new community
|
||||
response, err := s.owner.CreateCommunity(
|
||||
&requests.CreateCommunity{
|
||||
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
||||
Name: "status",
|
||||
Color: "#57a7e5",
|
||||
Description: "status community description",
|
||||
},
|
||||
true,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().Len(response.Communities(), 1)
|
||||
community, chat := s.createCommunity()
|
||||
s.Require().False(community.Encrypted())
|
||||
|
||||
// Check community is present in the DB and has default values we care about
|
||||
c, err := s.owner.GetCommunityByID(response.Communities()[0].ID())
|
||||
s.Require().NoError(err)
|
||||
s.Require().False(c.Encrypted())
|
||||
// TODO some check that there are no keys for the community. Alt for s.Require().Zero(c.RekeyedAt().Unix())
|
||||
|
||||
_, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{
|
||||
CommunityID: c.ID(),
|
||||
// Add community permission
|
||||
_, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{
|
||||
CommunityID: community.ID(),
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{{
|
||||
ContractAddresses: map[uint64]string{3: "0x933"},
|
||||
|
@ -3386,37 +3369,57 @@ func (s *MessengerCommunitiesSuite) TestStartCommunityRekeyLoop() {
|
|||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
c, err = s.owner.GetCommunityByID(c.ID())
|
||||
// Add channel permission
|
||||
response, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{
|
||||
CommunityID: community.ID(),
|
||||
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
|
||||
TokenCriteria: []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
ContractAddresses: map[uint64]string{3: "0x933"},
|
||||
Type: protobuf.CommunityTokenType_ERC20,
|
||||
Symbol: "STT",
|
||||
Name: "Status Test Token",
|
||||
Amount: "10",
|
||||
Decimals: 18,
|
||||
},
|
||||
},
|
||||
ChatIds: []string{chat.ID},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(c.Encrypted())
|
||||
|
||||
s.advertiseCommunityTo(c, s.owner, s.bob)
|
||||
s.advertiseCommunityTo(c, s.owner, s.alice)
|
||||
s.Require().Len(response.Communities(), 1)
|
||||
community = response.Communities()[0]
|
||||
s.Require().True(community.Encrypted())
|
||||
s.Require().True(community.ChannelEncrypted(chat.CommunityChatID()))
|
||||
|
||||
s.owner.communitiesManager.PermissionChecker = &testPermissionChecker{}
|
||||
|
||||
s.joinCommunity(c, s.owner, s.bob)
|
||||
s.joinCommunity(c, s.owner, s.alice)
|
||||
s.advertiseCommunityTo(community, s.owner, s.bob)
|
||||
s.advertiseCommunityTo(community, s.owner, s.alice)
|
||||
s.joinCommunity(community, s.owner, s.bob)
|
||||
s.joinCommunity(community, s.owner, s.alice)
|
||||
|
||||
// Check the Alice and Bob are members of the community
|
||||
c, err = s.owner.GetCommunityByID(c.ID())
|
||||
// Check keys in the database
|
||||
communityKeys, err := s.owner.sender.GetKeysForGroup(community.ID())
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(c.HasMember(&s.alice.identity.PublicKey))
|
||||
s.Require().True(c.HasMember(&s.bob.identity.PublicKey))
|
||||
communityKeyCount := len(communityKeys)
|
||||
|
||||
// Check the keys in the database
|
||||
keys, err := s.owner.sender.GetKeysForGroup(c.ID())
|
||||
channelKeys, err := s.owner.sender.GetKeysForGroup([]byte(chat.ID))
|
||||
s.Require().NoError(err)
|
||||
keyCount := len(keys)
|
||||
channelKeyCount := len(channelKeys)
|
||||
|
||||
// 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.owner.communitiesManager.RekeyInterval * 2)
|
||||
keys, err = s.owner.sender.GetKeysForGroup(c.ID())
|
||||
communityKeys, err = s.owner.sender.GetKeysForGroup(community.ID())
|
||||
s.Require().NoError(err)
|
||||
s.Require().Greater(len(keys), keyCount)
|
||||
keyCount = len(keys)
|
||||
s.Require().Greater(len(communityKeys), communityKeyCount)
|
||||
communityKeyCount = len(communityKeys)
|
||||
|
||||
channelKeys, err = s.owner.sender.GetKeysForGroup([]byte(chat.ID))
|
||||
s.Require().NoError(err)
|
||||
s.Require().Greater(len(channelKeys), channelKeyCount)
|
||||
channelKeyCount = len(channelKeys)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ import (
|
|||
"github.com/status-im/status-go/protocol/communities"
|
||||
"github.com/status-im/status-go/protocol/communities/token"
|
||||
"github.com/status-im/status-go/protocol/discord"
|
||||
"github.com/status-im/status-go/protocol/encryption"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/requests"
|
||||
"github.com/status-im/status-go/protocol/transport"
|
||||
|
@ -511,6 +510,10 @@ func (m *Messenger) Communities() ([]*communities.Community, error) {
|
|||
return m.communitiesManager.All()
|
||||
}
|
||||
|
||||
func (m *Messenger) ControlledCommunities() ([]*communities.Community, error) {
|
||||
return m.communitiesManager.Controlled()
|
||||
}
|
||||
|
||||
func (m *Messenger) JoinedCommunities() ([]*communities.Community, error) {
|
||||
return m.communitiesManager.Joined()
|
||||
}
|
||||
|
@ -2302,9 +2305,6 @@ func (m *Messenger) ImportCommunity(ctx context.Context, key *ecdsa.PrivateKey)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// TODO Init hash ratchet for community
|
||||
_, err = m.encryptor.GenerateHashRatchetKey(community.ID())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -5733,33 +5733,8 @@ func chunkAttachmentsByByteSize(slice []*protobuf.DiscordMessageAttachment, maxF
|
|||
return chunks
|
||||
}
|
||||
|
||||
// GetCurrentKeyForGroup returns the latest key timestampID belonging to a key group
|
||||
func (m *Messenger) GetCurrentKeyForGroup(groupID []byte) (*encryption.HashRatchetKeyCompatibility, error) {
|
||||
return m.sender.GetCurrentKeyForGroup(groupID)
|
||||
}
|
||||
|
||||
// RekeyCommunity takes a communities.Community.config.ID and triggers a force rekey event for that community
|
||||
func (m *Messenger) RekeyCommunity(cID types.HexBytes) error {
|
||||
// Get the community as the member list could have changed
|
||||
c, err := m.GetCommunityByID(cID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// RekeyCommunity
|
||||
return m.communitiesKeyDistributor.Rekey(c)
|
||||
}
|
||||
|
||||
// NOTE: disabling rekey loop as it rekeys too aggressively
|
||||
|
||||
var enableRekeyLoop = false
|
||||
|
||||
// startCommunityRekeyLoop creates a 5-minute ticker and starts a routine that attempts to rekey every community every tick
|
||||
func (m *Messenger) startCommunityRekeyLoop() {
|
||||
if !enableRekeyLoop {
|
||||
return
|
||||
}
|
||||
|
||||
logger := m.logger.Named("CommunityRekeyLoop")
|
||||
var d time.Duration
|
||||
if m.communitiesManager.RekeyInterval != 0 {
|
||||
|
@ -5777,7 +5752,7 @@ func (m *Messenger) startCommunityRekeyLoop() {
|
|||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
m.rekeyAllCommunities(logger)
|
||||
m.rekeyCommunities(logger)
|
||||
case <-m.quit:
|
||||
ticker.Stop()
|
||||
logger.Debug("CommunityRekeyLoop stopped")
|
||||
|
@ -5787,47 +5762,60 @@ func (m *Messenger) startCommunityRekeyLoop() {
|
|||
}()
|
||||
}
|
||||
|
||||
// rekeyAllCommunities attempts to rekey every community in persistence.
|
||||
// A community will be rekeyed if it meets all the following criteria:
|
||||
// - Community.IsAdmin()
|
||||
// - Community.Encrypted()
|
||||
// - Community.RekeyedAt().Add(rki).Before(time.Now()) where rki is a defined rekey interval
|
||||
func (m *Messenger) rekeyAllCommunities(logger *zap.Logger) {
|
||||
// Determine the rekey interval, if the value is not set as a property of m.communitiesManager
|
||||
// default to one hour
|
||||
// rekeyCommunities loops over controlled communities and rekeys if rekey interval elapsed
|
||||
func (m *Messenger) rekeyCommunities(logger *zap.Logger) {
|
||||
// TODO in future have a community level rki rather than a global rki
|
||||
/*
|
||||
var rki time.Duration
|
||||
if m.communitiesManager.RekeyInterval == 0 {
|
||||
rki = time.Hour
|
||||
} else {
|
||||
rki = m.communitiesManager.RekeyInterval
|
||||
}*/
|
||||
var rekeyInterval time.Duration
|
||||
if m.communitiesManager.RekeyInterval == 0 {
|
||||
rekeyInterval = 48 * time.Hour
|
||||
} else {
|
||||
rekeyInterval = m.communitiesManager.RekeyInterval
|
||||
}
|
||||
|
||||
// Get and loop over all communities in persistence
|
||||
cs, err := m.Communities()
|
||||
shouldRekey := func(hashRatchetGroupID []byte) bool {
|
||||
key, err := m.sender.GetCurrentKeyForGroup(hashRatchetGroupID)
|
||||
if err != nil {
|
||||
logger.Error("failed to get current hash ratchet key", zap.Error(err))
|
||||
return false
|
||||
}
|
||||
|
||||
keyDistributedAt := time.UnixMilli(int64(key.Timestamp))
|
||||
return time.Now().After(keyDistributedAt.Add(rekeyInterval))
|
||||
}
|
||||
|
||||
controlledCommunities, err := m.ControlledCommunities()
|
||||
if err != nil {
|
||||
logger.Error("error getting communities", zap.Error(err))
|
||||
return
|
||||
}
|
||||
for _, c := range cs {
|
||||
if err != nil {
|
||||
logger.Error("error getting current keyTimestampID for community", zap.Error(err), zap.Binary("community ID", c.ID()))
|
||||
continue
|
||||
|
||||
for _, c := range controlledCommunities {
|
||||
keyActions := &communities.EncryptionKeyActions{
|
||||
CommunityKeyAction: communities.EncryptionKeyAction{},
|
||||
ChannelKeysActions: map[string]communities.EncryptionKeyAction{},
|
||||
}
|
||||
|
||||
// TODO add functionality to encryptor.go that compares the timestamps and returns a bool
|
||||
// c.RekeyedAt().Add(rki).Before(time.Now())
|
||||
// keyTimestampID + rki < time.Now()
|
||||
// Just using the vars that will be used later
|
||||
|
||||
if c.IsControlNode() && c.Encrypted() { // && c.RekeyedAt().Add(rki).Before(time.Now())
|
||||
err := m.RekeyCommunity(c.ID())
|
||||
if err != nil {
|
||||
logger.Error("error sending rekey message", zap.Error(err), zap.Binary("community ID", c.ID()))
|
||||
continue
|
||||
if c.Encrypted() && shouldRekey(c.ID()) {
|
||||
keyActions.CommunityKeyAction = communities.EncryptionKeyAction{
|
||||
ActionType: communities.EncryptionKeyRekey,
|
||||
Members: c.Members(),
|
||||
}
|
||||
}
|
||||
|
||||
for channelID, channel := range c.Chats() {
|
||||
if c.ChannelEncrypted(channelID) && shouldRekey([]byte(c.IDString()+channelID)) {
|
||||
keyActions.ChannelKeysActions[channelID] = communities.EncryptionKeyAction{
|
||||
ActionType: communities.EncryptionKeyRekey,
|
||||
Members: channel.Members,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = m.communitiesKeyDistributor.Distribute(c, keyActions)
|
||||
if err != nil {
|
||||
logger.Error("failed to rekey community", zap.Error(err), zap.String("community ID", c.IDString()))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue