package protocol import ( "context" "crypto/ecdsa" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/communities" "github.com/status-im/status-go/protocol/encryption" "github.com/status-im/status-go/protocol/protobuf" ) type CommunitiesKeyDistributorImpl struct { sender *common.MessageSender encryptor *encryption.Protocol } func (ckd *CommunitiesKeyDistributorImpl) Generate(community *communities.Community, keyActions *communities.EncryptionKeyActions) error { if !community.IsControlNode() { return communities.ErrNotControlNode } return iterateActions(community, keyActions, ckd.generateKey) } func (ckd *CommunitiesKeyDistributorImpl) Distribute(community *communities.Community, keyActions *communities.EncryptionKeyActions) error { if !community.IsControlNode() { return communities.ErrNotControlNode } return iterateActions(community, keyActions, ckd.distributeKey) } func iterateActions(community *communities.Community, keyActions *communities.EncryptionKeyActions, fn func(community *communities.Community, hashRatchetGroupID []byte, keyAction *communities.EncryptionKeyAction) error) error { err := fn(community, community.ID(), &keyActions.CommunityKeyAction) if err != nil { return err } for channelID := range keyActions.ChannelKeysActions { keyAction := keyActions.ChannelKeysActions[channelID] err := fn(community, []byte(community.IDString()+channelID), &keyAction) if err != nil { return err } } return nil } func (ckd *CommunitiesKeyDistributorImpl) generateKey(community *communities.Community, hashRatchetGroupID []byte, keyAction *communities.EncryptionKeyAction) error { if keyAction.ActionType != communities.EncryptionKeyAdd { return nil } _, err := ckd.encryptor.GenerateHashRatchetKey(hashRatchetGroupID) return err } func (ckd *CommunitiesKeyDistributorImpl) distributeKey(community *communities.Community, hashRatchetGroupID []byte, keyAction *communities.EncryptionKeyAction) error { pubkeys := make([]*ecdsa.PublicKey, len(keyAction.Members)) i := 0 for hex := range keyAction.Members { pubkeys[i], _ = common.HexToPubkey(hex) i++ } switch keyAction.ActionType { case communities.EncryptionKeyAdd: // key must be already generated err := ckd.sendKeyExchangeMessage(community, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse) if err != nil { return err } case communities.EncryptionKeyRekey: err := ckd.sendKeyExchangeMessage(community, hashRatchetGroupID, pubkeys, common.KeyExMsgRekey) if err != nil { return err } case communities.EncryptionKeySendToMembers: err := ckd.sendKeyExchangeMessage(community, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse) if err != nil { return err } } return nil } func (ckd *CommunitiesKeyDistributorImpl) sendKeyExchangeMessage(community *communities.Community, hashRatchetGroupID []byte, pubkeys []*ecdsa.PublicKey, msgType common.CommKeyExMsgType) error { rawMessage := common.RawMessage{ Sender: community.PrivateKey(), SkipEncryptionLayer: false, CommunityID: community.ID(), CommunityKeyExMsgType: msgType, Recipients: pubkeys, MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, HashRatchetGroupID: hashRatchetGroupID, PubsubTopic: community.PubsubTopic(), // TODO: confirm if it should be sent in community pubsub topic } _, err := ckd.sender.SendCommunityMessage(context.Background(), &rawMessage) if err != nil { return err } return nil }