FMDB and encryption - ios

I'm using FMDB to work with sqlite and I'd prefer to avoid a dependency on SQLCipher. How can I simply leverage the DataProtection capability built into iOS? Is this possible - the only requirement is to protect the data in the event of the phone being stolen.
If the phone is unlocked with a PIN, it's fine that the user could access the DB - it's their data.

Look for the line where you do databaseWithPath: (or initWithPath:), then add:
FMDatabase *db = [FMDatabase databaseWithPath:path];
NSDictionary *attributes = #{NSFileProtectionKey: NSFileProtectionCompleteUnlessOpen};
NSError *error;
BOOL success = [[NSFileManager defaultManager] setAttributes:attributes
ofItemAtPath:path
error:&error];
if (!success) {
NSLog(#"File protection failed: %#", error);
}
The possible Values for the NSFileProtectionKey key are:
NSFileProtectionNone:
The file has no special protections associated with it. It can be read from or written to at any time.
NSFileProtectionComplete:
The file is stored in an encrypted format on disk and cannot be read from or written to while the device is locked or booting.
NSFileProtectionCompleteUnlessOpen:
The file is stored in an encrypted format on disk. Files can be created while the device is locked, but once closed, cannot be opened again until the device is unlocked. If the file is opened when unlocked, you may continue to access the file normally, even if the user locks the device. There is a small performance penalty when the file is created and opened, though not when being written to or read from. This can be mitigated by changing the file protection to NSFileProtectionComplete when the device is unlocked.
NSFileProtectionCompleteUntilFirstUserAuthentication:
The file is stored in an encrypted format on disk and cannot be accessed until after the device has booted. After the user unlocks the device for the first time, your app can access the file and continue to access it even if the user subsequently locks the device.
The right type of protection may depend on the version of iOS (the last two are not available on iOS 4) and whether you use your database when the device is locked.

By far the easiest way is to turn on Data Protection for the entire app. Go to App IDs, click "Edit" and set "Sharing and Permissions" to "Complete Protection."
Update Xcode with your new app id information, and from there on, it'll be handled for your app automatically.

Related

How to test if Core Data SQLlite file is encrypted?

Core Data seems to be encrypted by default when device is locked but only before the first unlock.
From apple docs
For apps built for iOS 5.0 or later, persistent stores now store data
by default in an encrypted format on disk. The default protection
level prevents access to the data until after the user unlocks the
device for the first time.
So I set it up to get encrypted whenever the device is locked. The encryption settings for the SQLite file are set before returning the _persistentStoreCoordinator like so:
NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:storeURL.path error:&error]) {
//Handle error
}
return _persistentStoreCoordinator;
Code is from here.
I would like to test if the file is really encrypted.
What I did is lock the device and download the app container using Xcode->Window->Devices. However the file is not shown in the container. If I do the same when the device is unlocked I can then find it in the container. Why is that? More importantly can I test to see that the file gets encrypted when the phone is locked or it being missing is proof enough.
EDIT: A better setting up encryption for Core Data per this answer suggestion would be:
NSDictionary *storeOptions = #{NSPersistentStoreFileProtectionKey: NSFileProtectionComplete};
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:storeOptions error:&error]){
//handle error
}
What I did is lock the device and download the app container using
Xcode->Window->Devices. However the file is not shown in the
container. If I do the same when the device is unlocked I can then
find it in the container.
From what I understand this is how Apple encryption works. When your device is locked you don't see encrypted files in the device container, when it is open you do see them.

Detect iCloud account availibility / change using Key-Value store only?

I use iCloud Key-Value store, without any container (as I don't need documents).
So the app is not listed in Settings / iCloud / Storage / Manage Storage list, only in iCloud / iCloud Drive list, like this:
I really want to know somehow if it is turned ON or OFF, or available at all.
Seems ubiquityIdentityToken is always nil, and NSUbiquityIdentityDidChangeNotification never gets called.
I have a working prototype, modifiy a slider in simulator, it gets updated on the device, everything seems fine. Even though, if I ask for the identifiers (in the NSUbiquitousKeyValueStoreDidChangeExternallyNotification callback), they're always null:
NSURL *containerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
id token = [[NSFileManager defaultManager] ubiquityIdentityToken];
NSLog(#"containerURL: %#", containerURL);
NSLog(#"token: %#", token);
// containerURL: (null)
// token: (null)
When I turn off iCloud Drive for my app, it gets terminated in the background. No any notification gets called, nor at relaunch.
Should I simply setup a Document container I never use?
Having iCloud Documents turned on, I have the token (app entitlements get some new values as well).
But the ubiquityIdentityToken now returns value even if I turn iCloud Drive OFF (!) in device settings for the given app. It does not pulls updates from the Key-Value store, still the token is set.
Only URLForUbiquityContainerIdentifier returns nil when turned OFF, and the URL when turned ON.

How can I use file encryption when calling parent application from Watch app?

I am calling a parent app on my iPhone from an Apple Watch app using openParentApplication and handleWatchKitExtensionRequest. In the main app, I use CoreData with the following options for addPersistentStoreWithType:
NSDictionary *options = #{
NSMigratePersistentStoresAutomaticallyOption : #YES, //
NSInferMappingModelAutomaticallyOption : #YES, //
NSSQLitePragmasOption : #{#"journal_mode" : #"DELETE"}, //
NSPersistentStoreFileProtectionKey : NSFileProtectionCompleteUnlessOpen
};
This caused an exception:
This NSPersistentStoreCoordinator has no persistent stores (device
locked). It cannot perform a save operation.
Does this mean that I can neither use NSFileProtectionCompleteUnlessOpen nor NSFileProtectionComplete?
Do I have to use NSFileProtectionNone or NSFileProtectionCompleteUntilFirstUserAuthentication?
I would like to know a way to protect my data by using NSFileProtectionCompleteUnlessOpen and still be able to access the data when my Watch app uses openParentApplication.
Possible ways to deal with the problem (but not a real solution)
Have two files (e.g., SQL data bases), where one is encrypted and the other one is not. The latter one would store only the data required by the Watch app.
NSFileProtectionCompleteUntilFirstUserAuthentication seems to be the recommended way for me. It makes sure the user has to unlock the device at least once since the last boot.
This problem was introduced with iOS 7 and background refresh. It's to prevent physical forensic analysis to read your unencrypted data.
Additionaly information from https://security.stackexchange.com/questions/57588/iphone-ios-7-encryption-at-lock-screen:
NSFileProtectionNone: file can be accessed any time, even if device is locked;
NSFileProtectionComplete: file can accessed only when device is unlocked (note there's ~10 seconds grace period after device is locked during which files are still accessible);
NSFileProtectionCompleteUnlessOpen: file can be created while device is locked, but once closed, can only be accessed when device is unlocked;
NSFileProtectionCompleteUntilFirstUserAuthentication: file can be accessed only if device has been unlocked at least once since boot.
The guys from Gilt also explained a lot about this behaviour here: http://tech.gilt.com/post/67708037571/sleuthing-and-solving-the-user-logout-bug-on-ios
Another idea which just came into my mind is to use an app group container. See the question here: WatchKit SDK not retrieving data from NSUserDefaults This way it should not only share NSUserDefaults but also the same keychain. This should work the same way to iOS Apps share the same keychain.

How to implement iOS Data Protection on iOS 8 app by XCode 6.1 project->targets->Capabilities->Data Protection?

I found some legacy questions with dinosaur answers about implement Data Protection. I hear there are differents since iOS7 or iOS8. Do we still need coding after turn on the Data Protection capability in XCode 6?
Is this necessary?
[[NSFileManager defaultManager] createFileAtPath:[self filePath]
contents:[#"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding]
attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete
forKey:NSFileProtectionKey]];
[[[NSFileManager defaultManager] attributesOfItemAtPath:[self filePath]
error:NULL]
valueForKey:NSFileProtectionKey];
And, how can we protect Core Data with this Data Protection?
OR, is this an one button solution for protect data in phone lock state?
According to https://www.apple.com/privacy/docs/iOS_Security_Guide_Oct_2014.pdf for NSFileProtectionComplete,
The class key is protected with a key derived from the user passcode
and the device UID. Shortly after the user locks a device (10 seconds,
if the Require Password setting is Immediately), the decrypted class
key is discarded, rendering all data in this class inaccessible until
the user enters the passcode again or unlocks the device using Touch
ID.
So, when you create your persistent store using:
- (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType configuration:(NSString *)configuration URL:(NSURL *)storeURL options:(NSDictionary *)options error:(NSError **)error;
pass in your file protection options as part of the options dictionary.

How do I check whether I can access a file saved with NSFileProtectionKey = NSFileProtectionCompleteUntilFirstUserAuthentication

I would like the determine if I have access to files that have been saved with the attribute NSFileProtectionKey = NSFileProtectionCompleteUntilFirstUserAuthentication.
I have tried [UIApplication sharedApplication].protectedDataAvailable, however from my tests it will return NO whenever the device is locked (if a pin code is set) even if the user has unlocked the device at least once since last starting the phone.
It says quite clearly in the docs: The value of this property is NO if data protection is enabled and the device is currently locked, and in this case files that were assigned the NSFileProtectionComplete or NSFileProtectionCompleteUnlessOpen protection key cannot be read or written by your app. i.e. this is not the correct property.
You need use one of the multi-tasking keys that causes the launch the app on boot - e.g. the voip key for UIBackgroundModes.
Mind you, you're pretty much testing the OS at that point - if you set the keys appropriately when creating the files it should work as advertised. If it doesn't then log a radar.

Resources