1

I am writing an OS X app that should maintain a custom Keychain, I am trying to use the Security framework's API to create the Keychain, however, I can't seem to get it to compile under Swift.

Here's what I have, assume that path contains a path to a potentially existing Keychain:

let pathName = (path as NSString).UTF8String
var keychain: Unmanaged<SecKeychain>?

var status = withUnsafeMutablePointer(&keychain) { pointer in
    SecKeychainOpen(pathName, pointer)
}

if status != errSecSuccess {
    status = withUnsafeMutablePointer(&keychain) { pointer in
        SecKeychainCreate(pathName, UInt32(0), nil, false, nil, pointer)
    }
}

The compiler is complaining about the types in the SecKeychainCreate call, however, I fail to understand what am I doing wrong.

Cannot invoke 'withUnsafeMutablePointer' with an argument list of type '(inout Unmanaged<SecKeychain>?, (_) -> _)'

If I modify the second closure slightly, I get this compiler error:

Cannot invoke 'SecKeychainCreate' with an argument list of type '(UnsafePointer<Int8>, UInt32, nil, Bool, nil, (UnsafeMutablePointer<Unmanaged<SecKeychain>?>))'

I appreciate all suggestions.

Henri Normak
  • 4,695
  • 2
  • 23
  • 33

1 Answers1

5

The promptUser parameter of SecKeychainCreate() has the type Boolean, which is a "Mac OS historic type" and an alias to UInt8, so it is different from the Swift Bool in Swift 1.2. (Compare Type 'Boolean' does not conform to protocol 'BooleanType' for a similar issue.) This means that you have to pass Boolean(0) instead of false:

SecKeychainCreate(pathName, UInt32(0), nil, Boolean(0), nil, pointer)

Additional remarks:

  • withUnsafeMutablePointer() is not needed, you can pass &keychain to the keychain functions.
  • (path as NSString).UTF8String is not needed, you can pass a Swift string to a C function expecting a const char * parameter, compare String value to UnsafePointer<UInt8> function parameter behavior.
  • Passing nil as password to SecKeychainCreate() is only allowed if promptUser is TRUE, otherwise it causes a "parameter error (-50)".
  • SecKeychainOpen() succeeds even if the keychain file does not exists. According to the documentation, you have to check SecKeychainGetStatus(). Alternatively, you can try to create the keychain file first, as for example in Open Local Items Keychain?.

Together:

let path = "/path/to/my.keychain"
var keychain: Unmanaged<SecKeychain>?

var status = SecKeychainCreate(path, 0, "", Boolean(0), nil, &keychain)
if status == OSStatus(errSecDuplicateKeychain) {
    status = SecKeychainOpen(path, &keychain)
}

As of Swift 2 / Xcode 7 beta 5, the Mac type Boolean is mapped to Swift as Bool, and the key chain functions do no longer return unmanaged objects:

let path = "/path/to/my.keychain"
var keychain: SecKeychain?

var status = SecKeychainCreate(path, 0, "", false, nil, &keychain)
if status == OSStatus(errSecDuplicateKeychain) {
    status = SecKeychainOpen(path, &keychain)
}
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thanks for the in-depth analysis, as I suspected it was something easy to miss. I wish the compiler would've been more specific about the error... – Henri Normak Aug 14 '15 at 08:57
  • @HenriNormak: You are welcome. – And yes, wrong argument types can be difficult to detect, and even more so inside closures. There is room for improvement for the Swift compiler! Sometimes it helps to use the auto-completion for methods (or jump to definition) and inspect each parameter. – Martin R Aug 14 '15 at 09:03
  • You mention the documentation describes how you must use `SecKeychainGetStatus()` since `SecKeychainOpen()` always succeeds. Where did you find this in the docs? I discovered this to be true empirically, but couldn't find any official reference to this behavior. – drootang Feb 07 '17 at 14:54
  • @drootang: If you command-click on `SecKeychainOpen` in the Xcode source editor to jump to the API documentation then you'll find the text *"This keychain might not currently exist, use SecKeychainGetStatus if you want to confirm the existence of this keychain."* – I don't know why that information is missing in the online docs. – Martin R Feb 07 '17 at 15:03
  • @MartinR: Thanks. I suppose its a good lesson that I should always check the header comments in addition to the documentation – drootang Feb 07 '17 at 16:20