Securing Your Core Data with Transformable Attributes

Posted by | Development | 3 Replies.

In order to store private data in an iOS Core Data database, there are several methods available for encryption, including:

However, neither of these options is sufficient for if you need to use multiple encryption keys, encrypt only certain attributes, or preserve decrypted data while the device is locked.

Encryption Transformer class

Instead, it’s fairly simple and straight-forward to perform lazy decryption on only certain database fields using the special Transformable Core Data attribute type. Transformable attributes are configured with an NSValueTransformer subclass that you write that specifies:

  • a method for converting one object into another
  • an optional method for reversing that process (and whether reversing is supported)
  • the class of an object after transformation

Here’s an example class that converts from a decrypted NSData object to an encrypted one. It relies on a key method to get the encryption key, and a couple NSData category methods that perform AES-256 encryption and decryption.

The key method might get the secret key from any number of places, such as a password requested at login and stored in the app delegate. I’ll touch on the NSData category methods that actually perform the encryption in a little bit.

This encryption class can then easily be subclassed to handle different data types, like strings, dates, numbers, etc. Here’s an example string encryption class that converts between NSString and NSData in order to use its parent class transformation methods.

Once these classes are set up, the Core Data model editor lets you assign an entity attribute type of Transformable and a name of the NSValueTransformer class, such as StringEncryptionTransformer.

Core Data model editor in Xcode showing an entity’s Transformable attribute.

Now, these attributes can be written to in code just like any other (e.g. clark.secretIdentity = @"superman"), but when they are persisted to the underlying SQLite database (or other sort of data store), the appropriate NSValueTransformer class will be called to encrypt the values before writing them to the data store.

Likewise, at the time a persisted object is read from the data store, the NSValueTransformer class will decrypt it. Encryption and decryption is thus lazy and only performed when an object is used or updated — no need to decrypt an entire file or database during app startup.

AES-256 encryption category

There are a number of example classes that make performing AES-256 encryption as simple as shown above, such as Jim Dovey’s NSData+CommonCrypto category and this unattributed snippet.  The better ones use encryption libraries provided by Apple, which may (or may not, IANAL) mean that you don’t need a CCATS form for app submission.

However, one major caveat to encrypting attributes individually is that patterns from short, repeated values will naturally rise.  If you are encrypting only a couple different possible values for an attribute (e.g. “superhero” and “evildoer”), simple encryption using the same key will result in only two encrypted values.  That is, for the key “top secret“, the value “Superman” will always encrypt to “?b64JzJ4aC0IhKaf7xeTWaglC6L/3VFxwA5XVrfDRntxebO4rFUSdNNrzfVFIU3y
” (Javascrypt is a handy online tool for encryption).

Enter the initialization vector (IV), which like the salt for a password helps hide patterns by randomizing encryption input.  The IV should be:

  1. a random number, for example the result of arc4random()
  2. different for every attribute, or at least every entity
  3. stored alongside the encrypted value, since the same IV used for encryption is necessary for decryption
  4. public, that is it need not (and should not) be encrypted

Unfortunately, many example code snippets that call CCCrypt (including one linked above) leave the initialization vector parameter as simply:

NULL /* initialization vector (optional) */,

Rather than follow suit, it’s simple to update our EncryptionTransformer to generate a IV and prepend it to the encrypted data:

Now, every time an attribute value is saved, its encrypted version includes an extra stage of randomization that prevents comparisons between different attributes encrypted with the same key.

Thus, with fairly minimal code that ensures resilient encryption, we have an easy way to store private information in Core Data attributes with as many different keys as necessary.