Clean up prekey logic.

// FREEBIE
This commit is contained in:
Michael Kirk 2017-02-09 19:35:10 -05:00 committed by Matthew Chen
parent e1949893f2
commit e0688e16a7
10 changed files with 336 additions and 116 deletions

View File

@ -1,9 +1,5 @@
//
// TSAccountManagement.m
// TextSecureKit
//
// Created by Frederic Jacobs on 27/10/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSAccountManager.h"
@ -210,7 +206,9 @@ NS_ASSUME_NONNULL_BEGIN
[TSStorageManager storeServerToken:authToken signalingKey:signalingKey];
[self didRegister];
[TSSocketManager becomeActiveFromForeground];
[TSPreKeyManager registerPreKeysWithSuccess:successBlock failure:failureBlock];
[TSPreKeyManager registerPreKeysWithMode:RefreshPreKeysMode_SignedAndOneTime
success:successBlock
failure:failureBlock];
break;
}
default: {

View File

@ -1,21 +1,31 @@
//
// TSPrekeyManager.h
// TextSecureKit
//
// Created by Frederic Jacobs on 07/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TSAccountManager.h"
// Time before deletion of signed PreKeys (measured in seconds)
#define SignedPreKeysDeletionTime 14 * 24 * 60 * 60
typedef NS_ENUM(NSInteger, RefreshPreKeysMode) {
// Refresh the signed prekey AND the one-time prekeys.
RefreshPreKeysMode_SignedAndOneTime,
// Only refresh the signed prekey, which should happen around every 48 hours.
//
// Most users will refresh their signed prekeys much more often than their
// one-time prekeys, so we use a "signed only" mode to avoid updating the
// one-time keys in this case.
//
// We do not need a "one-time only" mode.
RefreshPreKeysMode_SignedOnly,
};
@interface TSPreKeyManager : NSObject
+ (void)registerPreKeysWithSuccess:(void (^)())successHandler failure:(void (^)(NSError *error))failureHandler;
+ (void)registerPreKeysWithMode:(RefreshPreKeysMode)mode
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler;
+ (void)refreshPreKeys;
+ (void)checkPreKeysIfNecessary;
@end

View File

@ -1,79 +1,185 @@
//
// TSPrekeyManager.m
// TextSecureKit
//
// Created by Frederic Jacobs on 07/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSPreKeyManager.h"
#import "NSURLSessionDataTask+StatusCode.h"
#import "TSNetworkManager.h"
#import "TSRegisterSignedPrekeyRequest.h"
#import "TSStorageHeaders.h"
#define EPHEMERAL_PREKEYS_MINIMUM 15
// 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;
// Time before rotation of signed prekeys (measured in seconds)
//
// Currently we rotate signed prekeys every 2 days (48 hours).
static const CGFloat kSignedPreKeysRotationTime = 2 * 24 * 60 * 60;
// How often we check prekey state on app activation.
//
// Currently we check prekey state every 12 hours.
static const CGFloat kPreKeyCheckFrequencySeconds = 12 * 60 * 60;
// We generate 100 one-time prekeys at a time. We should replenish
// whenever ~2/3 of them have been consumed.
static const NSUInteger kEphemeralPreKeysMinimumCount = 35;
// This global should only be accessed on prekeyQueue.
static NSDate *lastPreKeyCheckTimestamp = nil;
@implementation TSPreKeyManager
+ (void)registerPreKeysWithSuccess:(void (^)())success failure:(void (^)(NSError *))failureBlock
// We should never dispatch sync to this queue.
+ (dispatch_queue_t)prekeyQueue
{
TSStorageManager *storageManager = [TSStorageManager sharedManager];
ECKeyPair *identityKeyPair = [storageManager identityKeyPair];
static dispatch_once_t onceToken;
static dispatch_queue_t queue;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("org.whispersystems.signal.prekeyQueue", NULL);
});
return queue;
}
if (!identityKeyPair) {
[storageManager generateNewIdentityKey];
identityKeyPair = [storageManager identityKeyPair];
}
+ (void)checkPreKeysIfNecessary
{
OWSAssert([UIApplication sharedApplication].applicationState == UIApplicationStateActive);
PreKeyRecord *lastResortPreKey = [storageManager getOrGenerateLastResortKey];
SignedPreKeyRecord *signedPreKey = [storageManager generateRandomSignedRecord];
NSArray *preKeys = [storageManager generatePreKeyRecords];
TSRegisterPrekeysRequest *request =
[[TSRegisterPrekeysRequest alloc] initWithPrekeyArray:preKeys
identityKey:[storageManager identityKeyPair].publicKey
signedPreKeyRecord:signedPreKey
preKeyLastResort:lastResortPreKey];
[[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) {
DDLogInfo(@"%@ Successfully registered pre keys.", self.tag);
[storageManager storePreKeyRecords:preKeys];
[storageManager storeSignedPreKey:signedPreKey.Id signedPreKeyRecord:signedPreKey];
success();
// Update the prekey check timestamp.
dispatch_async(TSPreKeyManager.prekeyQueue, ^{
BOOL shouldCheck = (lastPreKeyCheckTimestamp == nil
|| fabs([lastPreKeyCheckTimestamp timeIntervalSinceNow]) >= kPreKeyCheckFrequencySeconds);
if (shouldCheck) {
[[TSAccountManager sharedInstance] ifRegistered:YES
runAsync:^{
[TSPreKeyManager refreshPreKeys];
}];
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogError(@"%@ Failed to register pre keys.", self.tag);
failureBlock(error);
}];
});
}
+ (void)registerPreKeysWithMode:(RefreshPreKeysMode)mode
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler
{
// We use prekeyQueue to serialize this logic and ensure that only
// one thread is "registering" or "clearing" prekeys at a time.
dispatch_async(TSPreKeyManager.prekeyQueue, ^{
RefreshPreKeysMode modeCopy = mode;
TSStorageManager *storageManager = [TSStorageManager sharedManager];
ECKeyPair *identityKeyPair = [storageManager identityKeyPair];
if (!identityKeyPair) {
[storageManager generateNewIdentityKey];
identityKeyPair = [storageManager identityKeyPair];
// Switch modes if necessary.
modeCopy = RefreshPreKeysMode_SignedAndOneTime;
}
SignedPreKeyRecord *signedPreKey = [storageManager generateRandomSignedRecord];
NSArray *preKeys = nil;
TSRequest *request;
NSString *description;
if (modeCopy == RefreshPreKeysMode_SignedAndOneTime) {
description = @"signed and one-time prekeys";
PreKeyRecord *lastResortPreKey = [storageManager getOrGenerateLastResortKey];
preKeys = [storageManager generatePreKeyRecords];
request = [[TSRegisterPrekeysRequest alloc] initWithPrekeyArray:preKeys
identityKey:[storageManager identityKeyPair].publicKey
signedPreKeyRecord:signedPreKey
preKeyLastResort:lastResortPreKey];
} else {
description = @"just signed prekey";
request = [[TSRegisterSignedPrekeyRequest alloc] initWithSignedPreKeyRecord:signedPreKey];
}
[[TSNetworkManager sharedManager] makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) {
DDLogInfo(@"%@ Successfully registered %@.", self.tag, description);
if (modeCopy == RefreshPreKeysMode_SignedAndOneTime) {
[storageManager storePreKeyRecords:preKeys];
}
[storageManager storeSignedPreKey:signedPreKey.Id signedPreKeyRecord:signedPreKey];
successHandler();
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogError(@"%@ Failed to register %@.", self.tag, description);
failureHandler(error);
}];
});
}
+ (void)refreshPreKeys {
// We want to update prekeys if either the one-time or signed prekeys need an update, so
// we check the status of both.
//
// Most users will refresh their signed prekeys much more often than their
// one-time PreKeys, so we use a "signed only" mode to avoid updating the
// one-time keys in this case.
//
// We do not need a "one-time only" mode.
TSAvailablePreKeysCountRequest *preKeyCountRequest = [[TSAvailablePreKeysCountRequest alloc] init];
[[TSNetworkManager sharedManager] makeRequest:preKeyCountRequest
success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
NSString *preKeyCountKey = @"count";
NSNumber *count = [responseObject objectForKey:preKeyCountKey];
NSString *preKeyCountKey = @"count";
NSNumber *count = [responseObject objectForKey:preKeyCountKey];
if (count.integerValue > EPHEMERAL_PREKEYS_MINIMUM) {
DDLogVerbose(@"Available prekeys sufficient: %@", count.stringValue);
return;
} else {
[self registerPreKeysWithSuccess:^{
DDLogInfo(@"New PreKeys registered with server.");
void (^updatePreKeys)(RefreshPreKeysMode) = ^(RefreshPreKeysMode mode) {
[self registerPreKeysWithMode:mode
success:^{
DDLogInfo(@"%@ New prekeys registered with server.", self.tag);
[self clearSignedPreKeyRecords];
}
failure:^(NSError *error) {
DDLogWarn(@"Failed to update prekeys with the server");
}];
}
[self clearSignedPreKeyRecords];
}
failure:^(NSError *error) {
DDLogWarn(@"%@ Failed to update prekeys with the server", self.tag);
}];
};
BOOL shouldUpdateOneTimePreKeys = count.integerValue <= kEphemeralPreKeysMinimumCount;
if (shouldUpdateOneTimePreKeys) {
DDLogInfo(@"%@ Updating one-time and signed prekeys due to shortage of one-time prekeys.", self.tag);
updatePreKeys(RefreshPreKeysMode_SignedAndOneTime);
} 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];
SignedPreKeyRecord *currentRecord = [storageManager loadSignedPrekey:keyId.intValue];
OWSAssert(currentRecord);
BOOL shouldUpdateSignedPrekey
= fabs([currentRecord.generatedAt timeIntervalSinceNow]) >= kSignedPreKeysRotationTime;
if (shouldUpdateSignedPrekey) {
DDLogInfo(@"%@ Updating signed prekey due to rotation period.", self.tag);
updatePreKeys(RefreshPreKeysMode_SignedOnly);
} else {
DDLogDebug(@"%@ Not updating prekeys.", self.tag);
}
// 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);
}];
}
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogError(@"Failed to retrieve the number of available prekeys.");
DDLogError(@"%@ Failed to retrieve the number of available prekeys.", self.tag);
}];
}
@ -81,37 +187,62 @@
TSRequest *currentSignedPreKey = [[TSCurrentSignedPreKeyRequest alloc] init];
[[TSNetworkManager sharedManager] makeRequest:currentSignedPreKey
success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
NSString *keyIdDictKey = @"keyId";
NSNumber *keyId = [responseObject objectForKey:keyIdDictKey];
NSString *keyIdDictKey = @"keyId";
NSNumber *keyId = [responseObject objectForKey:keyIdDictKey];
[self clearSignedPreKeyRecordsWithKeyId:keyId];
[self clearSignedPreKeyRecordsWithKeyId:keyId];
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogWarn(@"Failed to retrieve current prekey.");
DDLogWarn(@"%@ Failed to retrieve current prekey.", self.tag);
}];
}
+ (void)clearSignedPreKeyRecordsWithKeyId:(NSNumber *)keyId {
if (!keyId) {
DDLogError(@"The server returned an incomplete ");
DDLogError(@"%@ The server returned an incomplete response", self.tag);
return;
}
TSStorageManager *storageManager = [TSStorageManager sharedManager];
SignedPreKeyRecord *currentRecord = [storageManager loadSignedPrekey:keyId.intValue];
NSArray *allSignedPrekeys = [storageManager loadSignedPreKeys];
NSArray *oldSignedPrekeys = [self removeCurrentRecord:currentRecord fromRecords:allSignedPrekeys];
// We use prekeyQueue to serialize this logic and ensure that only
// one thread is "registering" or "clearing" prekeys at a time.
dispatch_async(TSPreKeyManager.prekeyQueue, ^{
TSStorageManager *storageManager = [TSStorageManager sharedManager];
SignedPreKeyRecord *currentRecord = [storageManager loadSignedPrekey:keyId.intValue];
NSArray *allSignedPrekeys = [storageManager loadSignedPreKeys];
NSArray *oldSignedPrekeys = [self removeCurrentRecord:currentRecord fromRecords:allSignedPrekeys];
if ([oldSignedPrekeys count] > 3) {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateStyle = NSDateFormatterMediumStyle;
dateFormatter.timeStyle = NSDateFormatterMediumStyle;
// Sort the signed prekeys in ascending order of generation time.
oldSignedPrekeys = [oldSignedPrekeys sortedArrayUsingComparator:^NSComparisonResult(
SignedPreKeyRecord *_Nonnull left, SignedPreKeyRecord *_Nonnull right) {
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) {
DDLogInfo(@"Old signed prekey record: %@", deletionCandidate.generatedAt);
if ([deletionCandidate.generatedAt timeIntervalSinceNow] > SignedPreKeysDeletionTime) {
[storageManager removeSignedPreKey:deletionCandidate.Id];
// Always keep the last three signed prekeys.
NSUInteger remainingCount = allSignedPrekeys.count - deletedCount;
if (remainingCount <= 3) {
break;
}
// Never delete signed prekeys until they are N days old.
if (fabs([deletionCandidate.generatedAt timeIntervalSinceNow]) < kSignedPreKeysDeletionTime) {
break;
}
DDLogInfo(@"%@ Deleting old signed prekey: %@",
self.tag,
[dateFormatter stringFromDate:deletionCandidate.generatedAt]);
[storageManager removeSignedPreKey:deletionCandidate.Id];
deletedCount++;
}
}
});
}
+ (NSArray *)removeCurrentRecord:(SignedPreKeyRecord *)currentRecord fromRecords:(NSArray *)allRecords {

View File

@ -1,9 +1,5 @@
//
// PreKeyBundle+jsonDict.m
// Signal
//
// Created by Frederic Jacobs on 26/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "NSData+Base64.h"
@ -40,15 +36,22 @@
int registrationId = [registrationIdString intValue];
int deviceId = [deviceIdString intValue];
NSDictionary *preKey = [deviceDict objectForKey:@"preKey"];
int prekeyId;
NSData *preKeyPublic = nil;
NSDictionary *_Nullable preKeyDict;
id optionalPreKeyDict = [deviceDict objectForKey:@"preKey"];
// JSON parsing might give us NSNull, so we can't simply check for non-nil.
if ([optionalPreKeyDict isKindOfClass:[NSDictionary class]]) {
preKeyDict = (NSDictionary *)optionalPreKeyDict;
}
if (!preKey) {
int prekeyId;
NSData *_Nullable preKeyPublic;
if (!preKeyDict) {
DDLogInfo(@"%@ No one-time prekey included in the bundle.", self.tag);
prekeyId = -1;
} else {
prekeyId = [[preKey objectForKey:@"keyId"] intValue];
NSString *preKeyPublicString = [preKey objectForKey:@"publicKey"];
prekeyId = [[preKeyDict objectForKey:@"keyId"] intValue];
NSString *preKeyPublicString = [preKeyDict objectForKey:@"publicKey"];
preKeyPublic = [NSData dataFromBase64StringNoPadding:preKeyPublicString];
}
@ -92,4 +95,16 @@
return bundle;
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end

View File

@ -31,6 +31,7 @@
#import "TSInfoMessage.h"
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
#import "TSNetworkManager.h"
#import "TSPreKeyManager.h"
#import "TSStorageHeaders.h"
#import "TextSecureKitEnv.h"
#import <AxolotlKit/AxolotlExceptions.h>
@ -232,6 +233,7 @@ NS_ASSUME_NONNULL_BEGIN
completion:(void (^)(NSError *_Nullable error))completion
{
OWSAssert([NSThread isMainThread]);
@synchronized(self) {
TSStorageManager *storageManager = [TSStorageManager sharedManager];
NSString *recipientId = preKeyEnvelope.source;
@ -247,6 +249,9 @@ NS_ASSUME_NONNULL_BEGIN
dispatch_async([OWSDispatch sessionCipher], ^{
NSData *plaintextData;
@try {
// Check whether we need to refresh our PreKeys every time we receive a PreKeyWhisperMessage.
[TSPreKeyManager refreshPreKeys];
PreKeyWhisperMessage *message = [[PreKeyWhisperMessage alloc] initWithData:encryptedData];
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
preKeyStore:storageManager

View File

@ -0,0 +1,14 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSRequest.h"
@class SignedPreKeyRecord;
@class PreKeyRecord;
@interface TSRegisterSignedPrekeyRequest : TSRequest
- (id)initWithSignedPreKeyRecord:(SignedPreKeyRecord *)signedRecord;
@end

View File

@ -0,0 +1,36 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSRegisterSignedPrekeyRequest.h"
#import "TSConstants.h"
#import <25519/Curve25519.h>
#import <AxolotlKit/NSData+keyVersionByte.h>
#import <AxolotlKit/PreKeyRecord.h>
#import <AxolotlKit/SignedPreKeyStore.h>
@implementation TSRegisterSignedPrekeyRequest
- (id)initWithSignedPreKeyRecord:(SignedPreKeyRecord *)signedRecord
{
self = [super initWithURL:[NSURL URLWithString:textSecureSignedKeysAPI]];
self.HTTPMethod = @"PUT";
NSDictionary *serializedKeyRegistrationParameters = [self dictionaryFromSignedPreKey:signedRecord];
self.parameters = [serializedKeyRegistrationParameters mutableCopy];
return self;
}
- (NSDictionary *)dictionaryFromSignedPreKey:(SignedPreKeyRecord *)preKey
{
return @{
@"keyId" : [NSNumber numberWithInt:preKey.Id],
@"publicKey" : [[preKey.keyPair.publicKey prependKeyType] base64EncodedStringWithOptions:0],
@"signature" : [preKey.signature base64EncodedStringWithOptions:0]
};
}
@end

View File

@ -1,9 +1,5 @@
//
// TSStorageManager+PreKeyStore.h
// TextSecureKit
//
// Created by Frederic Jacobs on 06/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <AxolotlKit/PreKeyStore.h>

View File

@ -1,29 +1,25 @@
//
// TSStorageManager+PreKeyStore.m
// TextSecureKit
//
// Created by Frederic Jacobs on 06/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <AxolotlKit/AxolotlExceptions.h>
#import "TSStorageManager+PreKeyStore.h"
#import "TSStorageManager+keyFromIntLong.h"
#import <AxolotlKit/AxolotlExceptions.h>
#import <AxolotlKit/SessionBuilder.h>
#define TSStorageManagerPreKeyStoreCollection @"TSStorageManagerPreKeyStoreCollection"
#define TSNextPrekeyIdKey @"TSStorageInternalSettingsNextPreKeyId"
#define BATCH_SIZE 100
#define MAX_VALUE_LASTRESORT 0xFFFFFF
@implementation TSStorageManager (PreKeyStore)
- (PreKeyRecord *)getOrGenerateLastResortKey {
if ([self containsPreKey:MAX_VALUE_LASTRESORT]) {
return [self loadPreKey:MAX_VALUE_LASTRESORT];
if ([self containsPreKey:kPreKeyOfLastResortId]) {
return [self loadPreKey:kPreKeyOfLastResortId];
} else {
PreKeyRecord *lastResort =
[[PreKeyRecord alloc] initWithId:MAX_VALUE_LASTRESORT keyPair:[Curve25519 generateKeyPair]];
[self storePreKey:MAX_VALUE_LASTRESORT preKeyRecord:lastResort];
[[PreKeyRecord alloc] initWithId:kPreKeyOfLastResortId keyPair:[Curve25519 generateKeyPair]];
[self storePreKey:kPreKeyOfLastResortId preKeyRecord:lastResort];
return lastResort;
}
}
@ -33,6 +29,8 @@
@synchronized(self) {
int preKeyId = [self nextPreKeyId];
DDLogInfo(@"%@ building %d new preKeys starting from preKeyId: %d", self.tag, BATCH_SIZE, preKeyId);
for (int i = 0; i < BATCH_SIZE; i++) {
ECKeyPair *keyPair = [Curve25519 generateKeyPair];
PreKeyRecord *record = [[PreKeyRecord alloc] initWithId:preKeyId keyPair:keyPair];
@ -81,11 +79,29 @@
- (int)nextPreKeyId {
int lastPreKeyId = [self intForKey:TSNextPrekeyIdKey inCollection:TSStorageInternalSettingsCollection];
while (lastPreKeyId < 1 || (lastPreKeyId > (MAX_VALUE_LASTRESORT - BATCH_SIZE))) {
lastPreKeyId = rand();
if (lastPreKeyId < 1) {
// One-time prekey ids must be > 0 and < kPreKeyOfLastResortId.
lastPreKeyId = 1 + arc4random_uniform(kPreKeyOfLastResortId - (BATCH_SIZE + 1));
} else if (lastPreKeyId > kPreKeyOfLastResortId - BATCH_SIZE) {
// We want to "overflow" to 1 when we reach the "prekey of last resort" id
// to avoid biasing towards higher values.
lastPreKeyId = 1;
}
OWSCAssert(lastPreKeyId > 0 && lastPreKeyId < kPreKeyOfLastResortId);
return lastPreKeyId;
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@+PreKeyStore]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end

View File

@ -1,13 +1,9 @@
//
// TSStorageManager+SignedPreKeyStore.m
// TextSecureKit
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
// Created by Frederic Jacobs on 06/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager+PreKeyStore.h"
#import "TSStorageManager+SignedPreKeyStore.h"
#import "TSStorageManager+keyFromIntLong.h"
@ -19,8 +15,11 @@
- (SignedPreKeyRecord *)generateRandomSignedRecord {
ECKeyPair *keyPair = [Curve25519 generateKeyPair];
// Signed prekey ids must be > 0.
int preKeyId = 1 + arc4random_uniform(INT32_MAX - 1);
return [[SignedPreKeyRecord alloc]
initWithId:rand()
initWithId:preKeyId
keyPair:keyPair
signature:[Ed25519 sign:keyPair.publicKey.prependKeyType withKeyPair:[self identityKeyPair]]
generatedAt:[NSDate date]];