Skip to content
This repository has been archived by the owner on Dec 14, 2021. It is now read-only.

Commit

Permalink
Resolves database access issues if locally stored salt or key is inco…
Browse files Browse the repository at this point in the history
…rrect (#1202)

* Resolves database access issues if locally stored salt or key is incorrect.
  • Loading branch information
kaylagalway authored Apr 8, 2020
1 parent c363fb6 commit f501428
Showing 1 changed file with 57 additions and 9 deletions.
66 changes: 57 additions & 9 deletions Shared/Store/BaseDataStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,17 @@ extension BaseDataStore {
private func unlockInternal() {
guard let loginsStorage = loginsStorage,
let loginsKey = loginsKey,
let salt = salt else { return }
let salt = salt,
let loginsDatabasePath = loginsDatabasePath else { return }

do {
try loginsStorage.ensureUnlockedWithKeyAndSalt(key: loginsKey, salt: salt)
self.storageStateSubject.onNext(.Unlocked)
} catch let error as LoginsStoreError {
pushError(error)
// If we can not access database with current salt and key, need to delete local database and migrate to replacement salt
// This only deletes the local database file, does not delete the user's sync data
handleDatabaseAccessFailure(databasePath: loginsDatabasePath, encryptionKey: loginsKey)
} catch let error {
NSLog("Unknown error unlocking: \(error)")
}
Expand Down Expand Up @@ -468,13 +472,13 @@ extension BaseDataStore {
guard let loginsDatabasePath = loginsDatabasePath,
let loginsKey = loginsKey else { return nil }

let key = KeychainKey.salt.rawValue
if keychainWrapper.hasValue(forKey: key, withAccessibility: .afterFirstUnlock) {
return keychainWrapper.string(forKey: key, withAccessibility: .afterFirstUnlock)
let saltKey = KeychainKey.salt.rawValue
if keychainWrapper.hasValue(forKey: saltKey, withAccessibility: .afterFirstUnlock) {
return keychainWrapper.string(forKey: saltKey, withAccessibility: .afterFirstUnlock)
}

let val = setupPlaintextHeaderAndGetSalt(databasePath: loginsDatabasePath, encryptionKey: loginsKey)
keychainWrapper.set(val, forKey: key, withAccessibility: .afterFirstUnlock)
keychainWrapper.set(val, forKey: saltKey, withAccessibility: .afterFirstUnlock)
return val
}

Expand All @@ -487,19 +491,63 @@ extension BaseDataStore {
guard let db = loginsStorage as? LoginsStorage else {
return createRandomSalt()
}

do {
let salt = try db.getDbSaltForKey(key: encryptionKey)
try db.migrateToPlaintextHeader(key: encryptionKey, salt: salt)
return salt
} catch {
print("setupPlaintextHeaderAndGetSalt failed with error: \(error)")
self.dispatcher.dispatch(action: SentryAction(title: "setupPlaintextHeaderAndGetSalt failed", error: error, line: nil))
// the database exists. but we didn't store the salt?
return createRandomSalt()
}
}


// Closes database
// Deletes database file
// Creates new database and syncs
private func handleDatabaseAccessFailure(databasePath: String, encryptionKey: String) {
let saltKey = KeychainKey.salt.rawValue
if keychainWrapper.hasValue(forKey: saltKey, withAccessibility: .afterFirstUnlock) {
keychainWrapper.removeObject(forKey: saltKey)
}
do {
if let database = loginsStorage as? LoginsStorage {
database.close()
}
if FileManager.default.fileExists(atPath: databasePath) {
try FileManager.default.removeItem(atPath: databasePath)
loginsStorage = nil
try createNewDatabase()
} else {
loginsStorage = nil
try createNewDatabase()
}
} catch {
self.dispatcher.dispatch(action: SentryAction(title: "handleDatabaseAccessFailure failed", error: error, line: nil))
}
}

enum DatabaseError: Error {
case issueDeletingDatabase(description: String)
case issueCreatingDatabase(description: String)
}

private func createNewDatabase() throws {
guard let encryptionKey = loginsKey else { throw DatabaseError.issueCreatingDatabase(description: "logins database key is nil") }
do {
initializeLoginsStorage()
guard let newDatabase = loginsStorage as? LoginsStorage else { throw DatabaseError.issueCreatingDatabase(description: "initializing new database failed") }
let salt = createRandomSalt()
try newDatabase.ensureUnlockedWithKeyAndSalt(key: encryptionKey, salt: salt)
let saltKey = KeychainKey.salt.rawValue
keychainWrapper.set(salt, forKey: saltKey, withAccessibility: .afterFirstUnlock)
self.storageStateSubject.onNext(.Unlocked)
} catch {
self.dispatcher.dispatch(action: SentryAction(title: "handleDatabaseAccessFailure failed", error: error, line: nil))
throw DatabaseError.issueCreatingDatabase(description: "failed to unlock new database with key and salt:\(error)")
}
}

private func createRandomSalt() -> String {
return UUID().uuidString.replacingOccurrences(of: "-", with: "")
}
Expand Down

0 comments on commit f501428

Please sign in to comment.