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

[Valet 4.0] Remove .always accessibility specifier #197

Merged
merged 8 commits into from
Dec 30, 2019

Conversation

dfed
Copy link
Collaborator

@dfed dfed commented Dec 24, 2019

This PR removes the .always and .alwaysThisDeviceOnly accessibility specifiers. These specifiers were deprecated in iOS 12, tvOS 12, macOS 10.14, and watchOS 5. They were deprecated because they do not provide any security protection for the user.

The downside of removing these specifiers is that we were using .alwaysThisDeviceOnly to reliably determine the Bundle Seed ID (aka. App ID or Team ID). In this PR, I've removed the code that would return INVALID_SHARED_ACCESS_GROUP_PREFIX when determining the Bundle Seed ID. Instead, we return nil, which makes the keychainQuery nullable. If we can't determine the Bundle Seed ID, this means we can't access the keychain. This propagates to Valet (and friend) instances as a failure to access the keychain, since these objects need to unwrap their keychainQuery before interacting with the keychain. If we don't yet have a keychainQuery, we attempt to determine one before interacting with the keychain.

Note that as part of this PR I removed the KeychainQueryConvertible protocol, since that protocol relied on a non-null keychainQuery. It also wasn't providing us much value, as we only supported migrating values from a Valet (not a SecureEnclaveValet or SinglePromptSecureEnclaveValet).

@dfed dfed mentioned this pull request Dec 24, 2019
10 tasks
@dfed dfed changed the base branch from dfed--simplify-ci-v4 to develop--4.0 December 24, 2019 03:30
@dfed dfed force-pushed the dfed--remove-always-accessibility-specifier branch from bf6808c to a9bc846 Compare December 24, 2019 03:30
@dfed dfed force-pushed the dfed--remove-always-accessibility-specifier branch from a9bc846 to a72acd2 Compare December 24, 2019 22:07
@@ -105,7 +104,7 @@ internal final class SecItem {
// We should always be able to access the shared access group prefix because the accessibility of the above keychain data is set to `always`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update this comment

@@ -90,11 +90,10 @@ internal final class SecItem {
}

guard status == errSecSuccess, let queryResult = result as? [CFString : AnyHashable], let accessGroup = queryResult[kSecAttrAccessGroup] as? String else {
ErrorHandler.assertionFailure("Could not find shared access group prefix.")
// We should always be able to access the shared access group prefix because the accessibility of the above keychain data is set to `always`.
// We may not be able to access the shared access group prefix because the accessibility of the above keychain data is set to `afterFirstUnlock`.
// In other words, we should never hit this code. This code is here as a failsafe to prevent a crash in a scenario where the keychain is entirely hosed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't entirely true any more

@@ -125,6 +135,9 @@ public final class SecureEnclaveValet: NSObject {
/// - returns: The data currently stored in the keychain for the provided key. Returns `.itemNotFound` if no object exists in the keychain for the specified key, or if the keychain is inaccessible. Returns `.userCancelled` if the user cancels the user-presence prompt.
public func object(forKey key: String, withPrompt userPrompt: String) -> SecureEnclave.Result<Data> {
return execute(in: lock) {
guard let keychainQuery = keychainQuery else {
return .itemNotFound
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a new result case for .keychainNotAccessible? (applies throughout diff)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the comment above:

Returns .itemNotFound if no object exists in the keychain for the specified key, or if the keychain is inaccessible.

So we're doing the right thing per the comment. Adding a new case here would be a breaking change. The Valet 4.0 code will throw the right error (couldNotAccessKeychain ) in this case assuming #198 lands

@@ -135,6 +148,9 @@ public final class SecureEnclaveValet: NSObject {
@objc(containsObjectForKey:)
public func containsObject(forKey key: String) -> Bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should note in the header doc that it will return false if the keychain isn't accessible.

@@ -138,6 +151,9 @@ public final class SinglePromptSecureEnclaveValet: NSObject {
@objc(containsObjectForKey:)
public func containsObject(forKey key: String) -> Bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth noting in the header doc that it will return false if the keychain is not accessible.

@@ -176,6 +198,9 @@ public final class SinglePromptSecureEnclaveValet: NSObject {
@objc(allKeysWithUserPrompt:)
public func allKeys(userPrompt: String) -> Set<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth noting the that an empty set will be returned when the keychain is not accessible.

@@ -168,6 +179,9 @@ public final class Valet: NSObject, KeychainQueryConvertible {
@objc(containsObjectForKey:)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth noting in the header doc that it returns false when keychain is not accessible.

@@ -196,6 +216,9 @@ public final class Valet: NSObject, KeychainQueryConvertible {
@objc
public func allKeys() -> Set<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing about header doc

@NickEntin
Copy link
Collaborator

Looks good overall, most comments are about documenting behavior.

One thing to consider here is how consumers that were previously using .always/.alwaysThisDeviceOnly can migrate over to a more secure specifier. We expose the ability to migrate from a raw query or from a Valet, but if the Valet previously used one of those specifiers you won't be able to migrate for it, and consumers shouldn't need to know what the base query from that Valet looked like.

@dfed
Copy link
Collaborator Author

dfed commented Dec 29, 2019

One thing to consider here is how consumers that were previously using .always/.alwaysThisDeviceOnly can migrate over to a more secure specifier. We expose the ability to migrate from a raw query or from a Valet, but if the Valet previously used one of those specifiers you won't be able to migrate for it, and consumers shouldn't need to know what the base query from that Valet looked like.

This is a good point. I've added helper methods in ce5c0e5.

@dfed
Copy link
Collaborator Author

dfed commented Dec 30, 2019

Merging so I can deal with conflicts. Happy to take post-merge feedback on commits made after review.

@dfed dfed merged commit 40c014d into develop--4.0 Dec 30, 2019
@dfed dfed deleted the dfed--remove-always-accessibility-specifier branch December 30, 2019 03:34
@@ -0,0 +1,738 @@
//
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you accidentally duplicate this file? Not sure how this compiled since there's now 2 ValetIntegrationTests classes...

Copy link
Collaborator Author

@dfed dfed Dec 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed 🤦‍♂️. I’ve since removed the duplicate from the develop branch.

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

Successfully merging this pull request may close these issues.

2 participants