status-go/protocol/communities/community_encryption_key_ac...

154 lines
4.6 KiB
Go

package communities
import (
"github.com/status-im/status-go/protocol/protobuf"
)
type KeyDistributor interface {
Generate(community *Community, keyActions *EncryptionKeyActions) error
Distribute(community *Community, keyActions *EncryptionKeyActions) error
}
type EncryptionKeyActionType int
const (
EncryptionKeyNone EncryptionKeyActionType = iota
EncryptionKeyAdd
EncryptionKeyRemove
EncryptionKeyRekey
EncryptionKeySendToMembers
)
type EncryptionKeyAction struct {
ActionType EncryptionKeyActionType
Members map[string]*protobuf.CommunityMember
RemovedMembers map[string]*protobuf.CommunityMember
}
type EncryptionKeyActions struct {
// community-level encryption key action
CommunityKeyAction EncryptionKeyAction
// channel-level encryption key actions
ChannelKeysActions map[string]EncryptionKeyAction // key is: chatID
}
func EvaluateCommunityEncryptionKeyActions(origin, modified *Community) *EncryptionKeyActions {
if origin == nil {
// `modified` is a new community, create empty `origin` community
origin = &Community{
config: &Config{
ID: modified.config.ID,
CommunityDescription: &protobuf.CommunityDescription{
Members: map[string]*protobuf.CommunityMember{},
Permissions: &protobuf.CommunityPermissions{},
Identity: &protobuf.ChatIdentity{},
Chats: map[string]*protobuf.CommunityChat{},
Categories: map[string]*protobuf.CommunityCategory{},
AdminSettings: &protobuf.CommunityAdminSettings{},
TokenPermissions: map[string]*protobuf.CommunityTokenPermission{},
CommunityTokensMetadata: []*protobuf.CommunityTokenMetadata{},
},
},
}
}
changes := EvaluateCommunityChanges(origin, modified)
result := &EncryptionKeyActions{
CommunityKeyAction: *evaluateCommunityLevelEncryptionKeyAction(origin, modified, changes),
ChannelKeysActions: *evaluateChannelLevelEncryptionKeyActions(origin, modified, changes),
}
return result
}
func evaluateCommunityLevelEncryptionKeyAction(origin, modified *Community, changes *CommunityChanges) *EncryptionKeyAction {
return evaluateEncryptionKeyAction(
origin.Encrypted(),
modified.Encrypted(),
changes.ControlNodeChanged != nil,
modified.config.CommunityDescription.Members,
changes.MembersAdded,
changes.MembersRemoved,
)
}
func evaluateChannelLevelEncryptionKeyActions(origin, modified *Community, changes *CommunityChanges) *map[string]EncryptionKeyAction {
result := make(map[string]EncryptionKeyAction)
for channelID := range modified.config.CommunityDescription.Chats {
membersAdded := make(map[string]*protobuf.CommunityMember)
membersRemoved := make(map[string]*protobuf.CommunityMember)
chatChanges, ok := changes.ChatsModified[channelID]
if ok {
membersAdded = chatChanges.MembersAdded
membersRemoved = chatChanges.MembersRemoved
}
result[channelID] = *evaluateEncryptionKeyAction(
origin.ChannelEncrypted(channelID),
modified.ChannelEncrypted(channelID),
changes.ControlNodeChanged != nil,
modified.config.CommunityDescription.Chats[channelID].Members,
membersAdded,
membersRemoved,
)
}
return &result
}
func evaluateEncryptionKeyAction(originEncrypted, modifiedEncrypted, controlNodeChanged bool,
allMembers, membersAdded, membersRemoved map[string]*protobuf.CommunityMember) *EncryptionKeyAction {
result := &EncryptionKeyAction{
ActionType: EncryptionKeyNone,
Members: map[string]*protobuf.CommunityMember{},
}
copyMap := func(source map[string]*protobuf.CommunityMember) map[string]*protobuf.CommunityMember {
to := make(map[string]*protobuf.CommunityMember)
for pubKey, member := range source {
to[pubKey] = member
}
return to
}
// control node changed on closed community/channel
if controlNodeChanged && modifiedEncrypted {
result.ActionType = EncryptionKeyRekey
result.Members = copyMap(allMembers)
return result
}
// encryption was just added
if modifiedEncrypted && !originEncrypted {
result.ActionType = EncryptionKeyAdd
result.Members = copyMap(allMembers)
return result
}
// encryption was just removed
if !modifiedEncrypted && originEncrypted {
result.ActionType = EncryptionKeyRemove
result.Members = copyMap(allMembers)
return result
}
// open community/channel does not require any actions
if !modifiedEncrypted {
return result
}
if len(membersRemoved) > 0 {
result.ActionType = EncryptionKeyRekey
result.Members = copyMap(allMembers)
result.RemovedMembers = copyMap(membersRemoved)
} else if len(membersAdded) > 0 {
result.ActionType = EncryptionKeySendToMembers
result.Members = copyMap(membersAdded)
}
return result
}