Merge remote-tracking branch 'origin/release/2.7.1'

// FREEBIE
pull/1/head
Matthew Chen 9 years ago
commit c72e8f8e2f

@ -11,8 +11,8 @@
// Time before deletion of signed prekeys (measured in seconds) // Time before deletion of signed prekeys (measured in seconds)
// //
// Currently we retain signed prekeys for at least 14 days. // Currently we retain signed prekeys for at least 7 days.
static const CGFloat kSignedPreKeysDeletionTime = 14 * 24 * 60 * 60; static const CGFloat kSignedPreKeysDeletionTime = 7 * 24 * 60 * 60;
// Time before rotation of signed prekeys (measured in seconds) // Time before rotation of signed prekeys (measured in seconds)
// //
@ -123,6 +123,9 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
} }
SignedPreKeyRecord *signedPreKey = [storageManager generateRandomSignedRecord]; SignedPreKeyRecord *signedPreKey = [storageManager generateRandomSignedRecord];
// Store the new signed key immediately, before it is sent to the
// service to prevent race conditions and other edge cases.
[storageManager storeSignedPreKey:signedPreKey.Id signedPreKeyRecord:signedPreKey];
NSArray *preKeys = nil; NSArray *preKeys = nil;
TSRequest *request; TSRequest *request;
@ -131,6 +134,10 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
description = @"signed and one-time prekeys"; description = @"signed and one-time prekeys";
PreKeyRecord *lastResortPreKey = [storageManager getOrGenerateLastResortKey]; PreKeyRecord *lastResortPreKey = [storageManager getOrGenerateLastResortKey];
preKeys = [storageManager generatePreKeyRecords]; preKeys = [storageManager generatePreKeyRecords];
// Store the new one-time keys immediately, before they are sent to the
// service to prevent race conditions and other edge cases.
[storageManager storePreKeyRecords:preKeys];
request = [[TSRegisterPrekeysRequest alloc] initWithPrekeyArray:preKeys request = [[TSRegisterPrekeysRequest alloc] initWithPrekeyArray:preKeys
identityKey:[storageManager identityKeyPair].publicKey identityKey:[storageManager identityKeyPair].publicKey
signedPreKeyRecord:signedPreKey signedPreKeyRecord:signedPreKey
@ -142,12 +149,13 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
[[TSNetworkManager sharedManager] makeRequest:request [[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) { success:^(NSURLSessionDataTask *task, id responseObject) {
OWSAnalyticsInfo(@"Prekey update success: %@", description); DDLogInfo(@"%@ Successfully registered %@.", self.tag, description);
if (modeCopy == RefreshPreKeysMode_SignedAndOneTime) { // Mark signed prekey as accepted by service.
[storageManager storePreKeyRecords:preKeys]; [signedPreKey markAsAcceptedByService];
}
[storageManager storeSignedPreKey:signedPreKey.Id signedPreKeyRecord:signedPreKey]; // On success, update the "current" signed prekey state.
[storageManager setCurrentSignedPrekeyId:signedPreKey.Id];
successHandler(); successHandler();
@ -187,6 +195,7 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
NSString *preKeyCountKey = @"count"; NSString *preKeyCountKey = @"count";
NSNumber *count = [responseObject objectForKey:preKeyCountKey]; NSNumber *count = [responseObject objectForKey:preKeyCountKey];
BOOL didUpdatePreKeys = NO;
void (^updatePreKeys)(RefreshPreKeysMode) = ^(RefreshPreKeysMode mode) { void (^updatePreKeys)(RefreshPreKeysMode) = ^(RefreshPreKeysMode mode) {
[self registerPreKeysWithMode:mode [self registerPreKeysWithMode:mode
success:^{ success:^{
@ -204,35 +213,65 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
if (shouldUpdateOneTimePreKeys) { if (shouldUpdateOneTimePreKeys) {
DDLogInfo(@"%@ Updating one-time and signed prekeys due to shortage of one-time prekeys.", self.tag); DDLogInfo(@"%@ Updating one-time and signed prekeys due to shortage of one-time prekeys.", self.tag);
updatePreKeys(RefreshPreKeysMode_SignedAndOneTime); updatePreKeys(RefreshPreKeysMode_SignedAndOneTime);
didUpdatePreKeys = YES;
} else { } else {
TSRequest *currentSignedPreKey = [[TSCurrentSignedPreKeyRequest alloc] init];
[[TSNetworkManager sharedManager] makeRequest:currentSignedPreKey
success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
NSString *keyIdDictKey = @"keyId";
NSNumber *keyId = [responseObject objectForKey:keyIdDictKey];
OWSAssert(keyId);
TSStorageManager *storageManager = [TSStorageManager sharedManager]; TSStorageManager *storageManager = [TSStorageManager sharedManager];
SignedPreKeyRecord *currentRecord = [storageManager loadSignedPrekey:keyId.intValue]; NSNumber *currentSignedPrekeyId = [storageManager currentSignedPrekeyId];
OWSAssert(currentRecord); BOOL shouldUpdateSignedPrekey = NO;
if (!currentSignedPrekeyId) {
BOOL shouldUpdateSignedPrekey DDLogError(@"%@ %s Couldn't find current signed prekey id", self.tag, __PRETTY_FUNCTION__);
shouldUpdateSignedPrekey = YES;
} else {
SignedPreKeyRecord *currentRecord = [storageManager loadSignedPrekeyOrNil:currentSignedPrekeyId.intValue];
if (!currentRecord) {
DDLogError(@"%@ %s Couldn't find signed prekey for id: %@", self.tag, __PRETTY_FUNCTION__, currentSignedPrekeyId);
OWSAssert(0);
shouldUpdateSignedPrekey = YES;
} else {
shouldUpdateSignedPrekey
= fabs([currentRecord.generatedAt timeIntervalSinceNow]) >= kSignedPreKeyRotationTime; = fabs([currentRecord.generatedAt timeIntervalSinceNow]) >= kSignedPreKeyRotationTime;
}
}
if (shouldUpdateSignedPrekey) { if (shouldUpdateSignedPrekey) {
DDLogInfo(@"%@ Updating signed prekey due to rotation period.", self.tag); DDLogInfo(@"%@ Updating signed prekey due to rotation period.", self.tag);
updatePreKeys(RefreshPreKeysMode_SignedOnly); updatePreKeys(RefreshPreKeysMode_SignedOnly);
didUpdatePreKeys = YES;
} else { } else {
DDLogDebug(@"%@ Not updating prekeys.", self.tag); DDLogDebug(@"%@ Not updating prekeys.", self.tag);
} }
}
// Update the prekey check timestamp on success. // Update the prekey check timestamp on success.
dispatch_async(TSPreKeyManager.prekeyQueue, ^{ dispatch_async(TSPreKeyManager.prekeyQueue, ^{
lastPreKeyCheckTimestamp = [NSDate date]; lastPreKeyCheckTimestamp = [NSDate date];
}); });
if (!didUpdatePreKeys) {
// If we didn't update the prekeys, our local "current signed key" state should
// agree with the service's "current signed key" state. Let's verify that,
// since it's closely related to the issues we saw with the 2.7.0.10 release.
TSRequest *currentSignedPreKey = [[TSCurrentSignedPreKeyRequest alloc] init];
[[TSNetworkManager sharedManager] makeRequest:currentSignedPreKey
success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
NSString *keyIdDictKey = @"keyId";
NSNumber *keyId = [responseObject objectForKey:keyIdDictKey];
OWSAssert(keyId);
TSStorageManager *storageManager = [TSStorageManager sharedManager];
NSNumber *currentSignedPrekeyId = [storageManager currentSignedPrekeyId];
if (!keyId || !currentSignedPrekeyId || ![currentSignedPrekeyId isEqualToNumber:keyId]) {
DDLogError(
@"%@ Local and service 'current signed prekey ids' did not match. %@ == %@ == %d.",
self.tag,
keyId,
currentSignedPrekeyId,
[currentSignedPrekeyId isEqualToNumber:keyId]);
}
} }
failure:^(NSURLSessionDataTask *task, NSError *error) { failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogWarn(@"%@ Updating signed prekey because of failure to retrieve current signed prekey.", DDLogWarn(@"%@ Could not retrieve current signed key from the service.", self.tag);
self.tag);
updatePreKeys(RefreshPreKeysMode_SignedOnly);
}]; }];
} }
} }
@ -242,18 +281,9 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
} }
+ (void)clearSignedPreKeyRecords { + (void)clearSignedPreKeyRecords {
TSRequest *currentSignedPreKey = [[TSCurrentSignedPreKeyRequest alloc] init]; TSStorageManager *storageManager = [TSStorageManager sharedManager];
[[TSNetworkManager sharedManager] makeRequest:currentSignedPreKey NSNumber *currentSignedPrekeyId = [storageManager currentSignedPrekeyId];
success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) { [self clearSignedPreKeyRecordsWithKeyId:currentSignedPrekeyId];
NSString *keyIdDictKey = @"keyId";
NSNumber *keyId = [responseObject objectForKey:keyIdDictKey];
[self clearSignedPreKeyRecordsWithKeyId:keyId];
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogWarn(@"%@ Failed to retrieve current prekey.", self.tag);
}];
} }
+ (void)clearSignedPreKeyRecordsWithKeyId:(NSNumber *)keyId { + (void)clearSignedPreKeyRecordsWithKeyId:(NSNumber *)keyId {
@ -266,9 +296,15 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
// one thread is "registering" or "clearing" prekeys at a time. // one thread is "registering" or "clearing" prekeys at a time.
dispatch_async(TSPreKeyManager.prekeyQueue, ^{ dispatch_async(TSPreKeyManager.prekeyQueue, ^{
TSStorageManager *storageManager = [TSStorageManager sharedManager]; TSStorageManager *storageManager = [TSStorageManager sharedManager];
SignedPreKeyRecord *currentRecord = [storageManager loadSignedPrekey:keyId.intValue]; SignedPreKeyRecord *currentRecord = [storageManager loadSignedPrekeyOrNil:keyId.intValue];
if (!currentRecord) {
DDLogError(@"%@ %s Couldn't find signed prekey for id: %@", self.tag, __PRETTY_FUNCTION__, keyId);
OWSAssert(0);
}
NSArray *allSignedPrekeys = [storageManager loadSignedPreKeys]; NSArray *allSignedPrekeys = [storageManager loadSignedPreKeys];
NSArray *oldSignedPrekeys = [self removeCurrentRecord:currentRecord fromRecords:allSignedPrekeys]; NSArray *oldSignedPrekeys
= (currentRecord != nil ? [self removeCurrentRecord:currentRecord fromRecords:allSignedPrekeys]
: allSignedPrekeys);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.dateStyle = NSDateFormatterMediumStyle;
@ -280,24 +316,35 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
return [left.generatedAt compare:right.generatedAt]; return [left.generatedAt compare:right.generatedAt];
}]; }];
NSUInteger deletedCount = 0; int oldAcceptedSignedPreKeyCount = 0;
// Iterate the signed prekeys in ascending order so that we try to delete older keys first. for (SignedPreKeyRecord *signedPrekey in oldSignedPrekeys) {
for (SignedPreKeyRecord *deletionCandidate in oldSignedPrekeys) { if (signedPrekey.wasAcceptedByService) {
// Always keep the last three signed prekeys. oldAcceptedSignedPreKeyCount++;
NSUInteger remainingCount = allSignedPrekeys.count - deletedCount; }
if (remainingCount <= 3) {
break;
} }
// Iterate the signed prekeys in ascending order so that we try to delete older keys first.
for (SignedPreKeyRecord *signedPrekey in oldSignedPrekeys) {
// Never delete signed prekeys until they are N days old. // Never delete signed prekeys until they are N days old.
if (fabs([deletionCandidate.generatedAt timeIntervalSinceNow]) < kSignedPreKeysDeletionTime) { if (fabs([signedPrekey.generatedAt timeIntervalSinceNow]) < kSignedPreKeysDeletionTime) {
break; continue;
}
// We try to keep a minimum of 3 "old, accepted" signed prekeys.
if (signedPrekey.wasAcceptedByService) {
if (oldAcceptedSignedPreKeyCount < 3) {
continue;
} else {
oldAcceptedSignedPreKeyCount--;
}
} }
OWSAnalyticsInfo( OWSAnalyticsInfo(@"%@ Deleting old signed prekey: %@, wasAcceptedByService: %d",
@"Deleting old signed prekey: %@", [dateFormatter stringFromDate:deletionCandidate.generatedAt]); self.tag,
[storageManager removeSignedPreKey:deletionCandidate.Id]; [dateFormatter stringFromDate:signedPrekey.generatedAt],
deletedCount++; signedPrekey.wasAcceptedByService);
[storageManager removeSignedPreKey:signedPrekey.Id];
} }
}); });
} }

@ -9,6 +9,12 @@
- (SignedPreKeyRecord *)generateRandomSignedRecord; - (SignedPreKeyRecord *)generateRandomSignedRecord;
- (nullable SignedPreKeyRecord *)loadSignedPrekeyOrNil:(int)signedPreKeyId;
// Returns nil if no current signed prekey id is found.
- (nullable NSNumber *)currentSignedPrekeyId;
- (void)setCurrentSignedPrekeyId:(int)value;
#pragma mark - Prekey update failures #pragma mark - Prekey update failures
- (int)prekeyUpdateFailureCount; - (int)prekeyUpdateFailureCount;

@ -15,6 +15,7 @@ NSString *const TSStorageManagerSignedPreKeyStoreCollection = @"TSStorageManager
NSString *const TSStorageManagerSignedPreKeyMetadataCollection = @"TSStorageManagerSignedPreKeyMetadataCollection"; NSString *const TSStorageManagerSignedPreKeyMetadataCollection = @"TSStorageManagerSignedPreKeyMetadataCollection";
NSString *const TSStorageManagerKeyPrekeyUpdateFailureCount = @"prekeyUpdateFailureCount"; NSString *const TSStorageManagerKeyPrekeyUpdateFailureCount = @"prekeyUpdateFailureCount";
NSString *const TSStorageManagerKeyFirstPrekeyUpdateFailureDate = @"firstPrekeyUpdateFailureDate"; NSString *const TSStorageManagerKeyFirstPrekeyUpdateFailureDate = @"firstPrekeyUpdateFailureDate";
NSString *const TSStorageManagerKeyPrekeyCurrentSignedPrekeyId = @"currentSignedPrekeyId";
@implementation TSStorageManager (SignedPreKeyStore) @implementation TSStorageManager (SignedPreKeyStore)
@ -42,6 +43,12 @@ NSString *const TSStorageManagerKeyFirstPrekeyUpdateFailureDate = @"firstPrekeyU
} }
} }
- (nullable SignedPreKeyRecord *)loadSignedPrekeyOrNil:(int)signedPreKeyId
{
return [self signedPreKeyRecordForKey:[self keyFromInt:signedPreKeyId]
inCollection:TSStorageManagerSignedPreKeyStoreCollection];
}
- (NSArray *)loadSignedPreKeys { - (NSArray *)loadSignedPreKeys {
NSMutableArray *signedPreKeyRecords = [NSMutableArray array]; NSMutableArray *signedPreKeyRecords = [NSMutableArray array];
@ -73,6 +80,19 @@ NSString *const TSStorageManagerKeyFirstPrekeyUpdateFailureDate = @"firstPrekeyU
[self removeObjectForKey:[self keyFromInt:signedPrekeyId] inCollection:TSStorageManagerSignedPreKeyStoreCollection]; [self removeObjectForKey:[self keyFromInt:signedPrekeyId] inCollection:TSStorageManagerSignedPreKeyStoreCollection];
} }
- (nullable NSNumber *)currentSignedPrekeyId
{
return [TSStorageManager.sharedManager objectForKey:TSStorageManagerKeyPrekeyCurrentSignedPrekeyId
inCollection:TSStorageManagerSignedPreKeyMetadataCollection];
}
- (void)setCurrentSignedPrekeyId:(int)value
{
[TSStorageManager.sharedManager setObject:@(value)
forKey:TSStorageManagerKeyPrekeyCurrentSignedPrekeyId
inCollection:TSStorageManagerSignedPreKeyMetadataCollection];
}
#pragma mark - Prekey update failures #pragma mark - Prekey update failures
- (int)prekeyUpdateFailureCount; - (int)prekeyUpdateFailureCount;

Loading…
Cancel
Save