realm-swift: Seeing "Realm file decryption failed" errors
Background Our iOS app is live with over one hundred thousand users. Of those, we have an issue happening with approximately 80 users. We’re unable to reproduce this locally, but this is what we’re able to piece together from Crashlytics.
Details We store sensitive customer data in an encrypted Realm. To accomplish this, we have extended Realm with a static function that returns our encrypted Realm, or throws an error if it fails.
For 99% of users, this works fine. For less than 1%, we’re seeing the “Realm file decryption failed” error. From investigating, it seems that this error is caused by trying to access a Realm with either the wrong key, or by trying to decrypt an unencrypted Realm.
Right now, with how we’re generating the key, I’m inclined to think that that is not the issue.
Code Sample We have a Realm extension with the following:
static func encryptedRealm() throws -> Realm {
// Fetches key from Keychain if one exists, otherwise creates new Data key, stores in Keychain, returns it here.
let key: Data = getEncryptionKey()
// Create default config
let config = Realm.Configuration(encryptionKey: key)
// Return Realm for config
do {
let realm = try Realm(configuration: config)
return realm
} catch let error as NSError {
throw error
}
}
The first place Realm is hit in our app is via our User Settings manager. Simple version:
class SettingsManager {
/// Shared instance
static let shared = SettingsManager()
/// Realm instance
var realm: Realm {
do {
return try Realm.encryptedRealm()
} catch let error as NSError {
// Some logging to Crashlytics happening here
fatalError()
}
}
// Other methods/properties here as convenience accesses to user data
}
Expected results While we accounted for the fact that something could go wrong, I was expecting to see errors pertaining to file I/O, disk space, etc.
Actual results
For less than 1% of our users, we’re seeing “Realm file decryption failed”. As all hits to Realm are happening through the Realm.encryptedRealm() function, I don’t know how a non-encrypted Realm could be created. So I’m at a loss as to what might be going on.
The Crashlytics trace has our realm property getting above, followed by:
libswiftCore.dylib
__hidden#23281_ line 134
specialized _assertionFailure(StaticString, String, file : StaticString, line : UInt, flags : UInt32) -> Never
Looking up errors for that has shown me potentially issues with Realm, background threads, and the need to use autoreleasepool. But the error I’m catching before the fatalError call seems to imply otherwise.
Any guidance would be welcome at this point. Thanks.
Version of Realm 2.10.2 (Unable to upgrade at this time)
Xcode version: 9.2
iOS/OSX version: Bug has been reported on iOS 10 and 11
Dependency manager + version: CocoaPods 1.4.0
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 6
- Comments: 17 (2 by maintainers)
We just realized what was the problem and I thought it might be helpful for you to know what we did in case it helps to fix your issue as well.
Our scenario is the following one:
We were experiencing a random issue where the encription key failed to open the database. There was no usage pattern to reproduce it and it happened also in Production for some users.
What we did is:
After all of those changes, there were still a few users having problems with database access. We realized there was a situation where the user was using iCloud to make backups of their phone but wasn’t using iCloud Keychain. He/she changed or made a factory reset to his/her phone and restored the iCloud backup. Once the app was being started, Keychain returned nil for com.ourcompany.ourappname.databaseEncriptionKeyProd key and database failed to open (obviously). We added a specific control for this situation and created an empty database with a brand new encription key saved to the new Keychain.
Hope it helps.
One thing I notice about your
getKey()function is that it assumes that the only two possible results are “successfully read key” and “key not found”. Perhaps in some rare situations the key is in the keychain but not readable (due to something like the device being locked?), in which case your app will instead generate a new key and then fail to open the existing Realm.Hey @JoshHrach @Spenders @corteggo @bmunkholm @jguerinet I think the issue might be about waking from a suspended state and Realm not starting fast enough to reply to the request to open the database file. We turned off all iOS background processing in the XCode project settings and it solved our main issue. We also think we’ve found a situation when we call the keychain for credentials for an encrypted database and it’s returning the wrong credentials (maybe older ones), so we’re now in the process of seeing if we can get multiple credentials and then cycle through each one to ensure we find the one that’s valid. I hope this is helpful to you!
@nwainwright Thanks for the update. I don’t believe this is the case for me, as this is happening on the main thread upon opening the completely-terminated app. I also don’t know how the wrong credentials could have affected this, as we have only ever used one. Regardless, thanks for the theories and good luck!
@tgoyne This was a case I had originally considered as well, except that in that situation this would crash:
Since we’re asking to add and not to update, if the key already existed in any capacity the result would be
errSecDuplicateItemand crash the app in itself (I tested this). And if for some reason it came back aserrSecSuccess, the original key would have been lost since it seemingly no longer exists, and there wouldn’t be much I could anyway. I don’t believe this to be the case as I don’t see where the key would be deleted in the first place, and there are clearly other people facing the same bug. Thank you for your suggestion though!@Spenders No luck on our end. I ended up doing an early check of the Realm (within the AppDelegate) and am deleting the database if it hits this. It’s cut down on crashes in our app, but it hasn’t addressed the ultimate problem.