Search code examples
iosobjective-ccore-datansmanagedobjectnsvaluetransformer

Reference NSManagedObject entity from inside NSValueTransformer


I'm using NSValueTranformer to encrypt certain Core Data attributes. This all works fine, except I need to be able to use a different encryption key depending on the NSManagedObject. Is there anyway I can access this entity from within my transformer class?

The use case is I have multiple users with different passwords that can access different NSManagedObject entities. If I use the same encryption key for all of the objects, someone could just reassign who owns them in the SQL db and they would still decrypt.

Any ideas on the best way to go about this?

Edit: I should mention I'm doing this in iOS.


Solution

  • Third times the charm? Let me see if I can address your only-transform-when-going-to-disk requirement. Think of this as a hybrid of the other two approaches.

    @interface UserSession : NSObject
    
    + (UserSession*)currentSession;
    + (void)setCurrentSession: (UserSession*)session;
    - (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key;
    
    @property (nonatomic, readonly) NSString* userName;
    @property (nonatomic, readonly) NSData* encryptionKey;
    
    @end
    
    @implementation UserSession
    
    static UserSession* gCurrentSession = nil;
    
    + (UserSession*)currentSession
    {
        @synchronized(self)
        {
            return gCurrentSession;
        }
    }
    
    + (void)setCurrentSession: (UserSession*)userSession
    {
        @synchronized(self)
        {
            gCurrentSession = userSession;
        }
    }
    
    - (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key
    {
        if (self = [super init])
        {
            _userName = [username copy];
            _encryptionKey = [key copy];
        }
        return self;
    }
    
    - (void)dealloc
    {
        _userName = nil;
        _encryptionKey = nil;
    }
    
    @end
    
    @interface EncryptingValueTransformer : NSValueTransformer
    @end
    
    @implementation EncryptingValueTransformer
    
    - (id)transformedValue:(id)value
    {    
        UserSession* session = [UserSession currentSession];
        NSAssert(session, @"No user session! Can't decrypt!");
    
        NSData* key = session.encryptionKey;
        NSData* decryptedData = Decrypt(value, key);
        return decryptedData;
    }
    
    - (id)reverseTransformedValue:(id)value
    {
        UserSession* session = [UserSession currentSession];
        NSAssert(session, @"No user session! Can't encrypt!");
    
        NSData* key = session.encryptionKey;
        NSData* encryptedData = Encrypt(value, key);
        return encryptedData;
    }
    
    @end
    

    The only tricky part here is that you have to be sure that the current UserSession is set up before you create the managed object context and isn't changed until after the context is saved and deallocated.

    Hope this helps.