2017-06-06 20:12:50 +02:00
//
// Copyright ( c ) 2017 Open Whisper Systems . All rights reserved .
//
# import "OWSIdentityManager.h"
# import "NotificationsProtocol.h"
2017-06-06 21:16:09 +02:00
# import "OWSMessageSender.h"
2017-06-06 20:12:50 +02:00
# import "OWSRecipientIdentity.h"
2017-06-06 23:43:41 +02:00
# import "OWSVerificationStateSyncMessage.h"
2017-06-06 20:12:50 +02:00
# import "TSAccountManager.h"
# import "TSContactThread.h"
# import "TSErrorMessage.h"
# import "TSGroupThread.h"
2017-06-06 23:46:31 +02:00
# import "TSStorageManager+keyingMaterial.h"
2017-06-06 23:43:41 +02:00
# import "TSStorageManager.h"
# import "TextSecureKitEnv.h"
2017-06-06 20:12:50 +02:00
# import < 25519 / Curve25519 . h >
NS_ASSUME _NONNULL _BEGIN
// Storing our own identity key
NSString * const TSStorageManagerIdentityKeyStoreIdentityKey = @ "TSStorageManagerIdentityKeyStoreIdentityKey" ;
NSString * const TSStorageManagerIdentityKeyStoreCollection = @ "TSStorageManagerIdentityKeyStoreCollection" ;
// Storing recipients identity keys
NSString * const TSStorageManagerTrustedKeysCollection = @ "TSStorageManagerTrustedKeysCollection" ;
2017-06-06 23:43:41 +02:00
NSString * const OWSIdentityManager_QueuedVerificationStateSyncMessages =
@ "OWSIdentityManager_QueuedVerificationStateSyncMessages" ;
2017-06-06 20:12:50 +02:00
// Don ' t trust an identity for sending to unless they ' ve been around for at least this long
const NSTimeInterval kIdentityKeyStoreNonBlockingSecondsThreshold = 5.0 ;
2017-06-07 16:11:01 +02:00
const NSUInteger kIdentityKeyLength = 32 ;
2017-06-06 21:01:11 +02:00
NSString * const kNSNotificationName_IdentityStateDidChange = @ "kNSNotificationName_IdentityStateDidChange" ;
2017-06-06 20:12:50 +02:00
@ interface OWSIdentityManager ( )
@ property ( nonatomic , readonly ) TSStorageManager * storageManager ;
@ property ( nonatomic , readonly ) OWSMessageSender * messageSender ;
@ end
# pragma mark -
@ implementation OWSIdentityManager
+ ( instancetype ) sharedManager
{
static OWSIdentityManager * sharedMyManager = nil ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
sharedMyManager = [ [ self alloc ] initDefault ] ;
} ) ;
return sharedMyManager ;
}
- ( instancetype ) initDefault
{
TSStorageManager * storageManager = [ TSStorageManager sharedManager ] ;
OWSMessageSender * messageSender = [ TextSecureKitEnv sharedEnv ] . messageSender ;
return [ self initWithStorageManager : storageManager messageSender : messageSender ] ;
}
- ( instancetype ) initWithStorageManager : ( TSStorageManager * ) storageManager
messageSender : ( OWSMessageSender * ) messageSender
{
self = [ super init ] ;
if ( ! self ) {
return self ;
}
OWSAssert ( storageManager ) ;
OWSAssert ( messageSender ) ;
_storageManager = storageManager ;
_messageSender = messageSender ;
OWSSingletonAssert ( ) ;
2017-06-06 23:43:41 +02:00
// We want to observe these notifications lazily to avoid accessing
// the data store in [ application : didFinishLaunchingWithOptions : ] .
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) 1. f * NSEC_PER _SEC ) , dispatch_get _main _queue ( ) , ^ {
2017-06-07 17:27:33 +02:00
[ self tryToSyncQueuedVerificationStates ] ;
2017-06-06 23:43:41 +02:00
[ self observeNotifications ] ;
} ) ;
2017-06-06 20:12:50 +02:00
return self ;
}
2017-06-06 23:43:41 +02:00
- ( void ) dealloc
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
}
- ( void ) observeNotifications
{
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( applicationDidBecomeActive : )
name : UIApplicationDidBecomeActiveNotification
object : nil ] ;
}
2017-06-06 20:12:50 +02:00
- ( void ) generateNewIdentityKey
{
[ self . storageManager setObject : [ Curve25519 generateKeyPair ]
forKey : TSStorageManagerIdentityKeyStoreIdentityKey
inCollection : TSStorageManagerIdentityKeyStoreCollection ] ;
}
- ( nullable NSData * ) identityKeyForRecipientId : ( NSString * ) recipientId
{
@ synchronized ( self )
{
return [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] . identityKey ;
}
}
- ( nullable ECKeyPair * ) identityKeyPair
{
return [ self . storageManager keyPairForKey : TSStorageManagerIdentityKeyStoreIdentityKey
inCollection : TSStorageManagerIdentityKeyStoreCollection ] ;
}
- ( int ) localRegistrationId
{
return ( int ) [ TSAccountManager getOrGenerateRegistrationId ] ;
}
- ( BOOL ) saveRemoteIdentity : ( NSData * ) identityKey recipientId : ( NSString * ) recipientId
{
OWSAssert ( identityKey ! = nil ) ;
OWSAssert ( recipientId ! = nil ) ;
@ synchronized ( self )
{
// Deprecated . We actually no longer use the TSStorageManagerTrustedKeysCollection for trust
// decisions , but it ' s desirable to try to keep it up to date with our trusted identitys
// while we ' re switching between versions , e . g . so we don ' t get into a state where we have a
// session for an identity not in our key store .
[ self . storageManager setObject : identityKey
forKey : recipientId
inCollection : TSStorageManagerTrustedKeysCollection ] ;
OWSRecipientIdentity * existingIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
if ( existingIdentity = = nil ) {
2017-06-06 21:01:11 +02:00
DDLogInfo ( @ "%@ saving first use identity for recipient: %@" , self . tag , recipientId ) ;
2017-06-06 20:12:50 +02:00
[ [ [ OWSRecipientIdentity alloc ] initWithRecipientId : recipientId
identityKey : identityKey
isFirstKnownKey : YES
createdAt : [ NSDate new ]
2017-06-06 21:01:11 +02:00
verificationState : OWSVerificationStateDefault ] save ] ;
2017-06-06 23:43:41 +02:00
// Cancel any pending verification state sync messages for this recipient .
[ self clearSyncMessageForRecipientId : recipientId ] ;
2017-06-06 21:01:11 +02:00
[ self fireIdentityStateChangeNotification ] ;
2017-06-06 20:12:50 +02:00
return NO ;
}
if ( ! [ existingIdentity . identityKey isEqual : identityKey ] ) {
2017-06-06 21:01:11 +02:00
OWSVerificationState verificationState ;
switch ( existingIdentity . verificationState ) {
case OWSVerificationStateDefault :
verificationState = OWSVerificationStateDefault ;
break ;
case OWSVerificationStateVerified :
case OWSVerificationStateNoLongerVerified :
verificationState = OWSVerificationStateNoLongerVerified ;
break ;
}
DDLogInfo ( @ "%@ replacing identity for existing recipient: %@ (%@ -> %@)" ,
self . tag ,
recipientId ,
OWSVerificationStateToString ( existingIdentity . verificationState ) ,
OWSVerificationStateToString ( verificationState ) ) ;
2017-06-06 20:12:50 +02:00
[ self createIdentityChangeInfoMessageForRecipientId : recipientId ] ;
2017-06-06 21:01:11 +02:00
2017-06-06 20:12:50 +02:00
[ [ [ OWSRecipientIdentity alloc ] initWithRecipientId : recipientId
identityKey : identityKey
isFirstKnownKey : NO
createdAt : [ NSDate new ]
2017-06-06 21:01:11 +02:00
verificationState : verificationState ] save ] ;
2017-06-06 23:43:41 +02:00
// Cancel any pending verification state sync messages for this recipient .
[ self clearSyncMessageForRecipientId : recipientId ] ;
2017-06-06 21:01:11 +02:00
[ self fireIdentityStateChangeNotification ] ;
2017-06-06 20:12:50 +02:00
return YES ;
}
2017-06-06 21:01:11 +02:00
DDLogDebug ( @ "%@ no changes for identity saved for recipient: %@" , self . tag , recipientId ) ;
return NO ;
}
}
- ( void ) setVerificationState : ( OWSVerificationState ) verificationState
identityKey : ( NSData * ) identityKey
recipientId : ( NSString * ) recipientId
sendSyncMessage : ( BOOL ) sendSyncMessage
{
OWSAssert ( identityKey . length > 0 ) ;
OWSAssert ( recipientId . length > 0 ) ;
@ synchronized ( self )
{
2017-06-06 21:16:09 +02:00
// Ensure a remote identity exists for this key . We may be learning about
// it for the first time .
2017-06-06 21:01:11 +02:00
[ self saveRemoteIdentity : identityKey recipientId : recipientId ] ;
2017-06-07 20:20:29 +02:00
OWSRecipientIdentity * recipientIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
2017-06-06 21:01:11 +02:00
2017-06-07 20:20:29 +02:00
if ( recipientIdentity = = nil ) {
2017-06-06 21:01:11 +02:00
OWSFail ( @ "Missing expected identity: %@" , recipientId ) ;
return ;
2017-06-06 20:12:50 +02:00
}
2017-06-07 20:20:29 +02:00
if ( recipientIdentity . verificationState = = verificationState ) {
2017-06-06 21:01:11 +02:00
return ;
}
DDLogInfo ( @ "%@ setVerificationState: %@ (%@ -> %@)" ,
self . tag ,
recipientId ,
2017-06-07 20:20:29 +02:00
OWSVerificationStateToString ( recipientIdentity . verificationState ) ,
2017-06-06 21:01:11 +02:00
OWSVerificationStateToString ( verificationState ) ) ;
2017-06-07 20:20:29 +02:00
[ recipientIdentity updateWithVerificationState : verificationState ] ;
2017-06-06 23:43:41 +02:00
if ( sendSyncMessage ) {
[ self enqueueSyncMessageForVerificationState : verificationState
identityKey : identityKey
recipientId : recipientId ] ;
} else {
// Cancel any pending verification state sync messages for this recipient .
[ self clearSyncMessageForRecipientId : recipientId ] ;
}
2017-06-06 20:12:50 +02:00
}
2017-06-06 21:01:11 +02:00
[ self fireIdentityStateChangeNotification ] ;
}
2017-06-06 21:16:09 +02:00
- ( OWSVerificationState ) verificationStateForRecipientId : ( NSString * ) recipientId
{
OWSAssert ( recipientId . length > 0 ) ;
@ synchronized ( self )
{
OWSRecipientIdentity * _Nullable currentIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
if ( ! currentIdentity ) {
// We might not know the identity for this recipient yet .
return OWSVerificationStateDefault ;
}
return currentIdentity . verificationState ;
}
}
2017-06-07 01:01:12 +02:00
- ( nullable OWSRecipientIdentity * ) untrustedIdentityForSendingToRecipientId : ( NSString * ) recipientId
2017-06-06 21:32:43 +02:00
{
OWSAssert ( recipientId . length > 0 ) ;
@ synchronized ( self )
{
2017-06-07 01:01:12 +02:00
OWSRecipientIdentity * _Nullable recipientIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
2017-06-07 14:53:24 +02:00
if ( recipientIdentity = = nil ) {
// trust on first use
return nil ;
}
2017-06-07 01:01:12 +02:00
BOOL isTrusted = [ self isTrustedIdentityKey : recipientIdentity . identityKey
recipientId : recipientId
direction : TSMessageDirectionOutgoing ] ;
if ( isTrusted ) {
return nil ;
} else {
return recipientIdentity ;
2017-06-06 21:32:43 +02:00
}
}
}
2017-06-06 21:01:11 +02:00
- ( void ) fireIdentityStateChangeNotification
{
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kNSNotificationName_IdentityStateDidChange
object : nil
userInfo : nil ] ;
} ) ;
2017-06-06 20:12:50 +02:00
}
- ( BOOL ) isTrustedIdentityKey : ( NSData * ) identityKey
recipientId : ( NSString * ) recipientId
direction : ( TSMessageDirection ) direction
{
OWSAssert ( identityKey ! = nil ) ;
OWSAssert ( recipientId ! = nil ) ;
OWSAssert ( direction ! = TSMessageDirectionUnknown ) ;
@ synchronized ( self )
{
2017-06-06 23:43:13 +02:00
if ( [ [ self . storageManager localNumber ] isEqualToString : recipientId ] ) {
2017-06-06 20:12:50 +02:00
if ( [ [ self identityKeyPair ] . publicKey isEqualToData : identityKey ] ) {
return YES ;
} else {
2017-06-07 15:08:17 +02:00
DDLogError ( @ "%@ Wrong identity: %@ for local key: %@" ,
self . tag ,
2017-06-06 20:12:50 +02:00
identityKey ,
[ self identityKeyPair ] . publicKey ) ;
OWSAssert ( NO ) ;
return NO ;
}
}
switch ( direction ) {
case TSMessageDirectionIncoming : {
return YES ;
}
case TSMessageDirectionOutgoing : {
OWSRecipientIdentity * existingIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
return [ self isTrustedKey : identityKey forSendingToIdentity : existingIdentity ] ;
}
default : {
2017-06-07 15:08:17 +02:00
DDLogError ( @ "%@ unexpected message direction: %ld" , self . tag , ( long ) direction ) ;
2017-06-06 20:12:50 +02:00
OWSAssert ( NO ) ;
return NO ;
}
}
}
}
- ( BOOL ) isTrustedKey : ( NSData * ) identityKey forSendingToIdentity : ( nullable OWSRecipientIdentity * ) recipientIdentity
{
2017-06-07 16:11:01 +02:00
OWSAssert ( identityKey . length = = kIdentityKeyLength ) ;
2017-06-06 20:12:50 +02:00
@ synchronized ( self )
{
if ( recipientIdentity = = nil ) {
2017-06-07 15:08:17 +02:00
DDLogDebug ( @ "%@ Trusting previously unknown recipient: %@" , self . tag , recipientIdentity . recipientId ) ;
2017-06-06 20:12:50 +02:00
return YES ;
}
2017-06-07 16:11:01 +02:00
OWSAssert ( recipientIdentity . identityKey . length = = kIdentityKeyLength ) ;
2017-06-06 20:12:50 +02:00
if ( ! [ recipientIdentity . identityKey isEqualToData : identityKey ] ) {
2017-06-07 15:08:17 +02:00
DDLogWarn ( @ "%@ key mismatch for recipient: %@" , self . tag , recipientIdentity . recipientId ) ;
2017-06-06 20:12:50 +02:00
return NO ;
}
2017-06-07 15:18:49 +02:00
if ( [ recipientIdentity isFirstKnownKey ] ) {
DDLogDebug ( @ "%@ trusting first known key for recipient: %@" , self . tag , recipientIdentity . recipientId ) ;
return YES ;
}
2017-06-06 21:01:11 +02:00
switch ( recipientIdentity . verificationState ) {
case OWSVerificationStateDefault : {
BOOL isNew = ( fabs ( [ recipientIdentity . createdAt timeIntervalSinceNow ] )
< kIdentityKeyStoreNonBlockingSecondsThreshold ) ;
if ( isNew ) {
2017-06-07 15:08:17 +02:00
DDLogWarn ( @ "%@ not trusting new identity for recipient: %@" , self . tag , recipientIdentity . recipientId ) ;
2017-06-06 21:01:11 +02:00
return NO ;
} else {
2017-06-07 15:08:17 +02:00
DDLogWarn ( @ "%@ trusting existing identity for recipient: %@" , self . tag , recipientIdentity . recipientId ) ;
2017-06-06 21:01:11 +02:00
return YES ;
}
}
case OWSVerificationStateVerified :
2017-06-07 15:08:17 +02:00
DDLogWarn ( @ "%@ trusting verified identity for recipient: %@" , self . tag , recipientIdentity . recipientId ) ;
2017-06-06 21:01:11 +02:00
return YES ;
case OWSVerificationStateNoLongerVerified :
2017-06-07 15:08:17 +02:00
DDLogWarn ( @ "%@ not trusting no longer verified identity for recipient: %@" , self . tag , recipientIdentity . recipientId ) ;
2017-06-06 21:01:11 +02:00
return NO ;
2017-06-06 20:12:50 +02:00
}
}
}
- ( void ) createIdentityChangeInfoMessageForRecipientId : ( NSString * ) recipientId
{
OWSAssert ( recipientId ! = nil ) ;
TSContactThread * contactThread = [ TSContactThread getOrCreateThreadWithContactId : recipientId ] ;
OWSAssert ( contactThread ! = nil ) ;
TSErrorMessage * errorMessage =
[ TSErrorMessage nonblockingIdentityChangeInThread : contactThread recipientId : recipientId ] ;
[ errorMessage save ] ;
[ [ TextSecureKitEnv sharedEnv ] . notificationsManager notifyUserForErrorMessage : errorMessage inThread : contactThread ] ;
for ( TSGroupThread * groupThread in [ TSGroupThread groupThreadsWithRecipientId : recipientId ] ) {
[ [ TSErrorMessage nonblockingIdentityChangeInThread : groupThread recipientId : recipientId ] save ] ;
}
}
2017-06-06 23:43:41 +02:00
- ( void ) enqueueSyncMessageForVerificationState : ( OWSVerificationState ) verificationState
identityKey : ( NSData * ) identityKey
recipientId : ( NSString * ) recipientId
{
OWSAssert ( identityKey . length > 0 ) ;
OWSAssert ( recipientId . length > 0 ) ;
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
@ synchronized ( self )
{
[ self . storageManager setObject : recipientId
forKey : recipientId
inCollection : OWSIdentityManager_QueuedVerificationStateSyncMessages ] ;
}
2017-06-07 17:27:33 +02:00
[ self tryToSyncQueuedVerificationStates ] ;
2017-06-06 23:43:41 +02:00
} ) ;
}
2017-06-07 17:27:33 +02:00
- ( void ) tryToSyncQueuedVerificationStates
2017-06-06 23:43:41 +02:00
{
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
@ synchronized ( self )
{
NSMutableArray < NSString * > * recipientIds = [ NSMutableArray new ] ;
[ self . storageManager . dbConnection readWriteWithBlock : ^ ( YapDatabaseReadWriteTransaction * transaction ) {
[ transaction enumerateKeysAndObjectsInCollection : OWSIdentityManager_QueuedVerificationStateSyncMessages
usingBlock : ^ ( NSString * _Nonnull recipientId ,
2017-06-07 17:27:33 +02:00
id _Nonnull object ,
BOOL * _Nonnull stop ) {
2017-06-06 23:43:41 +02:00
[ recipientIds addObject : recipientId ] ;
} ] ;
2017-06-07 17:27:33 +02:00
} ] ;
OWSVerificationStateSyncMessage * message =
[ OWSVerificationStateSyncMessage new ] ;
for ( NSString * recipientId in recipientIds ) {
OWSRecipientIdentity * recipientIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
if ( ! recipientIdentity ) {
OWSFail ( @ "Could not load recipient identity for recipientId: %@" , recipientId ) ;
continue ;
}
2017-06-07 20:20:29 +02:00
if ( recipientIdentity . recipientId . length < 1 || recipientIdentity . identityKey . length < 1 ) {
OWSFail ( @ "Invalid recipient identity for recipientId: %@" , recipientId ) ;
continue ;
}
2017-06-07 17:27:33 +02:00
if ( recipientIdentity . verificationState = = OWSVerificationStateNoLongerVerified ) {
// We don ' t want to sync "no longer verified" state . Other clients can
// figure this out from the / profile / endpoint , and this can cause data
// loss as a user ' s devices overwrite each other ' s verification .
OWSFail ( @ "Queue verification state had unexpected value: %@ recipientId: %@" , OWSVerificationStateToString ( recipientIdentity . verificationState ) , recipientId ) ;
continue ;
}
[ message addVerificationState : recipientIdentity . verificationState
2017-06-07 20:20:29 +02:00
identityKey : recipientIdentity . identityKey
2017-06-07 17:27:33 +02:00
recipientId : recipientId ] ;
}
if ( message . recipientIds . count > 0 ) {
[ self sendSyncVerificationStateMessage : message ] ;
}
}
} ) ;
}
2017-06-06 23:43:41 +02:00
2017-06-07 17:27:33 +02:00
- ( void ) syncAllVerificationStates
{
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
@ synchronized ( self )
{
OWSVerificationStateSyncMessage * message =
[ OWSVerificationStateSyncMessage new ] ;
[ OWSRecipientIdentity enumerateCollectionObjectsUsingBlock : ^ ( OWSRecipientIdentity * recipientIdentity , BOOL * stop ) {
OWSAssert ( recipientIdentity ) ;
OWSAssert ( recipientIdentity . recipientId . length > 0 ) ;
OWSAssert ( recipientIdentity . identityKey . length > 0 ) ;
2017-06-07 20:20:29 +02:00
if ( recipientIdentity . recipientId . length < 1 || recipientIdentity . identityKey . length < 1 ) {
OWSFail ( @ "Invalid recipient identity for recipientId: %@" , recipientIdentity . recipientId ) ;
return ;
}
2017-06-07 17:27:33 +02:00
[ message addVerificationState : recipientIdentity . verificationState
2017-06-07 20:20:29 +02:00
identityKey : recipientIdentity . identityKey
2017-06-07 17:27:33 +02:00
recipientId : recipientIdentity . recipientId ] ;
2017-06-06 23:43:41 +02:00
} ] ;
2017-06-07 17:27:33 +02:00
if ( message . recipientIds . count > 0 ) {
[ self sendSyncVerificationStateMessage : message ] ;
}
2017-06-06 23:43:41 +02:00
}
} ) ;
}
2017-06-07 17:27:33 +02:00
- ( void ) sendSyncVerificationStateMessage : ( OWSVerificationStateSyncMessage * ) message
2017-06-06 23:43:41 +02:00
{
2017-06-07 17:27:33 +02:00
OWSAssert ( message ) ;
OWSAssert ( message . recipientIds . count > 0 ) ;
2017-06-06 23:43:41 +02:00
[ self . messageSender sendMessage : message
success : ^ {
DDLogInfo ( @ "%@ Successfully sent verification state sync message" , self . tag ) ;
// Record that this verification state was successfully synced .
2017-06-07 17:27:33 +02:00
[ self clearSyncMessageForRecipientIds : message . recipientIds ] ;
2017-06-06 23:43:41 +02:00
}
failure : ^ ( NSError * error ) {
DDLogError ( @ "%@ Failed to send verification state sync message with error: %@" , self . tag , error ) ;
} ] ;
}
- ( void ) clearSyncMessageForRecipientId : ( NSString * ) recipientId
{
OWSAssert ( recipientId . length > 0 ) ;
2017-06-07 17:27:33 +02:00
[ self clearSyncMessageForRecipientIds : @ [ recipientId ] ] ;
}
- ( void ) clearSyncMessageForRecipientIds : ( NSArray < NSString * > * ) recipientIds
{
OWSAssert ( recipientIds . count > 0 ) ;
2017-06-06 23:43:41 +02:00
dispatch_async ( dispatch_get _global _queue ( DISPATCH_QUEUE _PRIORITY _DEFAULT , 0 ) , ^ {
@ synchronized ( self )
{
2017-06-07 17:27:33 +02:00
for ( NSString * recipientId in recipientIds ) {
[ self . storageManager removeObjectForKey : recipientId
inCollection : OWSIdentityManager_QueuedVerificationStateSyncMessages ] ;
}
2017-06-06 23:43:41 +02:00
}
} ) ;
}
2017-06-07 20:20:29 +02:00
- ( void ) processIncomingSyncMessage : ( NSArray < OWSSignalServiceProtosSyncMessageVerification * > * ) verifications
{
for ( OWSSignalServiceProtosSyncMessageVerification * verification in verifications ) {
NSString * recipientId = [ verification destination ] ;
if ( recipientId . length < 1 ) {
OWSFail ( @ "Verification state sync message missing recipientId." ) ;
continue ;
}
NSData * identityKey = [ verification identityKey ] ;
if ( identityKey . length < 1 ) {
OWSFail ( @ "Verification state sync message missing identityKey." ) ;
continue ;
}
switch ( verification . state ) {
case OWSSignalServiceProtosSyncMessageVerificationStateDefault :
[ self tryToApplyVerificationStateFromSyncMessage : OWSVerificationStateDefault
recipientId : recipientId
identityKey : identityKey
overwriteOnConflict : NO ] ;
break ;
case OWSSignalServiceProtosSyncMessageVerificationStateVerified :
[ self tryToApplyVerificationStateFromSyncMessage : OWSVerificationStateVerified
recipientId : recipientId
identityKey : identityKey
overwriteOnConflict : YES ] ;
break ;
case OWSSignalServiceProtosSyncMessageVerificationStateNoLongerVerified :
OWSFail ( @ "Verification state sync message for recipientId: %@ has unexpected value: %@." ,
recipientId ,
OWSVerificationStateToString ( OWSVerificationStateNoLongerVerified ) ) ;
continue ;
}
}
}
- ( void ) tryToApplyVerificationStateFromSyncMessage : ( OWSVerificationState ) verificationState
recipientId : ( NSString * ) recipientId
identityKey : ( NSData * ) identityKey
overwriteOnConflict : ( BOOL ) overwriteOnConflict
{
if ( recipientId . length < 1 ) {
OWSFail ( @ "Verification state sync message missing recipientId." ) ;
return ;
}
if ( identityKey . length < 1 ) {
OWSFail ( @ "Verification state sync message missing identityKey." ) ;
return ;
}
@ synchronized ( self )
{
OWSRecipientIdentity * _Nullable recipientIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
if ( ! recipientIdentity ) {
// There ' s no existing recipient identity for this recipient .
// We should probably create one .
if ( verificationState = = OWSVerificationStateDefault ) {
// There ' s no point in creating a new recipient identity just to
// set its verification state to default .
return ;
}
// Ensure a remote identity exists for this key . We may be learning about
// it for the first time .
[ self saveRemoteIdentity : identityKey recipientId : recipientId ] ;
recipientIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
if ( recipientIdentity = = nil ) {
OWSFail ( @ "Missing expected identity: %@" , recipientId ) ;
return ;
}
if ( ! [ recipientIdentity . recipientId isEqualToString : recipientId ] ) {
OWSFail ( @ "recipientIdentity has unexpected recipientId: %@" , recipientId ) ;
return ;
}
if ( ! [ recipientIdentity . identityKey isEqualToData : identityKey ] ) {
OWSFail ( @ "recipientIdentity has unexpected identityKey" ) ;
return ;
}
if ( recipientIdentity . verificationState = = verificationState ) {
return ;
}
DDLogInfo ( @ "%@ setVerificationState: %@ (%@ -> %@)" ,
self . tag ,
recipientId ,
OWSVerificationStateToString ( recipientIdentity . verificationState ) ,
OWSVerificationStateToString ( verificationState ) ) ;
[ recipientIdentity updateWithVerificationState : verificationState ] ;
} else {
// There ' s an existing recipient identity for this recipient .
// We should update it .
if ( ! [ recipientIdentity . recipientId isEqualToString : recipientId ] ) {
OWSFail ( @ "recipientIdentity has unexpected recipientId: %@" , recipientId ) ;
return ;
}
if ( ! [ recipientIdentity . identityKey isEqualToData : identityKey ] ) {
// The conflict case where we receive a verification sync message
// whose identity key disagrees with the local identity key for
// this recipient .
if ( ! overwriteOnConflict ) {
DDLogWarn ( @ "recipientIdentity has non-matching identityKey" ) ;
return ;
}
DDLogWarn ( @ "recipientIdentity has non-matching identityKey; overwriting." ) ;
[ self saveRemoteIdentity : identityKey recipientId : recipientId ] ;
recipientIdentity = [ OWSRecipientIdentity fetchObjectWithUniqueID : recipientId ] ;
if ( recipientIdentity = = nil ) {
OWSFail ( @ "Missing expected identity: %@" , recipientId ) ;
return ;
}
if ( ! [ recipientIdentity . recipientId isEqualToString : recipientId ] ) {
OWSFail ( @ "recipientIdentity has unexpected recipientId: %@" , recipientId ) ;
return ;
}
if ( ! [ recipientIdentity . identityKey isEqualToData : identityKey ] ) {
OWSFail ( @ "recipientIdentity has unexpected identityKey" ) ;
return ;
}
}
if ( recipientIdentity . verificationState = = verificationState ) {
return ;
}
[ recipientIdentity updateWithVerificationState : OWSVerificationStateDefault ] ;
}
}
}
2017-06-06 23:43:41 +02:00
# pragma mark - Notifications
- ( void ) applicationDidBecomeActive : ( NSNotification * ) notification
{
OWSAssert ( [ NSThread isMainThread ] ) ;
2017-06-07 17:27:33 +02:00
[ self tryToSyncQueuedVerificationStates ] ;
2017-06-06 23:43:41 +02:00
}
2017-06-06 20:12:50 +02:00
# pragma mark - Logging
+ ( NSString * ) tag
{
return [ NSString stringWithFormat : @ "[%@]" , self . class ] ;
}
- ( NSString * ) tag
{
return self . class . tag ;
}
@ end
NS_ASSUME _NONNULL _END