Valet: A Better Place to Put Your Keys
Store your secrets securely without ever reading Apple’s SecItem.h
Written by Dan Federman and Eric Muller.
Heads up, we’ve moved! If you’d like to continue keeping up with the latest technical content from Square please visit us at our new home https://developer.squareup.com/blog
Keychain is terrifying
If you’ve ever had to pore over SecItem.h or interact with the Keychain, you’re well aware that it’s a terrifying API. You pass in a dictionary and then you get an error back that lets you know that at least one of your parameters was wrong. Want to know which one? No dice. Once you finally validate those dictionary parameters, there are still no set semantics. The only arrows you’ve got in the quiver are add and update, but adding a key/value pair will fail if the key already exists in Keychain. Worse yet, there are subtle and undocumented differences between the key/value pairs you should pass into SecItem* on iOS and Mac.
The pain doesn’t end there. Keychain is a loose cannon in multithreaded environments, since there’s nothing in place to guarantee atomicity of Keychain operations. Even if you do everything right, you might be bitten by a SecItemUpdate call that erroneously tells you it has succeeded updating that metadata you passed in — when, in fact, it only updated a subset of the elements it should have updated.
… But you need it
If you’re storing your secrets in a storage mechanism other than Keychain, you’re doing it wrong. There is no other secure place to store your data. There are NSUserDefaults, plists, etc., but these are risibly poor places to store anything requiring any level of security — just ask anyone who has used iExplorer. And if you’re thinking that you’re fine because you’ve rolled your own encryption mechanism, you’re breaking a fundamental rule of crypto: don’t write your own crypto.
So let us take care of your keys
Valet has an API inspired by NSMapTable that uses Keychain under the hood. Every Cocoa API you are used to has set/get semantics, and so does Valet. Valet ensures atomic reads and writes into the keychain; you’ll never get a failure, because you’re interacting with Valet on multiple threads. If you haven’t touched Keychain before, we promise you’ll never have to read SecItem.h, and you’ll have those tokens migrated out of your plists and into a Valet instance in no time.
If you’ve spent time learning the ins and outs of the Keychain, we’re sorry. But on the bright side, Valet’s API is already in your lexicon. Oh, and we’ve got a one-line method to import your old keychain items into Valet. Once you migrate to Valet, you’ll never need to write another switch for errSec* handling again. Valet calls will never fail as long as -canAccessKeychain returns YES.
Even better, you never have to worry about stomping or deleting a secret you didn’t mean to — every Valet lives in a sandbox. If two Valets can read or write to the same space, they’ll pass both -isEqual: and == checks. Each Valet is sandboxed based on four core attributes: initializer, identifier, accessibility, and class. This sandbox means that migrating from one Valet that is accessible only when the phone is unlocked to another Valet that is accessible after first unlock is guaranteed to work. (You’re not going to get bitten by a bad SecItemUpdate call.) When you migrate these items between your Valet’s, the items are literally copied from one Valet to the other rather than updating them in place (which we estimate has a silent failure rate of about 0.5%).
Best of all, Valet makes it easy to harness the full power of Keychain. Want to share secrets across multiple applications you are writing? Just pass your shared access group identifier into the shared access group initializer. Want to sync your secrets to other devices via iCloud? We’ve got a Valet for that. Want to store your secrets on the Secure Enclave, requiring Touch ID or the device’s passcode to retrieve each secret? We’ve got a Valet for that too.
So what are you waiting for? Hand over the keys and get back to doing the work you actually want to do.
This post is part of Square’s “Week of iOS” series.