fix: encrypt community's Categories and ActiveMembersCount

fixes: #4943
fixes: #4944
This commit is contained in:
Patryk Osmaczko 2024-03-20 20:18:18 +01:00 committed by Jonathan Rainville
parent 894eb5758e
commit 0aed93ff04
4 changed files with 59 additions and 18 deletions

View File

@ -1427,6 +1427,17 @@ func (o *Community) Description() *protobuf.CommunityDescription {
return o.config.CommunityDescription return o.config.CommunityDescription
} }
func (o *Community) EncryptedDescription() (*protobuf.CommunityDescription, error) {
clone := proto.Clone(o.config.CommunityDescription).(*protobuf.CommunityDescription)
if o.encryptor != nil {
err := encryptDescription(o.encryptor, o, clone)
if err != nil {
return nil, err
}
}
return clone, nil
}
func (o *Community) DescriptionProtocolMessage() []byte { func (o *Community) DescriptionProtocolMessage() []byte {
return o.config.CommunityDescriptionProtocolMessage return o.config.CommunityDescriptionProtocolMessage
} }

View File

@ -1,8 +1,6 @@
package communities package communities
import ( import (
"github.com/golang/protobuf/proto"
"go.uber.org/zap" "go.uber.org/zap"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
@ -26,7 +24,7 @@ func encryptDescription(encryptor DescriptionEncryptor, community *Community, de
descriptionToEncrypt := &protobuf.CommunityDescription{ descriptionToEncrypt := &protobuf.CommunityDescription{
Chats: map[string]*protobuf.CommunityChat{ Chats: map[string]*protobuf.CommunityChat{
channelID: proto.Clone(channel).(*protobuf.CommunityChat), channelID: channel,
}, },
} }
@ -42,8 +40,10 @@ func encryptDescription(encryptor DescriptionEncryptor, community *Community, de
if community.Encrypted() { if community.Encrypted() {
descriptionToEncrypt := &protobuf.CommunityDescription{ descriptionToEncrypt := &protobuf.CommunityDescription{
Members: description.Members, Members: description.Members,
Chats: description.Chats, ActiveMembersCount: description.ActiveMembersCount,
Chats: description.Chats,
Categories: description.Categories,
} }
keyIDSeqNo, encryptedDescription, err := encryptor.encryptCommunityDescription(community, descriptionToEncrypt) keyIDSeqNo, encryptedDescription, err := encryptor.encryptCommunityDescription(community, descriptionToEncrypt)
@ -51,10 +51,12 @@ func encryptDescription(encryptor DescriptionEncryptor, community *Community, de
return err return err
} }
// Set private data and cleanup unencrypted members and chats // Set private data and cleanup unencrypted members, chats and categories
description.PrivateData[keyIDSeqNo] = encryptedDescription description.PrivateData[keyIDSeqNo] = encryptedDescription
description.Members = make(map[string]*protobuf.CommunityMember) description.Members = make(map[string]*protobuf.CommunityMember)
description.ActiveMembersCount = 0
description.Chats = make(map[string]*protobuf.CommunityChat) description.Chats = make(map[string]*protobuf.CommunityChat)
description.Categories = make(map[string]*protobuf.CommunityCategory)
} }
return nil return nil
@ -85,11 +87,12 @@ func decryptDescription(id types.HexBytes, encryptor DescriptionEncryptor, descr
} }
decryptedDescription := decryptedDescriptionResponse.Description decryptedDescription := decryptedDescriptionResponse.Description
for pk, member := range decryptedDescription.Members { if len(decryptedDescription.Members) > 0 {
if description.Members == nil { description.Members = decryptedDescription.Members
description.Members = make(map[string]*protobuf.CommunityMember) }
}
description.Members[pk] = member if decryptedDescription.ActiveMembersCount > 0 {
description.ActiveMembersCount = decryptedDescription.ActiveMembersCount
} }
for id, decryptedChannel := range decryptedDescription.Chats { for id, decryptedChannel := range decryptedDescription.Chats {
@ -98,13 +101,17 @@ func decryptDescription(id types.HexBytes, encryptor DescriptionEncryptor, descr
} }
if channel := description.Chats[id]; channel != nil { if channel := description.Chats[id]; channel != nil {
if len(channel.Members) == 0 { if len(decryptedChannel.Members) > 0 {
channel.Members = decryptedChannel.Members channel.Members = decryptedChannel.Members
} }
} else { } else {
description.Chats[id] = decryptedChannel description.Chats[id] = decryptedChannel
} }
} }
if len(decryptedDescription.Categories) > 0 {
description.Categories = decryptedDescription.Categories
}
} }
return failedToDecrypt, nil return failedToDecrypt, nil

View File

@ -50,13 +50,13 @@ type DescriptionEncryptorMock struct {
func (dem *DescriptionEncryptorMock) encryptCommunityDescription(community *Community, d *protobuf.CommunityDescription) (string, []byte, error) { func (dem *DescriptionEncryptorMock) encryptCommunityDescription(community *Community, d *protobuf.CommunityDescription) (string, []byte, error) {
keyIDSeqNo := uuid.New().String() keyIDSeqNo := uuid.New().String()
dem.descriptions[keyIDSeqNo] = d dem.descriptions[keyIDSeqNo] = proto.Clone(d).(*protobuf.CommunityDescription)
return keyIDSeqNo, []byte("encryptedDescription"), nil return keyIDSeqNo, []byte("encryptedDescription"), nil
} }
func (dem *DescriptionEncryptorMock) encryptCommunityDescriptionChannel(community *Community, channelID string, d *protobuf.CommunityDescription) (string, []byte, error) { func (dem *DescriptionEncryptorMock) encryptCommunityDescriptionChannel(community *Community, channelID string, d *protobuf.CommunityDescription) (string, []byte, error) {
keyIDSeqNo := uuid.New().String() keyIDSeqNo := uuid.New().String()
dem.descriptions[keyIDSeqNo] = d dem.descriptions[keyIDSeqNo] = proto.Clone(d).(*protobuf.CommunityDescription)
dem.channelIDToKeyIDSeqNo[channelID] = keyIDSeqNo dem.channelIDToKeyIDSeqNo[channelID] = keyIDSeqNo
return keyIDSeqNo, []byte("encryptedDescription"), nil return keyIDSeqNo, []byte("encryptedDescription"), nil
} }
@ -86,6 +86,7 @@ func (s *CommunityEncryptionDescriptionSuite) description() *protobuf.CommunityD
"memberA": &protobuf.CommunityMember{}, "memberA": &protobuf.CommunityMember{},
"memberB": &protobuf.CommunityMember{}, "memberB": &protobuf.CommunityMember{},
}, },
ActiveMembersCount: 1,
Chats: map[string]*protobuf.CommunityChat{ Chats: map[string]*protobuf.CommunityChat{
"channelA": &protobuf.CommunityChat{ "channelA": &protobuf.CommunityChat{
Members: map[string]*protobuf.CommunityMember{ Members: map[string]*protobuf.CommunityMember{
@ -99,6 +100,13 @@ func (s *CommunityEncryptionDescriptionSuite) description() *protobuf.CommunityD
}, },
}, },
}, },
Categories: map[string]*protobuf.CommunityCategory{
"categoryA": &protobuf.CommunityCategory{
CategoryId: "categoryA",
Name: "categoryA",
Position: 0,
},
},
PrivateData: map[string][]byte{}, PrivateData: map[string][]byte{},
// ensure community and channel encryption // ensure community and channel encryption
@ -128,18 +136,23 @@ func (s *CommunityEncryptionDescriptionSuite) TestEncryptionDecryption() {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(description.PrivateData, 2) s.Require().Len(description.PrivateData, 2)
// members and chats should become empty (encrypted) // members, chats, categories should become empty (encrypted)
s.Require().Empty(description.Members) s.Require().Empty(description.Members)
s.Require().Empty(description.ActiveMembersCount)
s.Require().Empty(description.Chats) s.Require().Empty(description.Chats)
s.Require().Empty(description.Categories)
s.Require().Equal(description.IntroMessage, "one of not encrypted fields") s.Require().Equal(description.IntroMessage, "one of not encrypted fields")
// members and chats should be brought back // members and chats should be brought back
_, err = decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger) _, err = decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(description.Members, 2) s.Require().Len(description.Members, 2)
s.Require().EqualValues(description.ActiveMembersCount, 1)
s.Require().Len(description.Chats, 2) s.Require().Len(description.Chats, 2)
s.Require().Len(description.Chats["channelA"].Members, 2) s.Require().Len(description.Chats["channelA"].Members, 2)
s.Require().Len(description.Chats["channelB"].Members, 1) s.Require().Len(description.Chats["channelB"].Members, 1)
s.Require().Len(description.Categories, 1)
s.Require().Equal(description.Categories["categoryA"].Name, "categoryA")
s.Require().Equal(description.IntroMessage, "one of not encrypted fields") s.Require().Equal(description.IntroMessage, "one of not encrypted fields")
} }
@ -163,19 +176,24 @@ func (s *CommunityEncryptionDescriptionSuite) TestDecryption_NoKeys() {
_, err := decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger) _, err := decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(description.Members, 2) s.Require().Len(description.Members, 2)
s.Require().EqualValues(description.ActiveMembersCount, 1)
s.Require().Len(description.Chats, 2) s.Require().Len(description.Chats, 2)
s.Require().Len(description.Chats["channelA"].Members, 2) s.Require().Len(description.Chats["channelA"].Members, 2)
s.Require().Len(description.Chats["channelB"].Members, 0) // encrypted channel s.Require().Len(description.Chats["channelB"].Members, 0) // encrypted channel
s.Require().Len(description.Categories, 1)
s.Require().Equal(description.Categories["categoryA"].Name, "categoryA")
s.Require().Equal(description.IntroMessage, "one of not encrypted fields") s.Require().Equal(description.IntroMessage, "one of not encrypted fields")
description = proto.Clone(encryptedDescription).(*protobuf.CommunityDescription) description = proto.Clone(encryptedDescription).(*protobuf.CommunityDescription)
// forget the keys, so chats and members can't be decrypted // forget the keys, so members, chats, categories can't be decrypted
s.descriptionEncryptor.forgetAllKeys() s.descriptionEncryptor.forgetAllKeys()
// members and chats should be empty // members, chats, categories should be empty
_, err = decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger) _, err = decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Empty(description.Members) s.Require().Empty(description.Members)
s.Require().Empty(description.ActiveMembersCount)
s.Require().Empty(description.Chats) s.Require().Empty(description.Chats)
s.Require().Empty(description.Categories)
s.Require().Equal(description.IntroMessage, "one of not encrypted fields") s.Require().Equal(description.IntroMessage, "one of not encrypted fields")
} }

View File

@ -1550,11 +1550,16 @@ func (m *Messenger) acceptRequestToJoinCommunity(requestToJoin *communities.Requ
} }
} }
encryptedDescription, err := community.EncryptedDescription()
if err != nil {
return nil, err
}
requestToJoinResponseProto := &protobuf.CommunityRequestToJoinResponse{ requestToJoinResponseProto := &protobuf.CommunityRequestToJoinResponse{
Clock: community.Clock(), Clock: community.Clock(),
Accepted: true, Accepted: true,
CommunityId: community.ID(), CommunityId: community.ID(),
Community: community.Description(), Community: encryptedDescription,
Grant: grant, Grant: grant,
ProtectedTopicPrivateKey: crypto.FromECDSA(key), ProtectedTopicPrivateKey: crypto.FromECDSA(key),
Shard: community.Shard().Protobuffer(), Shard: community.Shard().Protobuffer(),