github.com/status-im/status-go@v1.1.0/protocol/communities/community_encryption_key_action.go (about)

     1  package communities
     2  
     3  import (
     4  	"github.com/status-im/status-go/protocol/protobuf"
     5  )
     6  
     7  type KeyDistributor interface {
     8  	Generate(community *Community, keyActions *EncryptionKeyActions) error
     9  	Distribute(community *Community, keyActions *EncryptionKeyActions) error
    10  }
    11  
    12  type EncryptionKeyActionType int
    13  
    14  const (
    15  	EncryptionKeyNone EncryptionKeyActionType = iota
    16  	EncryptionKeyAdd
    17  	EncryptionKeyRemove
    18  	EncryptionKeyRekey
    19  	EncryptionKeySendToMembers
    20  )
    21  
    22  type EncryptionKeyAction struct {
    23  	ActionType     EncryptionKeyActionType
    24  	Members        map[string]*protobuf.CommunityMember
    25  	RemovedMembers map[string]*protobuf.CommunityMember
    26  }
    27  
    28  type EncryptionKeyActions struct {
    29  	// community-level encryption key action
    30  	CommunityKeyAction EncryptionKeyAction
    31  
    32  	// channel-level encryption key actions
    33  	ChannelKeysActions map[string]EncryptionKeyAction // key is: chatID
    34  }
    35  
    36  func EvaluateCommunityEncryptionKeyActions(origin, modified *Community) *EncryptionKeyActions {
    37  	if origin == nil {
    38  		// `modified` is a new community, create empty `origin` community
    39  		origin = &Community{
    40  			config: &Config{
    41  				ID: modified.config.ID,
    42  				CommunityDescription: &protobuf.CommunityDescription{
    43  					Members:                 map[string]*protobuf.CommunityMember{},
    44  					Permissions:             &protobuf.CommunityPermissions{},
    45  					Identity:                &protobuf.ChatIdentity{},
    46  					Chats:                   map[string]*protobuf.CommunityChat{},
    47  					Categories:              map[string]*protobuf.CommunityCategory{},
    48  					AdminSettings:           &protobuf.CommunityAdminSettings{},
    49  					TokenPermissions:        map[string]*protobuf.CommunityTokenPermission{},
    50  					CommunityTokensMetadata: []*protobuf.CommunityTokenMetadata{},
    51  				},
    52  			},
    53  		}
    54  	}
    55  
    56  	changes := EvaluateCommunityChanges(origin, modified)
    57  
    58  	result := &EncryptionKeyActions{
    59  		CommunityKeyAction: *evaluateCommunityLevelEncryptionKeyAction(origin, modified, changes),
    60  		ChannelKeysActions: *evaluateChannelLevelEncryptionKeyActions(origin, modified, changes),
    61  	}
    62  	return result
    63  }
    64  
    65  func evaluateCommunityLevelEncryptionKeyAction(origin, modified *Community, changes *CommunityChanges) *EncryptionKeyAction {
    66  	return evaluateEncryptionKeyAction(
    67  		origin.Encrypted(),
    68  		modified.Encrypted(),
    69  		changes.ControlNodeChanged != nil,
    70  		modified.config.CommunityDescription.Members,
    71  		changes.MembersAdded,
    72  		changes.MembersRemoved,
    73  	)
    74  }
    75  
    76  func evaluateChannelLevelEncryptionKeyActions(origin, modified *Community, changes *CommunityChanges) *map[string]EncryptionKeyAction {
    77  	result := make(map[string]EncryptionKeyAction)
    78  
    79  	for channelID := range modified.config.CommunityDescription.Chats {
    80  		membersAdded := make(map[string]*protobuf.CommunityMember)
    81  		membersRemoved := make(map[string]*protobuf.CommunityMember)
    82  
    83  		chatChanges, ok := changes.ChatsModified[channelID]
    84  		if ok {
    85  			membersAdded = chatChanges.MembersAdded
    86  			membersRemoved = chatChanges.MembersRemoved
    87  		}
    88  
    89  		result[channelID] = *evaluateEncryptionKeyAction(
    90  			origin.ChannelEncrypted(channelID),
    91  			modified.ChannelEncrypted(channelID),
    92  			changes.ControlNodeChanged != nil,
    93  			modified.config.CommunityDescription.Chats[channelID].Members,
    94  			membersAdded,
    95  			membersRemoved,
    96  		)
    97  	}
    98  
    99  	return &result
   100  }
   101  
   102  func evaluateEncryptionKeyAction(originEncrypted, modifiedEncrypted, controlNodeChanged bool,
   103  	allMembers, membersAdded, membersRemoved map[string]*protobuf.CommunityMember) *EncryptionKeyAction {
   104  	result := &EncryptionKeyAction{
   105  		ActionType: EncryptionKeyNone,
   106  		Members:    map[string]*protobuf.CommunityMember{},
   107  	}
   108  
   109  	copyMap := func(source map[string]*protobuf.CommunityMember) map[string]*protobuf.CommunityMember {
   110  		to := make(map[string]*protobuf.CommunityMember)
   111  		for pubKey, member := range source {
   112  			to[pubKey] = member
   113  		}
   114  		return to
   115  	}
   116  
   117  	// control node changed on closed community/channel
   118  	if controlNodeChanged && modifiedEncrypted {
   119  		result.ActionType = EncryptionKeyRekey
   120  		result.Members = copyMap(allMembers)
   121  		return result
   122  	}
   123  
   124  	// encryption was just added
   125  	if modifiedEncrypted && !originEncrypted {
   126  		result.ActionType = EncryptionKeyAdd
   127  		result.Members = copyMap(allMembers)
   128  		return result
   129  	}
   130  
   131  	// encryption was just removed
   132  	if !modifiedEncrypted && originEncrypted {
   133  		result.ActionType = EncryptionKeyRemove
   134  		result.Members = copyMap(allMembers)
   135  		return result
   136  	}
   137  
   138  	// open community/channel does not require any actions
   139  	if !modifiedEncrypted {
   140  		return result
   141  	}
   142  
   143  	if len(membersRemoved) > 0 {
   144  		result.ActionType = EncryptionKeyRekey
   145  		result.Members = copyMap(allMembers)
   146  		result.RemovedMembers = copyMap(membersRemoved)
   147  	} else if len(membersAdded) > 0 {
   148  		result.ActionType = EncryptionKeySendToMembers
   149  		result.Members = copyMap(membersAdded)
   150  	}
   151  
   152  	return result
   153  }