status-go/protocol/communities/community_description_encry...

119 lines
4.0 KiB
Go

package communities
import (
"go.uber.org/zap"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/protobuf"
)
type DescriptionEncryptor interface {
encryptCommunityDescription(community *Community, d *protobuf.CommunityDescription) (string, []byte, error)
encryptCommunityDescriptionChannel(community *Community, channelID string, d *protobuf.CommunityDescription) (string, []byte, error)
decryptCommunityDescription(keyIDSeqNo string, d []byte) (*DecryptCommunityResponse, error)
}
// Encrypts members and chats
func encryptDescription(encryptor DescriptionEncryptor, community *Community, description *protobuf.CommunityDescription) error {
description.PrivateData = make(map[string][]byte)
for channelID, channel := range description.Chats {
if !community.channelEncrypted(channelID) {
continue
}
descriptionToEncrypt := &protobuf.CommunityDescription{
Chats: map[string]*protobuf.CommunityChat{
channelID: channel,
},
}
keyIDSeqNo, encryptedDescription, err := encryptor.encryptCommunityDescriptionChannel(community, channelID, descriptionToEncrypt)
if err != nil {
return err
}
// Set private data and cleanup unencrypted channel's members
description.PrivateData[keyIDSeqNo] = encryptedDescription
channel.Members = make(map[string]*protobuf.CommunityMember)
}
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)
if err != nil {
return err
}
// 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
}
type CommunityPrivateDataFailedToDecrypt struct {
GroupID []byte
KeyID []byte
}
// Decrypts members and chats
func decryptDescription(id types.HexBytes, encryptor DescriptionEncryptor, description *protobuf.CommunityDescription, logger *zap.Logger) ([]*CommunityPrivateDataFailedToDecrypt, error) {
if len(description.PrivateData) == 0 {
return nil, nil
}
var failedToDecrypt []*CommunityPrivateDataFailedToDecrypt
for keyIDSeqNo, encryptedDescription := range description.PrivateData {
decryptedDescriptionResponse, err := encryptor.decryptCommunityDescription(keyIDSeqNo, encryptedDescription)
if decryptedDescriptionResponse != nil && !decryptedDescriptionResponse.Decrypted {
failedToDecrypt = append(failedToDecrypt, &CommunityPrivateDataFailedToDecrypt{GroupID: id, KeyID: decryptedDescriptionResponse.KeyID})
}
if err != nil {
// ignore error, try to decrypt next data
logger.Debug("failed to decrypt community private data", zap.String("keyIDSeqNo", keyIDSeqNo), zap.Error(err))
continue
}
decryptedDescription := decryptedDescriptionResponse.Description
if len(decryptedDescription.Members) > 0 {
description.Members = decryptedDescription.Members
}
if decryptedDescription.ActiveMembersCount > 0 {
description.ActiveMembersCount = decryptedDescription.ActiveMembersCount
}
for id, decryptedChannel := range decryptedDescription.Chats {
if description.Chats == nil {
description.Chats = make(map[string]*protobuf.CommunityChat)
}
if channel := description.Chats[id]; channel != nil {
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
}