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
}
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 {
return o.config.CommunityDescriptionProtocolMessage
}

View File

@ -1,8 +1,6 @@
package communities
import (
"github.com/golang/protobuf/proto"
"go.uber.org/zap"
"github.com/status-im/status-go/eth-node/types"
@ -26,7 +24,7 @@ func encryptDescription(encryptor DescriptionEncryptor, community *Community, de
descriptionToEncrypt := &protobuf.CommunityDescription{
Chats: map[string]*protobuf.CommunityChat{
channelID: proto.Clone(channel).(*protobuf.CommunityChat),
channelID: channel,
},
}
@ -43,7 +41,9 @@ func encryptDescription(encryptor DescriptionEncryptor, community *Community, de
if community.Encrypted() {
descriptionToEncrypt := &protobuf.CommunityDescription{
Members: description.Members,
ActiveMembersCount: description.ActiveMembersCount,
Chats: description.Chats,
Categories: description.Categories,
}
keyIDSeqNo, encryptedDescription, err := encryptor.encryptCommunityDescription(community, descriptionToEncrypt)
@ -51,10 +51,12 @@ func encryptDescription(encryptor DescriptionEncryptor, community *Community, de
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.Members = make(map[string]*protobuf.CommunityMember)
description.ActiveMembersCount = 0
description.Chats = make(map[string]*protobuf.CommunityChat)
description.Categories = make(map[string]*protobuf.CommunityCategory)
}
return nil
@ -85,11 +87,12 @@ func decryptDescription(id types.HexBytes, encryptor DescriptionEncryptor, descr
}
decryptedDescription := decryptedDescriptionResponse.Description
for pk, member := range decryptedDescription.Members {
if description.Members == nil {
description.Members = make(map[string]*protobuf.CommunityMember)
if len(decryptedDescription.Members) > 0 {
description.Members = decryptedDescription.Members
}
description.Members[pk] = member
if decryptedDescription.ActiveMembersCount > 0 {
description.ActiveMembersCount = decryptedDescription.ActiveMembersCount
}
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 len(channel.Members) == 0 {
if len(decryptedChannel.Members) > 0 {
channel.Members = decryptedChannel.Members
}
} else {
description.Chats[id] = decryptedChannel
}
}
if len(decryptedDescription.Categories) > 0 {
description.Categories = decryptedDescription.Categories
}
}
return failedToDecrypt, nil

View File

@ -50,13 +50,13 @@ type DescriptionEncryptorMock struct {
func (dem *DescriptionEncryptorMock) encryptCommunityDescription(community *Community, d *protobuf.CommunityDescription) (string, []byte, error) {
keyIDSeqNo := uuid.New().String()
dem.descriptions[keyIDSeqNo] = d
dem.descriptions[keyIDSeqNo] = proto.Clone(d).(*protobuf.CommunityDescription)
return keyIDSeqNo, []byte("encryptedDescription"), nil
}
func (dem *DescriptionEncryptorMock) encryptCommunityDescriptionChannel(community *Community, channelID string, d *protobuf.CommunityDescription) (string, []byte, error) {
keyIDSeqNo := uuid.New().String()
dem.descriptions[keyIDSeqNo] = d
dem.descriptions[keyIDSeqNo] = proto.Clone(d).(*protobuf.CommunityDescription)
dem.channelIDToKeyIDSeqNo[channelID] = keyIDSeqNo
return keyIDSeqNo, []byte("encryptedDescription"), nil
}
@ -86,6 +86,7 @@ func (s *CommunityEncryptionDescriptionSuite) description() *protobuf.CommunityD
"memberA": &protobuf.CommunityMember{},
"memberB": &protobuf.CommunityMember{},
},
ActiveMembersCount: 1,
Chats: map[string]*protobuf.CommunityChat{
"channelA": &protobuf.CommunityChat{
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{},
// ensure community and channel encryption
@ -128,18 +136,23 @@ func (s *CommunityEncryptionDescriptionSuite) TestEncryptionDecryption() {
s.Require().NoError(err)
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.ActiveMembersCount)
s.Require().Empty(description.Chats)
s.Require().Empty(description.Categories)
s.Require().Equal(description.IntroMessage, "one of not encrypted fields")
// members and chats should be brought back
_, err = decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger)
s.Require().NoError(err)
s.Require().Len(description.Members, 2)
s.Require().EqualValues(description.ActiveMembersCount, 1)
s.Require().Len(description.Chats, 2)
s.Require().Len(description.Chats["channelA"].Members, 2)
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")
}
@ -163,19 +176,24 @@ func (s *CommunityEncryptionDescriptionSuite) TestDecryption_NoKeys() {
_, err := decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger)
s.Require().NoError(err)
s.Require().Len(description.Members, 2)
s.Require().EqualValues(description.ActiveMembersCount, 1)
s.Require().Len(description.Chats, 2)
s.Require().Len(description.Chats["channelA"].Members, 2)
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")
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()
// members and chats should be empty
// members, chats, categories should be empty
_, err = decryptDescription([]byte("some-id"), s.descriptionEncryptor, description, s.logger)
s.Require().NoError(err)
s.Require().Empty(description.Members)
s.Require().Empty(description.ActiveMembersCount)
s.Require().Empty(description.Chats)
s.Require().Empty(description.Categories)
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{
Clock: community.Clock(),
Accepted: true,
CommunityId: community.ID(),
Community: community.Description(),
Community: encryptedDescription,
Grant: grant,
ProtectedTopicPrivateKey: crypto.FromECDSA(key),
Shard: community.Shard().Protobuffer(),