agens-no / EllipticCurveKeyPair

Sign, verify, encrypt and decrypt using the Secure Enclave
Other
709 stars 115 forks source link

Document how to use on macOS #10

Closed swizzlr closed 6 years ago

swizzlr commented 7 years ago

Can't quite get the basic examples working on macOS, it's complaining about the parameters being incorrect.

I'll update here my findings when I figure it out.

hfossli commented 7 years ago

Are you using latest on master? Master had some bugs regarding that about 16 hours ago.

I haven’t tried to run this on MacOS myself, yet, but I have tried to support MacOS on an API level all the while. Sorry if that caused any confusion. I should update the readme.

If you get it working that would be awesome. Let me know if I can assist in some way! Thanks for reporting this issue!

hfossli commented 7 years ago

Maybe also check what Valet does https://github.com/square/Valet

swizzlr commented 7 years ago

Yeah I was only using 1.0.2. Compilation is fine obviously, just runtime.

I found a code snippet on the Dev forums that did seem to yield an EC. I’ll compare with what you have here. I’ll also check master and Valet.

swizzlr commented 7 years ago

Oh wait, I was using master.

It works on MacOS!

I had to create a full application and enable Keychain Access Groups in the entitlements. It's fine to not specify any actual groups, but you have to switch it on in Xcode.

Now to try to get this working on the CLI...

swizzlr commented 7 years ago

Closing this issue, but I'll document anything else I learn here.

hfossli commented 7 years ago

Allright! That’s awesome!

If you want to contribute to this project a mac os demo similar to the ios demo in the xcodeproj would be awesome

hfossli commented 7 years ago

Whoops. Tapped the reopen button by mistake here on the phone. Might as well keep it open until I fully document how to use this micro library on mac.

swizzlr commented 7 years ago

Todo:

hfossli commented 7 years ago

I’ve been meaning to add a new target in the xcodeproj targetting macOS. That could serve as a template? What do you think?

swizzlr commented 7 years ago

Sounds like a great idea!

hfossli commented 7 years ago

I have started work on creating a demo for macOS. Can you have a look @swizzlr?

I keep getting errors like

Error: underlying(message: "Could not generate keypair.", error: Error Domain=NSOSStatusErrorDomain Code=-25293 "errKCAuthFailed / errSecAuthFailed:  / Authorization/Authentication failed." UserInfo={NSLocalizedRecoverySuggestion=See https://www.osstatus.com/search/results?platform=all&framework=all&search=-25293, NSLocalizedDescription=Could not generate keypair.})

and

Error: underlying(message: "Could not generate keypair.", error: Error Domain=NSOSStatusErrorDomain Code=-34018 "Could not generate keypair." UserInfo={NSLocalizedRecoverySuggestion=See https://www.osstatus.com/search/results?platform=all&framework=all&search=-34018, NSLocalizedDescription=Could not generate keypair.})

Please enlighten me :)

swizzlr commented 7 years ago

@hfossli Those are new to me. I'll try to check it out tonight.

hfossli commented 7 years ago

@wuf810 and @swizzlr Now it is possible to run this on my iMac as well :) I think the mac demo is great now. On macs that doesn't possess secure enclave it is possible to authenticate use of the private key with application password or system password. Only thing that bugs me is that it asks about the system password a couple times too much... It asks each time I query the private key instead of each time I use (e.g. sign, decrypt) with the private key. 😞

hfossli commented 7 years ago

The library has grown quite a bit and that saddens me a bit.. I don't know if I should avoid branching code like this with available-checks or not

        private func queryData() throws -> PublicKeyData {
            let keyRaw: Data
            if #available(iOS 10.0, *) {
                keyRaw = try export()
            } else {
                keyRaw = try exportWithOldApi()
            }
            guard keyRaw.first == Constants.noCompression else {
                throw Error.inconcistency(message: "Tried reading public key bytes, but its headers says it is compressed and this library only handles uncompressed keys.")
            }
            return PublicKeyData(keyRaw)
        }

        @available(iOS 10.0, *)
        private func export() throws -> Data {
            var error : Unmanaged<CFError>?
            guard let raw = SecKeyCopyExternalRepresentation(underlying, &error) else {
                throw EllipticCurveKeyPair.Error.fromError(error?.takeRetainedValue(), message: "Tried reading public key bytes.")
            }
            return raw as Data
        }

        private func exportWithOldApi() throws -> Data {
            var matchResult: AnyObject? = nil
            let query: [String:Any] = [
                kSecClass as String: kSecClassKey,
                kSecValueRef as String: underlying,
                kSecReturnData as String: true
            ]
            logger?("SecItemCopyMatching: \(query)")
            let status = SecItemCopyMatching(query as CFDictionary, &matchResult)
            guard status == errSecSuccess else {
                throw Error.osStatus(message: "Could not generate keypair", osStatus: status)
            }
            guard let keyRaw = matchResult as? Data else {
                throw Error.inconcistency(message: "Tried reading public key bytes. Expected data, but received \(String(describing: matchResult)).")
            }
            return keyRaw
        }
swizzlr commented 7 years ago

The branching seems acceptable/necessary. As I continue with my work I'll see if there's a way to cache the authentication.

wuf810 commented 7 years ago

You can cache the authentication by passing a LAContext into the the query.

I’m on holiday at the moment so don’t have time to paste Example code but you should be able to find examples.

Either way the process is 1. Context. 2. Set it’s expiry period and 3. pass this in with a key to the query.

NB. Currently the period of expiry doesn’t seem to work and context remains valid (no authentication dialog) for 10 minutes.

wuf810 commented 7 years ago

Also note that the TouchID authentication can be applied to keys/certificates stored eithe in or outside the secureEnclave. That is determined by a different flag (again I’ll post code when I’m back) when generating the keys.

wuf810 commented 7 years ago

I'm not sure I answered the question you are actually asking above @swizzlr but if I am, then one way to cache the authentication is to create a Context and set an reused duration us this:

let context = LAContext() context.touchIDAuthenticationAllowableReuseDuration = LATouchIDAuthenticationMaximumAllowableReuseDuration

then in your SecItemCopyMatching query use it like this:

query[kSecUseAuthenticationContext as String] = context

Or you could call evaluatePolicy on the context if you like. Basically you create the context, force the authentication and then the reuse period cuts it.

But note what I said about the expiry period seemingly being set to 10 minutes regardless. Also note if you create a new context, you'll nullify the period and will be immediately prompted for authentication again.

hfossli commented 7 years ago

What Michael says works well if you provide .privateKeyUsage in access control flags. It does not work well however if you don't provide that flag on e.g. OSX hardware that doesn't have TouchID. In that situation I think you are better off using no context at all.

hfossli commented 7 years ago

So I have pushed a beta release 2.0-beta1. Several breaking changes. Now it works well on macOS hardware that doesn't have touch id. I had some problems with LAContext.

Here's what's changed https://github.com/agens-no/EllipticCurveKeyPair/compare/1.0.2...2.0-beta1

If you have any feedback I'll fix that before releasing 2.0. Master should have probably be pointing to 1.0.2, but I will do better next time.

moritz2 commented 6 years ago

Are there any updates for the hardware with Touch ID?

I always get the following error on MacBook Pro (13-inch, 2016, Four Thunderbolt 3 Ports), running 10.12.6:

Error: underlying(message: "Could not generate keypair.", error: Error Domain=NSOSStatusErrorDomain Code=-34018 "Could not generate keypair." UserInfo={NSLocalizedRecoverySuggestion=See https://www.osstatus.com/search/results?platform=all&framework=all&search=-34018, NSLocalizedDescription=Could not generate keypair.})

hfossli commented 6 years ago

Which branch/tag?

hfossli commented 6 years ago

And how does your config object look?

moritz2 commented 6 years ago

I use the macOS-Demo from the master-branch.

hfossli commented 6 years ago

Okay. Can you copy paste how you set up the manager/helper?

moritz2 commented 6 years ago

I haven't changed the demo code.

In the demo code it is set up like this

static let keypair: EllipticCurveKeyPair.Manager = { EllipticCurveKeyPair.logger = { print($0) } let publicAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAlwaysThisDeviceOnly, flags: []) let privateAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, flags: { return EllipticCurveKeyPair.Device.hasSecureEnclave ? [.userPresence, .privateKeyUsage] : [.userPresence] }()) let config = EllipticCurveKeyPair.Config( publicLabel: "no.agens.sign.public", privateLabel: "no.agens.sign.private", operationPrompt: "Sign transaction", publicKeyAccessControl: publicAccessControl, privateKeyAccessControl: privateAccessControl, token: .secureEnclaveIfAvailable) return EllipticCurveKeyPair.Manager(config: config) }()

hfossli commented 6 years ago

I know I won't be able to look at this before next week. Everything is working fine on my end on my imac without secure enclave. Can you verify you have the entitlements properly set up? https://www.osstatus.com/search/results?platform=all&framework=all&search=-34018

moritz2 commented 6 years ago

okay now I got the problem. the problem was the provisioning profile. just delete it and create a new one, and now the code works. thank you 😊

hfossli commented 6 years ago

Awesome. That was a proper error message from security then. I feared it was pointing to something else, but it was on point.