Handling Keychain data with NSString

I've been doing some work on ConnectionKit's SFTP public key authentication support. One tricky aspect is that private key files may be encrypted using a passphrase as protection against someone snooping around your Mac's filesystem. Quite often, the passphrase is already present in the Keychain, for use by SSH Agent. If so, it'd be nice to re-use that and simply prompt the user for permission, rather than make them type in the passphrase for a second time.

Getting a password out of the keychain is fairly straightforward. You can do it in a single shot using SecKeychainFindGenericPassword(), or alternatively grab just a SecKeychainItemRef and later call SecKeychainItemCopyContent() to retrieve the password.

Either way you end up with a bit of raw password data, presumably a UTF8-encoded string. But let's say that you want to wrap that up as an NSString for passing onto another bit of your app. Well, it's fairly straightforward to create an NSString direct from a lump of data, but does have one nasty omission:

You've handed over control of this sensitive data entirely to Foundation's whims.

The keychain API expects you to free any password data with the SecKeychainItemFreeContent() function. My understanding is this takes pains to ensure the data is properly cleared out from memory before returning it to the system for re-use. i.e. to ensure no-one can snoop around your app's memory and discover the password sitting there for any longer than is necessary.

It seems a fair bet that NSString won't be going to such lengths in its -dealloc routine.

So ideally what we want is a custom NSString that is backed by keychain-provided data, taking care to clean it up properly when no longer needed. We'll have to presume that any APIs receiving the NSString are smart enough to know it's sensitive data and handle any copies of the data properly.

How to achieve this? Perhaps a subclass? Fortunately not! A little digging reveals that the CFString API offers just such a function. Here's a nice example:

CFStringCreateWithBytesNoCopy() allows us to create a string backed by a custom, non-copied bit of data. Most importantly we can specify a custom CFAllocator to use for deallocation once the string is no longer in use. It's pretty straightforward to create such an allocator, particularly since it's only needed for a single callback function.

This was my first time playing with CFAllocators. I rather like how the API is implemented in terms of itself. i.e. a CFAllocator is a full-blown object, managed using the CFAllocator of your choice. Passing in NULL for this suffices as the allocator itself doesn't require anything outside of the usual memory management.

While writing this blog post, I remembered that Cocoa Is My Girlfriend had covered a similar topic recently with Extending NSData And (Not) Overriding Dealloc. Tom's solution there is interesting, and so is the possibility of subclassing NSData directly. But I think an even better solution would be similar to mine above, using CFDataCreateWithBytesNoCopy() with a custom deallocator.

© Mike Abdullah 2007-2015