@ -143,19 +143,20 @@ extension MessageReceiver {
}
private static func handleConfigurationMessage ( _ message : ConfigurationMessage , using transaction : Any ) {
guard message . sender = = getUserHexEncodedPublicKey ( ) else { return }
guard message . sender = = getUserHexEncodedPublicKey ( ) , ! UserDefaults . standard [ . hasSyncedConfiguration ] else { return }
let storage = SNMessagingKitConfiguration . shared . storage
let allClosedGroupPublicKeys = storage . getUserClosedGroupPublicKeys ( )
for closedGroup in message . closedGroups {
guard ! allClosedGroupPublicKeys . contains ( closedGroup . publicKey ) else { continue }
handleNewClosedGroup ( groupPublicKey : closedGroup . publicKey , name : closedGroup . name , encryptionKeyPair : closedGroup . encryptionKeyPair ,
members : [ String ] ( closedGroup . members ) , admins : [ String ] ( closedGroup . admins ) , using: transaction )
members : [ String ] ( closedGroup . members ) , admins : [ String ] ( closedGroup . admins ) , messageSentTimestamp: message . sentTimestamp ! , using: transaction )
}
let allOpenGroups = Set ( storage . getAllUserOpenGroups ( ) . keys )
for openGroupURL in message . openGroups {
guard ! allOpenGroups . contains ( openGroupURL ) else { continue }
OpenGroupManager . shared . add ( with : openGroupURL , using : transaction ) . retainUntilComplete ( )
}
UserDefaults . standard [ . hasSyncedConfiguration ] = true
}
@ discardableResult
@ -252,6 +253,7 @@ extension MessageReceiver {
case . membersAdded : handleClosedGroupMembersAdded ( message , using : transaction )
case . membersRemoved : handleClosedGroupMembersRemoved ( message , using : transaction )
case . memberLeft : handleClosedGroupMemberLeft ( message , using : transaction )
case . encryptionKeyPairRequest : handleClosedGroupEncryptionKeyPairRequest ( message , using : transaction )
}
}
@ -261,10 +263,11 @@ extension MessageReceiver {
let groupPublicKey = publicKeyAsData . toHexString ( )
let members = membersAsData . map { $0 . toHexString ( ) }
let admins = adminsAsData . map { $0 . toHexString ( ) }
handleNewClosedGroup ( groupPublicKey : groupPublicKey , name : name , encryptionKeyPair : encryptionKeyPair , members : members , admins : admins , using : transaction )
handleNewClosedGroup ( groupPublicKey : groupPublicKey , name : name , encryptionKeyPair : encryptionKeyPair ,
members : members , admins : admins , messageSentTimestamp : message . sentTimestamp ! , using : transaction )
}
private static func handleNewClosedGroup ( groupPublicKey : String , name : String , encryptionKeyPair : ECKeyPair , members : [ String ] , admins : [ String ] , using transaction : Any ) {
private static func handleNewClosedGroup ( groupPublicKey : String , name : String , encryptionKeyPair : ECKeyPair , members : [ String ] , admins : [ String ] , messageSentTimestamp: UInt64 , using transaction : Any ) {
let transaction = transaction as ! YapDatabaseReadWriteTransaction
// C r e a t e t h e g r o u p
let groupID = LKGroupUtilities . getEncodedClosedGroupIDAsData ( groupPublicKey )
@ -276,21 +279,24 @@ extension MessageReceiver {
} else {
thread = TSGroupThread . getOrCreateThread ( with : group , transaction : transaction )
thread . save ( with : transaction )
// N o t i f y t h e u s e r
let infoMessage = TSInfoMessage ( timestamp : NSDate . ows_millisecondTimeStamp ( ) , in : thread , messageType : . typeGroupUpdate )
infoMessage . save ( with : transaction )
}
// A d d t h e g r o u p t o t h e u s e r ' s s e t o f p u b l i c k e y s t o p o l l f o r
Storage . shared . addClosedGroupPublicKey ( groupPublicKey , using : transaction )
// S t o r e t h e k e y p a i r
Storage . shared . addClosedGroupEncryptionKeyPair ( encryptionKeyPair , for : groupPublicKey , using : transaction )
// S t o r e t h e f o r m a t i o n t i m e s t a m p
Storage . shared . setClosedGroupFormationTimestamp ( to : messageSentTimestamp , for : groupPublicKey , using : transaction )
// N o t i f y t h e P N s e r v e r
let _ = PushNotificationAPI . performOperation ( . subscribe , for : groupPublicKey , publicKey : getUserHexEncodedPublicKey ( ) )
// N o t i f y t h e u s e r
let infoMessage = TSInfoMessage ( timestamp : NSDate . ows_millisecondTimeStamp ( ) , in : thread , messageType : . typeGroupUpdate )
infoMessage . save ( with : transaction )
}
private static func handleClosedGroupEncryptionKeyPair ( _ message : ClosedGroupControlMessage , using transaction : Any ) {
// P r e p a r e
guard case let . encryptionKeyPair ( wrappers ) = message . kind , let groupPublicKey = message . groupPublicKey else { return }
guard case let . encryptionKeyPair ( explicitGroupPublicKey , wrappers ) = message . kind ,
let groupPublicKey = explicitGroupPublicKey ? . toHexString ( ) ? ? message . groupPublicKey else { return }
let transaction = transaction as ! YapDatabaseReadWriteTransaction
let userPublicKey = getUserHexEncodedPublicKey ( )
guard let userKeyPair = SNMessagingKitConfiguration . shared . storage . getUserKeyPair ( ) else {
@ -301,8 +307,8 @@ extension MessageReceiver {
guard let thread = TSGroupThread . fetch ( uniqueId : threadID , transaction : transaction ) else {
return SNLog ( " Ignoring closed group encryption key pair for nonexistent group. " )
}
guard thread . groupModel . group Admin Ids. contains ( message . sender ! ) else {
return SNLog ( " Ignoring closed group encryption key pair from non- admin ." )
guard thread . groupModel . group Member Ids. contains ( message . sender ! ) else {
return SNLog ( " Ignoring closed group encryption key pair from non- member ." )
}
// F i n d o u r w r a p p e r a n d d e c r y p t i t i f p o s s i b l e
guard let wrapper = wrappers . first ( where : { $0 . publicKey = = userPublicKey } ) , let encryptedKeyPair = wrapper . encryptedKeyPair else { return }
@ -325,7 +331,11 @@ extension MessageReceiver {
} catch {
return SNLog ( " Couldn't parse closed group encryption key pair. " )
}
// S t o r e i t
// S t o r e i t i f n e e d e d
let closedGroupEncryptionKeyPairs = Storage . shared . getClosedGroupEncryptionKeyPairs ( for : groupPublicKey )
guard ! closedGroupEncryptionKeyPairs . contains ( keyPair ) else {
return SNLog ( " Ignoring duplicate closed group encryption key pair. " )
}
Storage . shared . addClosedGroupEncryptionKeyPair ( keyPair , for : groupPublicKey , using : transaction )
SNLog ( " Received a new closed group encryption key pair. " )
}
@ -411,13 +421,20 @@ extension MessageReceiver {
performIfValid ( for : message , using : transaction ) { groupID , thread , group in
let didAdminLeave = group . groupAdminIds . contains ( message . sender ! )
let members : Set < String > = didAdminLeave ? [ ] : Set ( group . groupMemberIds ) . subtracting ( [ message . sender ! ] ) // I f t h e a d m i n l e a v e s t h e g r o u p i s d i s b a n d e d
// G u a r d a g a i n s t s e l f - s e n d s
guard message . sender != getUserHexEncodedPublicKey ( ) else {
return SNLog ( " Ignoring invalid closed group update. " )
}
// G e n e r a t e a n d d i s t r i b u t e a n e w e n c r y p t i o n k e y p a i r i f n e e d e d
let isCurrentUserAdmin = group . groupAdminIds . contains ( getUserHexEncodedPublicKey ( ) )
if isCurrentUserAdmin {
let userPublicKey = getUserHexEncodedPublicKey ( )
let isCurrentUserAdmin = group . groupAdminIds . contains ( userPublicKey )
// I f a r e g u l a r m e m b e r l e f t :
// • D i s t r i b u t e a n e w e n c r y p t i o n k e y p a i r i f w e ' r e t h e a d m i n o f t h e g r o u p
// I f t h e a d m i n l e f t :
// • D o n ' t d i s t r i b u t e a n e w e n c r y p t i o n k e y p a i r
// • U n s u b s c r i b e f r o m P N s , d e l e t e t h e g r o u p p u b l i c k e y , e t c . a s t h e g r o u p w i l l b e d i s b a n d e d
if didAdminLeave {
// R e m o v e t h e g r o u p f r o m t h e d a t a b a s e a n d u n s u b s c r i b e f r o m P N s
Storage . shared . removeAllClosedGroupEncryptionKeyPairs ( for : groupPublicKey , using : transaction )
Storage . shared . removeClosedGroupPublicKey ( groupPublicKey , using : transaction )
let _ = PushNotificationAPI . performOperation ( . unsubscribe , for : groupPublicKey , publicKey : userPublicKey )
} else if isCurrentUserAdmin {
// G e n e r a t e a n d d i s t r i b u t e a n e w e n c r y p t i o n k e y p a i r i f n e e d e d
do {
try MessageSender . generateAndSendNewEncryptionKeyPair ( for : groupPublicKey , to : members , using : transaction )
} catch {
@ -435,6 +452,30 @@ extension MessageReceiver {
}
}
private static func handleClosedGroupEncryptionKeyPairRequest ( _ message : ClosedGroupControlMessage , using transaction : Any ) {
guard case . encryptionKeyPairRequest = message . kind else { return }
let transaction = transaction as ! YapDatabaseReadWriteTransaction
guard let groupPublicKey = message . groupPublicKey else { return }
performIfValid ( for : message , using : transaction ) { groupID , _ , group in
let publicKey = message . sender !
// G u a r d a g a i n s t s e l f - s e n d s
guard publicKey != getUserHexEncodedPublicKey ( ) else {
return SNLog ( " Ignoring invalid closed group update. " )
}
// G e t t h e l a t e s t e n c r y p t i o n k e y p a i r
guard let encryptionKeyPair = Storage . shared . getLatestClosedGroupEncryptionKeyPair ( for : groupPublicKey ) else { return }
// S e n d i t
guard let proto = try ? SNProtoKeyPair . builder ( publicKey : encryptionKeyPair . publicKey ,
privateKey : encryptionKeyPair . privateKey ) . build ( ) , let plaintext = try ? proto . serializedData ( ) else { return }
let thread = TSContactThread . getOrCreateThread ( withContactId : publicKey , transaction : transaction )
guard let ciphertext = try ? MessageSender . encryptWithSessionProtocol ( plaintext , for : publicKey ) else { return }
SNLog ( " Responding to closed group encryption key pair request from: \( publicKey ) . " )
let wrapper = ClosedGroupControlMessage . KeyPairWrapper ( publicKey : publicKey , encryptedKeyPair : ciphertext )
let closedGroupControlMessage = ClosedGroupControlMessage ( kind : . encryptionKeyPair ( publicKey : Data ( hex : groupPublicKey ) , wrappers : [ wrapper ] ) )
MessageSender . send ( closedGroupControlMessage , in : thread , using : transaction )
}
}
private static func performIfValid ( for message : ClosedGroupControlMessage , using transaction : Any , _ update : ( Data , TSGroupThread , TSGroupModel ) -> Void ) {
// P r e p a r e
let transaction = transaction as ! YapDatabaseReadWriteTransaction
@ -447,8 +488,10 @@ extension MessageReceiver {
}
let group = thread . groupModel
// C h e c k t h a t t h e m e s s a g e i s n ' t f r o m b e f o r e t h e g r o u p w a s c r e a t e d
guard Double ( message . sentTimestamp ! ) > thread . creationDate . timeIntervalSince1970 * 1000 else {
return SNLog ( " Ignoring closed group update from before thread was created. " )
if let formationTimestamp = Storage . shared . getClosedGroupFormationTimestamp ( for : groupPublicKey ) {
guard message . sentTimestamp ! > formationTimestamp else {
return SNLog ( " Ignoring closed group update from before thread was created. " )
}
}
// C h e c k t h a t t h e s e n d e r i s a m e m b e r o f t h e g r o u p
guard Set ( group . groupMemberIds ) . contains ( message . sender ! ) else {