Key material changes

- For new installs, generate raw key-spec rather than derive it
- Adapt to separated concerns of the key derivation migration from the unencrypted header migration
- Reduce number of places where we delete/generate keying information
- Only store relevant keying material

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent 938b9c85b8
commit 426c9baa16

@ -141,7 +141,7 @@ DEPENDENCIES:
- SocketRocket (from `https://github.com/facebook/SocketRocket.git`) - SocketRocket (from `https://github.com/facebook/SocketRocket.git`)
- SQLCipher (from `https://github.com/sqlcipher/sqlcipher.git`, commit `d5c2bec`) - SQLCipher (from `https://github.com/sqlcipher/sqlcipher.git`, commit `d5c2bec`)
- SSZipArchive - SSZipArchive
- YapDatabase/SQLCipher (from `../YapDatabase`) - YapDatabase/SQLCipher (from `https://github.com/WhisperSystems/YapDatabase.git`, branch `release/unencryptedHeaders`)
- YYImage - YYImage
EXTERNAL SOURCES: EXTERNAL SOURCES:
@ -167,7 +167,8 @@ EXTERNAL SOURCES:
:commit: d5c2bec :commit: d5c2bec
:git: https://github.com/sqlcipher/sqlcipher.git :git: https://github.com/sqlcipher/sqlcipher.git
YapDatabase: YapDatabase:
:path: ../YapDatabase :branch: release/unencryptedHeaders
:git: https://github.com/WhisperSystems/YapDatabase.git
CHECKOUT OPTIONS: CHECKOUT OPTIONS:
AxolotlKit: AxolotlKit:
@ -191,6 +192,9 @@ CHECKOUT OPTIONS:
SQLCipher: SQLCipher:
:commit: d5c2bec :commit: d5c2bec
:git: https://github.com/sqlcipher/sqlcipher.git :git: https://github.com/sqlcipher/sqlcipher.git
YapDatabase:
:commit: a88958a8db03a050650a495394e1817e48d99f4b
:git: https://github.com/WhisperSystems/YapDatabase.git
SPEC CHECKSUMS: SPEC CHECKSUMS:
AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
@ -217,6 +221,6 @@ SPEC CHECKSUMS:
YapDatabase: 299a32de9d350d37a9ac5b0532609d87d5d2a5de YapDatabase: 299a32de9d350d37a9ac5b0532609d87d5d2a5de
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
PODFILE CHECKSUM: bb32efdc239e2a93d6304d25de33a25dc4cdbab2 PODFILE CHECKSUM: 0d804514eb2db34b9874b653e543255d8c2f5751
COCOAPODS: 1.3.1 COCOAPODS: 1.3.1

@ -257,7 +257,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
} }
NSError *error; NSError *error;
NSData *_Nullable databasePassword = [OWSStorage tryToLoadDatabasePassword:&error]; NSData *_Nullable databasePassword = [OWSStorage tryToLoadDatabaseLegacyPassphrase:&error];
if (!databasePassword || error) { if (!databasePassword || error) {
return (error return (error
?: OWSErrorWithCodeDescription( ?: OWSErrorWithCodeDescription(
@ -266,11 +266,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
YapDatabaseSaltBlock recordSaltBlock = ^(NSData *saltData) { YapDatabaseSaltBlock recordSaltBlock = ^(NSData *saltData) {
DDLogVerbose(@"%@ saltData: %@", self.logTag, saltData.hexadecimalString); DDLogVerbose(@"%@ saltData: %@", self.logTag, saltData.hexadecimalString);
[OWSStorage storeDatabaseSalt:saltData];
}; // Derive and store the raw cipher key spec, to avoid the ongoing tax of future KDF
YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) { NSData *keySpecData =
DDLogVerbose(@"%@ keySpecData: %@", self.logTag, keySpecData.hexadecimalString); [YapDatabaseCryptoUtils deriveDatabaseKeySpecForPassword:databasePassword saltData:saltData];
[OWSStorage storeDatabaseKeySpec:keySpecData]; [OWSStorage storeDatabaseCipherKeySpec:keySpecData];
}; };
return [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath return [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath

@ -298,19 +298,15 @@ NS_ASSUME_NONNULL_BEGIN
XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]); XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]);
__block NSData *_Nullable databaseSalt = nil; __block NSData *_Nullable databaseSalt = nil;
__block NSData *_Nullable databaseKeySpec = nil;
YapDatabaseSaltBlock recordSaltBlock = ^(NSData *saltData) { YapDatabaseSaltBlock recordSaltBlock = ^(NSData *saltData) {
OWSAssert(!databaseSalt); OWSAssert(!databaseSalt);
OWSAssert(saltData); OWSAssert(saltData);
databaseSalt = saltData; databaseSalt = saltData;
databaseKeySpec = [YapDatabaseCryptoUtils deriveDatabaseKeySpecForPassword:databasePassword saltData:saltData];
}; };
__block NSData *_Nullable databaseKeySpec = nil;
YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) {
OWSAssert(!databaseKeySpec);
OWSAssert(keySpecData);
databaseKeySpec = keySpecData;
};
NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath
databasePassword:databasePassword databasePassword:databasePassword
recordSaltBlock:recordSaltBlock]; recordSaltBlock:recordSaltBlock];
@ -339,19 +335,16 @@ NS_ASSUME_NONNULL_BEGIN
XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]); XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]);
__block NSData *_Nullable databaseSalt = nil; __block NSData *_Nullable databaseSalt = nil;
YapDatabaseSaltBlock saltBlock = ^(NSData *saltData) {
__block NSData *_Nullable databaseKeySpec = nil;
YapDatabaseSaltBlock recordSaltBlock = ^(NSData *saltData) {
OWSAssert(!databaseSalt); OWSAssert(!databaseSalt);
OWSAssert(saltData); OWSAssert(saltData);
databaseSalt = saltData; databaseSalt = saltData;
databaseKeySpec = [YapDatabaseCryptoUtils deriveDatabaseKeySpecForPassword:databasePassword saltData:saltData];
}; };
__block NSData *_Nullable databaseKeySpec = nil;
YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) {
OWSAssert(!databaseKeySpec);
OWSAssert(keySpecData);
databaseKeySpec = keySpecData;
};
NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath
databasePassword:databasePassword databasePassword:databasePassword
recordSaltBlock:recordSaltBlock]; recordSaltBlock:recordSaltBlock];
@ -398,23 +391,19 @@ NS_ASSUME_NONNULL_BEGIN
XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]); XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]);
__block NSData *_Nullable databaseSalt = nil; __block NSData *_Nullable databaseSalt = nil;
__block NSData *_Nullable databaseKeySpec = nil;
YapDatabaseSaltBlock recordSaltBlock = ^(NSData *saltData) { YapDatabaseSaltBlock recordSaltBlock = ^(NSData *saltData) {
OWSAssert(!databaseSalt); OWSAssert(!databaseSalt);
OWSAssert(saltData); OWSAssert(saltData);
databaseSalt = saltData; databaseSalt = saltData;
databaseKeySpec = [YapDatabaseCryptoUtils deriveDatabaseKeySpecForPassword:databasePassword saltData:saltData];
}; };
__block NSData *_Nullable databaseKeySpec = nil;
YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) {
OWSAssert(!databaseKeySpec);
OWSAssert(keySpecData);
databaseKeySpec = keySpecData;
};
NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath
databasePassword:databasePassword databasePassword:databasePassword
saltBlock:saltBlock recordSaltBlock:recordSaltBlock];
keySpecBlock:keySpecBlock];
if (error) { if (error) {
DDLogError(@"%s error: %@", __PRETTY_FUNCTION__, error); DDLogError(@"%s error: %@", __PRETTY_FUNCTION__, error);
} }
@ -428,9 +417,9 @@ NS_ASSUME_NONNULL_BEGIN
// Verify the contents of the unconverted database. // Verify the contents of the unconverted database.
__block BOOL isValid = NO; __block BOOL isValid = NO;
[self openYapDatabase:databaseFilePath [self openYapDatabase:databaseFilePath
databasePassword:databasePassword databasePassword:nil
databaseSalt:nil databaseSalt:nil
databaseKeySpec:nil databaseKeySpec:databaseKeySpec
databaseBlock:^(YapDatabase *database) { databaseBlock:^(YapDatabase *database) {
YapDatabaseConnection *dbConnection = database.newConnection; YapDatabaseConnection *dbConnection = database.newConnection;
isValid = [dbConnection numberOfKeysInCollection:@"test_collection_name"] == kItemCount; isValid = [dbConnection numberOfKeysInCollection:@"test_collection_name"] == kItemCount;
@ -453,11 +442,6 @@ NS_ASSUME_NONNULL_BEGIN
XCTFail(@"%s No conversion should be necessary", __PRETTY_FUNCTION__); XCTFail(@"%s No conversion should be necessary", __PRETTY_FUNCTION__);
}; };
YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) {
OWSAssert(keySpecData);
XCTFail(@"%s No conversion should be necessary", __PRETTY_FUNCTION__);
};
NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath
databasePassword:databasePassword databasePassword:databasePassword
@ -490,11 +474,6 @@ NS_ASSUME_NONNULL_BEGIN
XCTFail(@"%s No conversion should be necessary", __PRETTY_FUNCTION__); XCTFail(@"%s No conversion should be necessary", __PRETTY_FUNCTION__);
}; };
YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) {
OWSAssert(keySpecData);
XCTFail(@"%s No conversion should be necessary", __PRETTY_FUNCTION__);
};
NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath
databasePassword:databasePassword databasePassword:databasePassword

@ -75,13 +75,10 @@ extern NSString *const StorageIsReadyNotification;
*/ */
+ (BOOL)isDatabasePasswordAccessible; + (BOOL)isDatabasePasswordAccessible;
+ (nullable NSData *)tryToLoadDatabasePassword:(NSError **)errorHandle; + (nullable NSData *)tryToLoadDatabaseLegacyPassphrase:(NSError **)errorHandle;
+ (nullable NSData *)tryToLoadDatabaseSalt:(NSError **)errorHandle; + (void)storeDatabaseCipherKeySpec:(NSData *)cipherKeySpecData;
+ (void)storeDatabaseSalt:(NSData *)saltData;
+ (nullable NSData *)tryToLoadDatabaseKeySpec:(NSError **)errorHandle;
+ (void)storeDatabaseKeySpec:(NSData *)keySpecData;
@end @end

@ -27,9 +27,8 @@ NSString *const OWSStorageExceptionName_NoDatabase = @"OWSStorageExceptionName_N
NSString *const OWSResetStorageNotification = @"OWSResetStorageNotification"; NSString *const OWSResetStorageNotification = @"OWSResetStorageNotification";
static NSString *keychainService = @"TSKeyChainService"; static NSString *keychainService = @"TSKeyChainService";
static NSString *keychainDBPassAccount = @"TSDatabasePass"; static NSString *keychainDBLegacyPassphrase = @"TSDatabasePass";
static NSString *keychainDBSalt = @"OWSDatabaseSalt"; static NSString *keychainDBCipherKeySpec = @"OWSDatabaseCipherKeySpec";
static NSString *keychainDBKeySpec = @"OWSDatabaseKeySpec";
const NSUInteger kDatabasePasswordLength = 30; const NSUInteger kDatabasePasswordLength = 30;
@ -381,17 +380,9 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
- (BOOL)tryToLoadDatabase - (BOOL)tryToLoadDatabase
{ {
// We determine the database password, salt and key spec first, since a side effect of // We determine the database key spec first, since a side effect of
// this can be deleting any existing database file (if we're recovering // this can be deleting any existing database file (if we're recovering
// from a corrupt keychain). // from a corrupt keychain).
//
// Although we don't use databasePassword or databaseSalt in this method,
// we use their accessors to ensure that all three exist in the keychain
// and can be loaded or that we reset the database & keychain.
// NSData *databasePassword = [self databasePassword];
// OWSAssert(databasePassword.length > 0);
// NSData *databaseSalt = [self databaseSalt];
// OWSAssert(databaseSalt.length > 0);
NSData *databaseKeySpec = [self databaseKeySpec]; NSData *databaseKeySpec = [self databaseKeySpec];
OWSAssert(databaseKeySpec.length == kSQLCipherKeySpecLength); OWSAssert(databaseKeySpec.length == kSQLCipherKeySpecLength);
@ -401,6 +392,11 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
options.cipherKeySpecBlock = ^{ options.cipherKeySpecBlock = ^{
return databaseKeySpec; return databaseKeySpec;
}; };
// We leave a portion of the header decrypted so that iOS will recognize the file
// as a SQLite database. Otherwise, because the database lives in a shared data container,
// and our usage of sqlite's write-ahead logging retains a lock on the database, the OS
// would kill the app/share extension as soon as it is backgrounded.
options.cipherUnencryptedHeaderLength = kSqliteHeaderLength; options.cipherUnencryptedHeaderLength = kSqliteHeaderLength;
// If any of these asserts fails, we need to verify and update // If any of these asserts fails, we need to verify and update
@ -506,7 +502,7 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
// This might be redundant but in the spirit of thoroughness... // This might be redundant but in the spirit of thoroughness...
[self deleteDatabaseFiles]; [self deleteDatabaseFiles];
[self deletePasswordFromKeychain]; [self deleteDBKeys];
if (CurrentAppContext().isMainApp) { if (CurrentAppContext().isMainApp) {
[TSAttachmentStream deleteAttachments]; [TSAttachmentStream deleteAttachments];
@ -528,116 +524,41 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
+ (BOOL)isDatabasePasswordAccessible + (BOOL)isDatabasePasswordAccessible
{ {
[SAMKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly];
NSError *error; NSError *error;
NSString *dbPassword = [SAMKeychain passwordForService:keychainService account:keychainDBPassAccount error:&error]; NSData *cipherKeySpec = [self tryToLoadDatabaseCipherKeySpec:&error];
if (dbPassword && !error) { if (cipherKeySpec && !error) {
return YES; return YES;
} }
if (error) { if (error) {
DDLogWarn(@"Database password couldn't be accessed: %@", error.localizedDescription); DDLogWarn(@"Database key couldn't be accessed: %@", error.localizedDescription);
} }
return NO; return NO;
} }
+ (nullable NSData *)tryToLoadKeyChainValue:(NSString *)keychainKey errorHandle:(NSError **)errorHandle + (nullable NSData *)tryToLoadDatabaseLegacyPassphrase:(NSError **)errorHandle
{
OWSAssert(keychainKey.length > 0);
OWSAssert(errorHandle);
[SAMKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly];
return [SAMKeychain passwordDataForService:keychainService account:keychainKey error:errorHandle];
}
+ (nullable NSData *)tryToLoadDatabasePassword:(NSError **)errorHandle
{
return [self tryToLoadKeyChainValue:keychainDBPassAccount errorHandle:errorHandle];
}
+ (nullable NSData *)tryToLoadDatabaseSalt:(NSError **)errorHandle
{
return [self tryToLoadKeyChainValue:keychainDBSalt errorHandle:errorHandle];
}
+ (nullable NSData *)tryToLoadDatabaseKeySpec:(NSError **)errorHandle
{ {
return [self tryToLoadKeyChainValue:keychainDBKeySpec errorHandle:errorHandle]; return [self tryToLoadKeyChainValue:keychainDBLegacyPassphrase errorHandle:errorHandle];
} }
- (NSData *)databasePassword + (nullable NSData *)tryToLoadDatabaseCipherKeySpec:(NSError **)errorHandle
{ {
return [self loadMetadataOrClearDatabase:^(NSError **_Nullable errorHandle) { return [self tryToLoadKeyChainValue:keychainDBCipherKeySpec errorHandle:errorHandle];
return [OWSStorage tryToLoadDatabasePassword:errorHandle];
}
createDataBlock:^{
NSData *passwordData = [self createAndSetNewDatabasePassword];
NSData *saltData = [self createAndSetNewDatabaseSalt];
NSData *keySpecData = [self createAndSetNewDatabaseKeySpec];
OWSAssert(passwordData.length > 0);
OWSAssert(saltData.length == kSQLCipherSaltLength);
OWSAssert(keySpecData.length == kSQLCipherKeySpecLength);
return passwordData;
}
label:@"Database password"];
} }
- (NSData *)databaseSalt + (void)storeDatabaseCipherKeySpec:(NSData *)cipherKeySpecData
{ {
return [self loadMetadataOrClearDatabase:^(NSError **_Nullable errorHandle) { OWSAssert(cipherKeySpecData.length == kSQLCipherKeySpecLength);
return [OWSStorage tryToLoadDatabaseSalt:errorHandle];
}
createDataBlock:^{
NSData *passwordData = [self createAndSetNewDatabasePassword];
NSData *saltData = [self createAndSetNewDatabaseSalt];
NSData *keySpecData = [self createAndSetNewDatabaseKeySpec];
OWSAssert(passwordData.length > 0); [self storeKeyChainValue:cipherKeySpecData keychainKey:keychainDBCipherKeySpec];
OWSAssert(saltData.length == kSQLCipherSaltLength);
OWSAssert(keySpecData.length == kSQLCipherKeySpecLength);
return saltData;
}
label:@"Database salt"];
} }
- (NSData *)databaseKeySpec - (NSData *)databaseKeySpec
{ {
// Get or generate salt and cipherKeyData
return [self loadMetadataOrClearDatabase:^(NSError **_Nullable errorHandle) {
return [OWSStorage tryToLoadDatabaseKeySpec:errorHandle];
}
createDataBlock:^{
OWSFail(@"%@ It should never be necessary to generate a random key spec.", self.logTag);
NSData *passwordData = [self createAndSetNewDatabasePassword];
NSData *saltData = [self createAndSetNewDatabaseSalt];
NSData *keySpecData = [self createAndSetNewDatabaseKeySpec];
OWSAssert(passwordData.length > 0);
OWSAssert(saltData.length == kSQLCipherSaltLength);
OWSAssert(keySpecData.length == kSQLCipherKeySpecLength);
return keySpecData;
}
label:@"Database key spec"];
}
- (NSData *)loadMetadataOrClearDatabase:(LoadDatabaseMetadataBlock)loadDataBlock
createDataBlock:(CreateDatabaseMetadataBlock)createDataBlock
label:(NSString *)label
{
OWSAssert(loadDataBlock);
OWSAssert(createDataBlock);
NSError *error; NSError *error;
NSData *_Nullable data = loadDataBlock(&error); NSData *_Nullable data = [[self class] tryToLoadDatabaseCipherKeySpec:&error];
if (error) { if (error) {
// Because we use kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, // Because we use kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
@ -647,7 +568,7 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
// process that notification, so we should just terminate by throwing // process that notification, so we should just terminate by throwing
// an uncaught exception. // an uncaught exception.
NSString *errorDescription = NSString *errorDescription =
[NSString stringWithFormat:@"%@ inaccessible. No unlock since device restart? Error: %@", label, error]; [NSString stringWithFormat:@"CipherKeySpec inaccessible. No unlock since device restart? Error: %@", error];
if (CurrentAppContext().isMainApp) { if (CurrentAppContext().isMainApp) {
UIApplicationState applicationState = CurrentAppContext().mainApplicationState; UIApplicationState applicationState = CurrentAppContext().mainApplicationState;
errorDescription = errorDescription =
@ -661,11 +582,10 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
// TODO: Rather than crash here, we should detect the situation earlier // TODO: Rather than crash here, we should detect the situation earlier
// and exit gracefully - (in the app delegate?). See the ` // and exit gracefully - (in the app delegate?). See the `
// This is a last ditch effort to avoid blowing away the user's database. // This is a last ditch effort to avoid blowing away the user's database.
[self backgroundedAppDatabasePasswordInaccessibleWithErrorDescription:errorDescription]; [self raiseKeySpecInaccessibleExceptionWithErrorDescription:errorDescription];
} }
} else { } else {
[self backgroundedAppDatabasePasswordInaccessibleWithErrorDescription: [self raiseKeySpecInaccessibleExceptionWithErrorDescription:@"CipherKeySpec inaccessible; not main app."];
[NSString stringWithFormat:@"%@ inaccessible; not main app.", label]];
} }
// At this point, either this is a new install so there's no existing password to retrieve // At this point, either this is a new install so there's no existing password to retrieve
@ -681,47 +601,14 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
// Try to reset app by deleting database. // Try to reset app by deleting database.
[OWSStorage resetAllStorage]; [OWSStorage resetAllStorage];
data = createDataBlock(); data = [Randomness generateRandomBytes:(int)kSQLCipherKeySpecLength];
[[self class] storeDatabaseCipherKeySpec:data];
} }
return data; return data;
} }
- (NSData *)createAndSetNewDatabasePassword - (void)raiseKeySpecInaccessibleExceptionWithErrorDescription:(NSString *)errorDescription
{
NSData *password = [[[Randomness generateRandomBytes:kDatabasePasswordLength] base64EncodedString]
dataUsingEncoding:NSUTF8StringEncoding];
[OWSStorage storeDatabasePassword:password];
return password;
}
- (NSData *)createAndSetNewDatabaseSalt
{
NSData *saltData = [Randomness generateRandomBytes:(int)kSQLCipherSaltLength];
[OWSStorage storeDatabaseSalt:saltData];
return saltData;
}
- (NSData *)createAndSetNewDatabaseKeySpec
{
NSData *databasePassword = [self databasePassword];
OWSAssert(databasePassword.length > 0);
NSData *databaseSalt = [self databaseSalt];
OWSAssert(databaseSalt.length == kSQLCipherSaltLength);
NSData *keySpecData = [YapDatabaseCryptoUtils databaseKeySpecForPassword:databasePassword saltData:databaseSalt];
OWSAssert(keySpecData.length == kSQLCipherKeySpecLength);
[OWSStorage storeDatabaseKeySpec:keySpecData];
return keySpecData;
}
- (void)backgroundedAppDatabasePasswordInaccessibleWithErrorDescription:(NSString *)errorDescription
{ {
OWSAssert(CurrentAppContext().isMainApp && CurrentAppContext().isInBackground); OWSAssert(CurrentAppContext().isMainApp && CurrentAppContext().isInBackground);
@ -734,11 +621,10 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
OWSRaiseException(OWSStorageExceptionName_DatabasePasswordInaccessibleWhileBackgrounded, @"%@", errorDescription); OWSRaiseException(OWSStorageExceptionName_DatabasePasswordInaccessibleWhileBackgrounded, @"%@", errorDescription);
} }
+ (void)deletePasswordFromKeychain + (void)deleteDBKeys
{ {
[SAMKeychain deletePasswordForService:keychainService account:keychainDBPassAccount]; [SAMKeychain deletePasswordForService:keychainService account:keychainDBLegacyPassphrase];
[SAMKeychain deletePasswordForService:keychainService account:keychainDBSalt]; [SAMKeychain deletePasswordForService:keychainService account:keychainDBCipherKeySpec];
[SAMKeychain deletePasswordForService:keychainService account:keychainDBKeySpec];
} }
- (unsigned long long)databaseFileSize - (unsigned long long)databaseFileSize
@ -746,6 +632,14 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
return [OWSFileSystem fileSizeOfPath:self.databaseFilePath].unsignedLongLongValue; return [OWSFileSystem fileSizeOfPath:self.databaseFilePath].unsignedLongLongValue;
} }
+ (nullable NSData *)tryToLoadKeyChainValue:(NSString *)keychainKey errorHandle:(NSError **)errorHandle
{
OWSAssert(keychainKey.length > 0);
OWSAssert(errorHandle);
return [SAMKeychain passwordDataForService:keychainService account:keychainKey error:errorHandle];
}
+ (void)storeKeyChainValue:(NSData *)data keychainKey:(NSString *)keychainKey + (void)storeKeyChainValue:(NSData *)data keychainKey:(NSString *)keychainKey
{ {
OWSAssert(keychainKey.length > 0); OWSAssert(keychainKey.length > 0);
@ -758,8 +652,6 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
OWSFail(@"%@ Could not store database metadata", self.logTag); OWSFail(@"%@ Could not store database metadata", self.logTag);
OWSProdCritical([OWSAnalyticsEvents storageErrorCouldNotStoreKeychainValue]); OWSProdCritical([OWSAnalyticsEvents storageErrorCouldNotStoreKeychainValue]);
[OWSStorage deletePasswordFromKeychain];
// Sleep to give analytics events time to be delivered. // Sleep to give analytics events time to be delivered.
[NSThread sleepForTimeInterval:15.0f]; [NSThread sleepForTimeInterval:15.0f];
@ -770,25 +662,6 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void);
} }
} }
+ (void)storeDatabasePassword:(NSData *)passwordData
{
[self storeKeyChainValue:passwordData keychainKey:keychainDBPassAccount];
}
+ (void)storeDatabaseSalt:(NSData *)saltData
{
OWSAssert(saltData.length == kSQLCipherSaltLength);
[self storeKeyChainValue:saltData keychainKey:keychainDBSalt];
}
+ (void)storeDatabaseKeySpec:(NSData *)keySpecData
{
OWSAssert(keySpecData.length == kSQLCipherKeySpecLength);
[self storeKeyChainValue:keySpecData keychainKey:keychainDBKeySpec];
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

Loading…
Cancel
Save