Skip to content
Robbie Hanson edited this page May 5, 2016 · 11 revisions

YapDatabase supports two different forms of encryption:

  • whole-database encryption utilizing SQLCipher
  • object encryption via the serializer & deserializer

SQLCipher
Serializer / Deserializer
Performance Concerns

## SQLCipher

SQLCipher is a SQLite extension that transparently encrypts the entire database, and is available under a BSD-style license. SQLCipher support has been added as a Cocoapod subspec. Simply change your Podfile:

pod 'YapDatabase/SQLCipher'

If you aren't using the SQLCipher subspec, your project won't compile the encryption configuration options to prevent the case of accidentally trying to use encryption when support is not available.

Once the project is configured to use SQLCipher, you then need to tell YapDatabase the passphrase. You do this by setting the cipherKeyBlock of YapDatabaseOptions.

YapDatabaseOptions *options = [[YapDatabaseOptions alloc] init];
options.corruptAction = YapDatabaseCorruptAction_Fail;
options.cipherKeyBlock = ^ NSData *(void){
    // You can also do things like fetch from the keychain in here.
    return [@"super secure passphrase" dataUsingEncoding:NSUTF8StringEncoding];
    
    // Note: The return type is NSData, and does NOT have to be a string in UTF-8.
    // It can be any kind of blob of data, including randomly generated bytes.
};
    
self.database = [[YapDatabase alloc] initWithPath:databasePath options:options];

The cipherKeyBlock helps prevent storing the credentials in memory any longer than necessary. This block will be executed on database setup, and when new connections are made to the database.

## Serializer / Deserializer

An alternative to using SQLCipher is to utilize the serializer & deserializer to encrypt objects on-the-fly. This technique may be preferable if you only need to encrypt a few key objects. It may possibly also work for you if your database needs are minimal and specific.

However it is important to understand exactly what is and what is NOT encrypted:

  • The object itself will be encrypted, and will be stored as a blob of encrypted data.
    And you have full control over the encryption technique that is used.

  • The collection & key of every object will still be in plaintext.
    This may be fine if you're only using UUID's, for example.

  • Views are kinda sorta encrypted, depending on your situation.
    A YapDatabaseView actually stores an array of int64_t rowid's. These rowids are the primary keys for the main database table, which means they can be associated with the collection & key (which, again, are in plaintext). So whether or not this is a concern is largely application specific.

  • Secondary Indexes would not be encrypted.
    So anything you explicitly store via YapDatabaseSecondaryIndex would be in plaintext.

  • Full Text Search would not be encrypted.
    So any string you pull out for YapDatabaseFullTextSearch would be in plaintext.

All these caveats is why we stress that the technique is only applicable to very specific use cases. (When in doubt, go with SQLCipher.) But if want to use this technique, then setup is quite easy:

YapDatabaseSerializer serializer = ^(NSString *collection, NSString *key, id object){

    // However you normally do serialization (this example uses NSCoding)
    NSData *clearData = [NSKeyedArchiver archivedDataWithRootObject:object];

    // And then encrypt
    NSData *encryptedData = /* add encryption here */
    return encryptedData;
};
	
YapDatabaseDeserializer deserializer = ^(NSString *collection, NSString *key, NSData *encryptedData){

    // Decrypt
    NSData *clearData = /* add decryption here */
    
    // And then however you normally do deserialization (this examples uses NSCoding)
    id object = [NSKeyedUnarchiver unarchiveObjectWithData:clearData];
    return object;
};

That's all there is to it!

If you only want to encrypt certain items, then you can check the collection & key parameters to determine if encryption is needed.

## Performance Concerns

Adding encryption will obviously add overhead due to (at a minimum) the CPU usage required to run the encryption / decryption task(s). However, YapDatabase's architecture can really shine here, and can reduce the overhead considerably. More on this in a moment.

SQLCipher works by encrypting/decrypting sqlite pages on-the-fly.

Quick side note: Basically, sqlite works by breaking up all data into "pages". So if you were to update the value of a column in a row, then sqlite would write a new page that replaces the previous page that contained the old value. Other connections in the middle of a transaction (or frozen using beginLongLivedReadTransaction) will refer to the old page until they move to the newer commit. And at that point, the old page is "deleted".

So that means, when using SQLCipher, anytime you're reading or writing to the sqlite database file, you'll have the encryption overhead. (Although this is probably pretty obvious since it's a "whole database encryption" technique.)

With the serializer/deserializer technique, you'll only experience the encryption overhead when you're reading or writing encrypted objects. (You might not choose to encrypt all objects. Perhaps only certain collections...)

The good news is that YapDatabase has an object cache! What does this mean for encryption? It means that YapDatabase is able to cache objects in memory, after they've been read from disk, after they've been decrypted, and after they've been deserialized. So if you follow the performance recommendations (1, 2), and you configure proper cache sizes, then you can drastically cut down on the number of times YapDatabase is forced to hit the disk. And if you implement an object sharing policy, you can effectively kick that up another notch.

So while SQLCipher claims as little as 5-15% overhead for encryption, you can reduce that overhead by pairing it with YapDatabase.

Clone this wiki locally