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

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

@ -11,8 +11,8 @@
// Time before deletion of signed prekeys (measured in seconds)
//
// Currently we retain signed prekeys for at least 14 days.
static const CGFloat kSignedPreKeysDeletionTime = 14 * 24 * 60 * 60;
// Currently we retain signed prekeys for at least 7 days.
static const CGFloat kSignedPreKeysDeletionTime = 7 * 24 * 60 * 60;
// Time before rotation of signed prekeys (measured in seconds)
//
@ -123,6 +123,9 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
}
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;
TSRequest *request;
@ -131,6 +134,10 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
description = @"signed and one-time prekeys";
PreKeyRecord *lastResortPreKey = [storageManager getOrGenerateLastResortKey];
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
identityKey:[storageManager identityKeyPair].publicKey
signedPreKeyRecord:signedPreKey
@ -142,12 +149,13 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
[[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) {
OWSAnalyticsInfo(@"Prekey update success: %@", description);
DDLogInfo(@"%@ Successfully registered %@.", self.tag, description);
if (modeCopy == RefreshPreKeysMode_SignedAndOneTime) {
[storageManager storePreKeyRecords:preKeys];
}
[storageManager storeSignedPreKey:signedPreKey.Id signedPreKeyRecord:signedPreKey];
// Mark signed prekey as accepted by service.
[signedPreKey markAsAcceptedByService];
// On success, update the "current" signed prekey state.
[storageManager setCurrentSignedPrekeyId:signedPreKey.Id];
successHandler();
@ -187,6 +195,7 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
NSString *preKeyCountKey = @"count";
NSNumber *count = [responseObject objectForKey:preKeyCountKey];
BOOL didUpdatePreKeys = NO;
void (^updatePreKeys)(RefreshPreKeysMode) = ^(RefreshPreKeysMode mode) {
[self registerPreKeysWithMode:mode
success:^{
@ -204,35 +213,65 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
if (shouldUpdateOneTimePreKeys) {
DDLogInfo(@"%@ Updating one-time and signed prekeys due to shortage of one-time prekeys.", self.tag);
updatePreKeys(RefreshPreKeysMode_SignedAndOneTime);
didUpdatePreKeys = YES;
} else {
TSStorageManager *storageManager = [TSStorageManager sharedManager];
NSNumber *currentSignedPrekeyId = [storageManager currentSignedPrekeyId];
BOOL shouldUpdateSignedPrekey = NO;
if (!currentSignedPrekeyId) {
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;
}
}
if (shouldUpdateSignedPrekey) {
DDLogInfo(@"%@ Updating signed prekey due to rotation period.", self.tag);
updatePreKeys(RefreshPreKeysMode_SignedOnly);
didUpdatePreKeys = YES;
} else {
DDLogDebug(@"%@ Not updating prekeys.", self.tag);
}
}
// Update the prekey check timestamp on success.
dispatch_async(TSPreKeyManager.prekeyQueue, ^{
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];
SignedPreKeyRecord *currentRecord = [storageManager loadSignedPrekey:keyId.intValue];
OWSAssert(currentRecord);
BOOL shouldUpdateSignedPrekey
= fabs([currentRecord.generatedAt timeIntervalSinceNow]) >= kSignedPreKeyRotationTime;
if (shouldUpdateSignedPrekey) {
DDLogInfo(@"%@ Updating signed prekey due to rotation period.", self.tag);
updatePreKeys(RefreshPreKeysMode_SignedOnly);
} else {
DDLogDebug(@"%@ Not updating prekeys.", self.tag);
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]);
}
// Update the prekey check timestamp on success.
dispatch_async(TSPreKeyManager.prekeyQueue, ^{
lastPreKeyCheckTimestamp = [NSDate date];
});
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogWarn(@"%@ Updating signed prekey because of failure to retrieve current signed prekey.",
self.tag);
updatePreKeys(RefreshPreKeysMode_SignedOnly);
DDLogWarn(@"%@ Could not retrieve current signed key from the service.", self.tag);
}];
}
}
@ -242,18 +281,9 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
}
+ (void)clearSignedPreKeyRecords {
TSRequest *currentSignedPreKey = [[TSCurrentSignedPreKeyRequest alloc] init];
[[TSNetworkManager sharedManager] makeRequest:currentSignedPreKey
success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
NSString *keyIdDictKey = @"keyId";
NSNumber *keyId = [responseObject objectForKey:keyIdDictKey];
[self clearSignedPreKeyRecordsWithKeyId:keyId];
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogWarn(@"%@ Failed to retrieve current prekey.", self.tag);
}];
TSStorageManager *storageManager = [TSStorageManager sharedManager];
NSNumber *currentSignedPrekeyId = [storageManager currentSignedPrekeyId];
[self clearSignedPreKeyRecordsWithKeyId:currentSignedPrekeyId];
}
+ (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.
dispatch_async(TSPreKeyManager.prekeyQueue, ^{
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 *oldSignedPrekeys = [self removeCurrentRecord:currentRecord fromRecords:allSignedPrekeys];
NSArray *oldSignedPrekeys
= (currentRecord != nil ? [self removeCurrentRecord:currentRecord fromRecords:allSignedPrekeys]
: allSignedPrekeys);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateStyle = NSDateFormatterMediumStyle;
@ -280,24 +316,35 @@ static const CGFloat kSignedPreKeyUpdateFailureMaxFailureDuration = 10 * 24 * 60
return [left.generatedAt compare:right.generatedAt];
}];
NSUInteger deletedCount = 0;
// Iterate the signed prekeys in ascending order so that we try to delete older keys first.
for (SignedPreKeyRecord *deletionCandidate in oldSignedPrekeys) {
// Always keep the last three signed prekeys.
NSUInteger remainingCount = allSignedPrekeys.count - deletedCount;
if (remainingCount <= 3) {
break;
int oldAcceptedSignedPreKeyCount = 0;
for (SignedPreKeyRecord *signedPrekey in oldSignedPrekeys) {
if (signedPrekey.wasAcceptedByService) {
oldAcceptedSignedPreKeyCount++;
}
}
// 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.
if (fabs([deletionCandidate.generatedAt timeIntervalSinceNow]) < kSignedPreKeysDeletionTime) {
break;
if (fabs([signedPrekey.generatedAt timeIntervalSinceNow]) < kSignedPreKeysDeletionTime) {
continue;
}
OWSAnalyticsInfo(
@"Deleting old signed prekey: %@", [dateFormatter stringFromDate:deletionCandidate.generatedAt]);
[storageManager removeSignedPreKey:deletionCandidate.Id];
deletedCount++;
// We try to keep a minimum of 3 "old, accepted" signed prekeys.
if (signedPrekey.wasAcceptedByService) {
if (oldAcceptedSignedPreKeyCount < 3) {
continue;
} else {
oldAcceptedSignedPreKeyCount--;
}
}
OWSAnalyticsInfo(@"%@ Deleting old signed prekey: %@, wasAcceptedByService: %d",
self.tag,
[dateFormatter stringFromDate:signedPrekey.generatedAt],
signedPrekey.wasAcceptedByService);
[storageManager removeSignedPreKey:signedPrekey.Id];
}
});
}

@ -9,6 +9,12 @@
- (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
- (int)prekeyUpdateFailureCount;

@ -15,6 +15,7 @@ NSString *const TSStorageManagerSignedPreKeyStoreCollection = @"TSStorageManager
NSString *const TSStorageManagerSignedPreKeyMetadataCollection = @"TSStorageManagerSignedPreKeyMetadataCollection";
NSString *const TSStorageManagerKeyPrekeyUpdateFailureCount = @"prekeyUpdateFailureCount";
NSString *const TSStorageManagerKeyFirstPrekeyUpdateFailureDate = @"firstPrekeyUpdateFailureDate";
NSString *const TSStorageManagerKeyPrekeyCurrentSignedPrekeyId = @"currentSignedPrekeyId";
@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 {
NSMutableArray *signedPreKeyRecords = [NSMutableArray array];
@ -73,6 +80,19 @@ NSString *const TSStorageManagerKeyFirstPrekeyUpdateFailureDate = @"firstPrekeyU
[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
- (int)prekeyUpdateFailureCount;

Loading…
Cancel
Save