Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

errSecItemNotFound after iCloud backup #36

Open
giuseppeVacatello opened this issue Dec 4, 2018 · 13 comments
Open

errSecItemNotFound after iCloud backup #36

giuseppeVacatello opened this issue Dec 4, 2018 · 13 comments

Comments

@giuseppeVacatello
Copy link

Hi,
after a backup from one iPhone to other, my app, throw this exception:

> Found public key, but couldn't find or access private key. The errSecItemNotFound error is sometimes wrongfully reported when LAContext authentication fails

I can not understand what the problem is.
Some advice? thank you.

@hfossli
Copy link
Contributor

hfossli commented Dec 4, 2018

Private keys can not be migrates. Public keys can. In this case you need to recreate the key pair.

Also you should configure the public key to not be possible to migrate using the flags.

@giuseppeVacatello
Copy link
Author

What is the flag for public key to not migrate? Thank you

@hfossli
Copy link
Contributor

hfossli commented Dec 5, 2018

Sorry, I meant protection id. You select one of the protection id's that fits your needs from here

https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_attribute_keys_and_values?language=objc#1679100
For example you can choose this one https://developer.apple.com/documentation/security/ksecattraccessibleafterfirstunlockthisdeviceonly?language=objc

Example with kSecAttrAccessibleAlwaysThisDeviceOnly

struct KeyPair {
    static let manager: EllipticCurveKeyPair.Manager = {
        let publicAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAlwaysThisDeviceOnly, flags: [])
        let privateAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, flags: [.userPresence, .privateKeyUsage])
        let config = EllipticCurveKeyPair.Config(
            publicLabel: "payment.sign.public",
            privateLabel: "payment.sign.private",
            operationPrompt: "Confirm payment",
            publicKeyAccessControl: publicAccessControl,
            privateKeyAccessControl: privateAccessControl,
            token: .secureEnclave)
        return EllipticCurveKeyPair.Manager(config: config)
    }()
}

@giuseppeVacatello
Copy link
Author

My problem is in this method:

public func privateKey(context: LAContext? = nil) throws -> PrivateKey {
            do {
                if cachedPrivateKey?.context !== context {
                    cachedPrivateKey = nil
                }
                if let key = cachedPrivateKey {
                    return key
                }
                let key = try helper.getPrivateKey(context: context)
                cachedPrivateKey = key
                return key
            } catch EllipticCurveKeyPair.Error.underlying(_, let underlying) where underlying.code == errSecItemNotFound {
                if config.publicKeyAccessControl.flags.contains(.privateKeyUsage) == false, (try? helper.getPublicKey()) != nil {
                    **throw Error.probablyAuthenticationError(underlying: underlying)**
                }
                let keys = try helper.generateKeyPair(context: nil)
                cachedPublicKey = keys.public
                cachedPrivateKey = keys.private
                return keys.private
            } catch {
                throw error
            }
        }

Once I finish in the catch, the code throws "Error.probablyAuthenticationError(underlying: underlying)" and I can not recover the situation.

Thanks for the tips until now

@hfossli
Copy link
Contributor

hfossli commented Dec 5, 2018

In the event of an itunes backup and you get this error

Found public key, but couldn't find or access private key. The errSecItemNotFound error is sometimes wrongfully reported when LAContext authentication fails

Then you need to delete the key (pseudo code)

do {
    let privateKey = try manager.privateKey()
} catch {
    if error == Error.probablyAuthenticationError {
        try? manager.deleteKeyPair()
    }
    do {
        let privateKey = try manager.privateKey()
    } catch {
        // this should be handled or reported back to user
    }
}

What is your minimum deployment target? If it is iOS 10 you are lucky, then I have another solution for you.

@giuseppeVacatello
Copy link
Author

yes, it is iOS 10.

@hfossli
Copy link
Contributor

hfossli commented Dec 5, 2018

Awesome. Then you may choose to not store the public key and instead just derive it from the private key when needed using SecKeyCopyPublicKey. Some modifications to the library is needed in order to get that to work. I want to create a pre-ios-10 version and a post-ios-10 version. Maybe I find time this week.

@giuseppeVacatello
Copy link
Author

In the meantime of your changes how can I fix it?
If I delete the keypair I do not have to regenerate the entire keypair?
In your example above only the private key is regenerated

@hfossli
Copy link
Contributor

hfossli commented Dec 6, 2018

You need to regenerate the entire keypair

@giuseppeVacatello
Copy link
Author

I did it.
Thank you.
Wait for your modifications of library :)

@muhamedhfayiz
Copy link

I'm also facing the same issue after restoring the ios device. private key not found. able to get a public key. could you please share the code?

@hfossli
Copy link
Contributor

hfossli commented May 27, 2021

It is not possible to restore a private key stored in the secure enclave

@muhamedhfayiz
Copy link

Thank you.
After successful deletion of keypair its works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants