1
\$\begingroup\$

I want to be able to store and retrieve a unique user throughout the app. Multiple accounts are not used. The code I have written is stated below.

This is my first attempt using KeyChain. The test succeeds. I am wondering if anything if the code can be improved. Security issues perhaps?

import XCTest

class KeyChain {
    struct User {
        let identifier: Int64
        let password: String
    }

    static func save(user: User) -> Bool {
        let identifier = Data(from: user.identifier)
        let password = user.password.data(using: .utf8)!
        let query = [kSecClass as String : kSecClassGenericPassword as String,
                     kSecAttrService as String : "comapp",
                     kSecAttrAccount as String : identifier,
                     kSecValueData as String : password]
            as [String : Any]

        let deleteStatus = SecItemDelete(query as CFDictionary)

        if deleteStatus == noErr || deleteStatus == errSecItemNotFound {
            return SecItemAdd(query as CFDictionary, nil) == noErr
        }

        return false
    }

    static func retrieveUser() -> User? {
        let query = [kSecClass as String : kSecClassGenericPassword,
                     kSecAttrService as String : "comapp",
                     kSecReturnAttributes as String : kCFBooleanTrue!,
                     kSecReturnData as String: kCFBooleanTrue!]
            as [String : Any]

        var result: AnyObject? = nil
        let status = SecItemCopyMatching(query as CFDictionary, &result)

        if status == noErr,
            let dict = result as? [String: Any],
            let passwordData = dict[String(kSecValueData)] as? Data,
            let password = String(data: passwordData, encoding: .utf8),
            let identifier = (dict[String(kSecAttrAccount)] as? Data)?.to(type: Int64.self) {

            return User(identifier: identifier, password: password)
        } else {
            return nil
        }
    }
}

private extension Data {
    init<T>(from value: T) {
        var value = value

        self.init(buffer: UnsafeBufferPointer(start: &value, count: 1))
    }

    func to<T>(type: T.Type) -> T {
        withUnsafeBytes { $0.load(as: T.self) }
    }
}


class SecureStoreTests: XCTestCase {
    func testUser() {
        let user = KeyChain.User(identifier: 200, password: "somePassword")

        let didAddedUser = KeyChain.save(user: user)

        XCTAssertTrue(didAddedUser)

        let retrievedUser = KeyChain.retrieveUser()!

        XCTAssert(user.identifier == retrievedUser.identifier)
        XCTAssert(user.password == retrievedUser.password)
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.