Using FourCharCode / SecItemAttr constants from Swift

I play with some Mac OS X Keychain / Security APIs using Swift. So far I have been able to successfully receive both the username and password of an existing element keychain (by setting both kSecReturnAttributesand kSecReturnAttributeson true).

This code has been compiled from several sources, including StackOverflow and Apple Dev Forums:

//
//  main.swift
//  go-osxkeychain
//

import Foundation

// Create an HTTPS Keychain item w/ this server name before running this code
let serverName = "api.myserver.com"

public func getPassword(serverName: NSString) -> NSString? {
    // Instantiate a new default keychain query.
    // Tell the query to return the password data, as well as item attributes.
    // Limit our results to one item.
    var keychainQuery = NSMutableDictionary(
        objects: [
            kSecClassInternetPassword,
            serverName,
            kSecAttrProtocolHTTPS,
            true,
            true,
            kSecMatchLimitOne
        ], forKeys: [
            kSecClass,
            kSecAttrServer,
            kSecAttrProtocol,
            kSecReturnAttributes,
            kSecReturnData,
            kSecMatchLimit
        ])

    var dataTypeRef :Unmanaged<AnyObject>?

    // Search for the keychain items
    let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)

    if Int(errSecSuccess) != Int(status) {
        println("error finding keychain item")
        return "ERROR"
    }

    let opaque = dataTypeRef?.toOpaque()

    if let op = opaque? {
        let retrievedDictData = Unmanaged<NSDictionary>.fromOpaque(op).takeUnretainedValue()
        let foundDict = NSDictionary(dictionary: retrievedDictData)

        // let account = foundDict[kSecAccountItemAttr as NSNumber] as String // BROKEN
        let account = foundDict["acct"] as String // WORKS
        println("ACCOUNT: \(account)")

        let foundSecret = NSString(data: foundDict[kSecValueData as NSString] as NSData, encoding: NSUTF8StringEncoding) as String
        println("PASSWORD: \(foundSecret)")

        return foundSecret
    } else {
        println("Nothing was retrieved from the keychain. Status code \(status)")
    }

    return ""
}

let contents = getPassword(serverName) as String

As far as I know, the ugliness of this code is mostly inevitable due to the nature of the Keychain APIs. When I run this code locally, I see the following output:

0
ACCOUNT: bgentry@fakedomain.com
PASSWORD: this.is.a.fake.password
Program ended with exit code: 0

, , . NSDictionary keychain, API ( foundDict), , SecItemAttr/FourCharCode, . , kSecAccountItemAttr keychain. Objective-C:

typedef FourCharCode SecItemAttr;
enum
{
    ...
    kSecAccountItemAttr             = 'acct',
    ...
}

Swift , :

// Runtime error, "fatal error: unexpectedly found nil while unwrapping an Optional value"
let account = foundDict[kSecAccountItemAttr] as String

// Runtime error, "fatal error: unexpectedly found nil while unwrapping an Optional value"
let account = foundDict[kSecAccountItemAttr as NSNumber] as String

// Compiler error, "type 'FourCharCode' does not conform to protocol 'NSCopying'"
let account = foundDict[kSecAccountItemAttr as FourCharCode] as String

// Compiler error, "'Int' is not convertible to 'String'"
let account = foundDict[kSecAccountItemAttr as String] as String

// This works:
let account = foundDict["acct"] as String

, , Swift.

?

+4
1

keychain, kSecAttrAccount ( NSString), kSecAccountItemAttr. :

if let account = foundDict[kSecAttrAccount as NSString] as? NSString {
    println("ACCOUNT: \(account)")
}
+2

All Articles