My first version of this (I may throw it and dig in to the replicator as you 
suggested). I can live with the db contents being unencrypted since, as you 
point out, it is encrypted in the Documents directory. Here is what I have so 
far and it seems to work with the few model classes that I have updated so far.

In my subclass of CBLModel:

// encrypted model subclasses implement init to set isEncrypted to YES
- (id) init {
  self = [super init];
  if(self) {
    self.decryptedProperties = [NSMutableDictionary dictionary];
    self.isEncrypted = NO;
  }
  return self;
}

- (void) didLoadFromDocument {
  // decrypt document
  NSString *encryptionKey = [OTSKeychainHelper 
keychainStringFromMatchingIdentifier:PROVIDER_ENCRYPTION_KEY];
  self.decryptedProperties = [OTSCrypter decrypt:self.document.properties 
encryptionKey:encryptionKey];
}

- (BOOL) save:(NSError *__autoreleasing *)outError {
  // encrypt doument
  if(self.isEncrypted) {
    NSString *encryptionKey = [OTSKeychainHelper 
keychainStringFromMatchingIdentifier:PROVIDER_ENCRYPTION_KEY];
    // encryption result is a dictionary: @{@"iv":iv, @"data":data} where iv is 
the initialization vector and data is a base 64 encoded string of encrypted data
    NSDictionary *encryptionResult = [OTSCrypter 
encrypt:self.decryptedProperties encryptionKey:encryptionKey];
    NSMutableDictionary * newProperties = [@{} mutableCopy];
    [newProperties setObject:[self.document.properties objectForKey:@"_id"] 
forKey:@"_id"];
    [newProperties setObject:[self.document.properties objectForKey:@"_rev"] 
forKey:@"_rev"];
    if(encryptionResult != nil) {
      [newProperties setObject:[self.document.properties objectForKey:@"type"] 
forKey:@"type"];
      [newProperties setObject:[encryptionResult objectForKey:@"iv"] 
forKey:@"iv"];
      [newProperties setObject:[encryptionResult objectForKey:@"data"] 
forKey:@"data"];
    }
    else {
      [newProperties setObject:@YES forKey:@"_deleted"];
      [newProperties setObject:[self.document.properties objectForKey:@"type"] 
forKey:@"type"];
    }
    [self.document putProperties:newProperties error:outError];
  }
  else {
    [super save:outError];
  }
  return !outError;
}

All models that are encrypted have to implement accessor methods to get and set 
the properties in self.decryptedProperties. Not elegant, so I'm still looking 
for a better way. Maybe the changes to the replicator as you mentioned. The 
real downside is that there are two copies of all of the properties in memory 
when the model object gets loaded: one dictionary in self.document.properties 
and the other in self.decryptedProperties.

On Feb 13, 2014, at 2:20 PM, Jens Alfke <[email protected]> wrote:

> 
> On Feb 13, 2014, at 12:57 PM, Alan McKean <[email protected]> wrote:
> 
>> I would rather not put the decryption/encryption in the replicator because I 
>> like having the local database contents encrypted.
> 
> The database is already encrypted, like all other document files on iOS. (And 
> yes, the key for that encryption is in the keychain. But so is your AES key, 
> so it's no less secure.)
> 
> But if you want to leave your docs AES-encrypted in the db, you'll have to do 
> the hacking at a lower level, down in the CBLDatabase+*.m files and in 
> CBLView+Internal.m -- that's the only way to get map-reduce views to work. 
> Any code that reads and writes the 'json' column of the 'revs' table will 
> need to encrypt it on save and decrypt it on read. (And actually the same 
> goes for the 'key' and 'value' columns of the 'maps' table, if those could 
> contain sensitive data ... which is a problem because the 'key' table has an 
> index on it so it has to be sortable, which doesn't combine well with 
> encryption.)
> 
>> Further, not all documents are encrypted, so it would have to do it 
>> selectively.
> 
> You could add a property to the doc that indicates that it should be 
> encrypted.
> 
>> BTW, I don't understand your comment about it not being safe. I am loading 
>> the document, modifying it with putProperties:error: and (after that) 
>> creating the model from it. Instantiating the model does not modify the 
>> document. It is already modified by the time I instantiate the model.
> 
> It was your use of 'override' that threw me -- I was at first reading the 
> method as an override of +modelForDocument:, whose semantics are read-only. 
> But you gave yours a different name so it can have different behavior.
> 
> But, now you've created new unencrypted revisions of the encrypted documents. 
> If you have a push replication, it's going to upload those unencrypted 
> revisions of every doc you've accessed (not just the ones you've 
> intentionally changed) back to the server. That doesn't seem good.
> 
> --Jens
> 
> -- 
> You received this message because you are subscribed to a topic in the Google 
> Groups "Couchbase Mobile" group.
> To unsubscribe from this topic, visit 
> https://groups.google.com/d/topic/mobile-couchbase/jFADxJ8cV6Y/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to 
> [email protected].
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/mobile-couchbase/3FED640E-3C35-43FF-BB62-5B97BAE9713F%40couchbase.com.
> For more options, visit https://groups.google.com/groups/opt_out.

-- 
You received this message because you are subscribed to the Google Groups 
"Couchbase Mobile" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/mobile-couchbase/1646AC56-BD5F-43E5-8A96-8F700E55D213%40me.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to