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

Implement app attest key ID storage #8014

Merged
merged 10 commits into from
May 3, 2021

Conversation

ncooke3
Copy link
Member

@ncooke3 ncooke3 commented Apr 30, 2021

• Implements FIRAppAttestKeyIDStorage
• Adds tests

b/186653653

@google-oss-bot
Copy link

1 Warning
⚠️ Did you forget to add a changelog entry? (Add #no-changelog to the PR description to silence this warning.)

Generated by 🚫 Danger

Copy link
Contributor

@maksymmalyhin maksymmalyhin left a comment

Choose a reason for hiding this comment

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

Thank you for the implementation! Mostly LGTM, a couple adjustments needed.

#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"

/// The `NSUserDefaults` suite name for the storage location of the app attest key ID.
static NSString *const kFIRAppAttestKeyIDStorageDefaultsSuiteName = @"com.firebase.FIRAppAttestKeyIDStorage";
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: local static variables/constants are not visible in the global scope, so prefixing is not required, so it can be just kKeyIDStorageDefaultsSuiteName

Copy link
Member Author

Choose a reason for hiding this comment

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

Follow-up question: Should "com.firebase.appcheck" be used instead of "com.firebase.FIRAppAttestKeyIDStorage"? I referenced the conversation in this pull request and was wondering if "com.firebase.appcheck" is a better name for a suite of appcheck related defaults (and, if so, then should FIRAppCheckSettings use the "com.firebase.appcheck" suite as well– instead of using the shared user defaults instance).

Copy link
Contributor

Choose a reason for hiding this comment

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

I think using a class specific suite name for this case is a good idea, so "com.firebase.FIRAppAttestKeyIDStorage" LGTM.

#pragma mark - Helpers

- (void)storeAppAttestKeyID:(nullable NSString *)keyID {
@synchronized (self.userDefaults) {
Copy link
Contributor

Choose a reason for hiding this comment

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

optional: Alternatively we may use a private dispatch queue for synchronization which is a preferable way when we have async API. You may leverage API like +[FBLPromise onQueue:do:] to organize to code.

Copy link
Member Author

Choose a reason for hiding this comment

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

Discussed offline. Decision is to allow FIRAppAttestKeyIDStorage's sole intended consumer, FIRAppAttestProvider, to manage synchronization of FIRAppAttestKeyIDStorage on its (FIRAppAttestProvider's) internal serial queue.

A comment of the intended use has been added at the public interface definition of FIRAppAttestKeyIDStorage.

- (nullable NSString *)appAttestKeyIDFromStorage {
NSString *appAttestKeyID = nil;
@synchronized (self.userDefaults) {
appAttestKeyID = [self.userDefaults objectForKey:kFIRAppAttestKeyIDKey];
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to store an App Attest key ID per Firebase app instance to make sure they can operate independently. The app instance is identified by the app name. In addition we need to make sure that if Firebase config changes, the app can still work correctly on user devices after update. For this we need to keep track of the app ID. The easiest way to achieve it in the code I can see is to make the app name and the app ID a part of the key. We use this approach already in the core part of the SDK .

Copy link
Member Author

Choose a reason for hiding this comment

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

Done and added a test like testSetTokenPerApp seen here.

Copy link
Contributor

@maksymmalyhin maksymmalyhin left a comment

Choose a reason for hiding this comment

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

LGTM, one recommendation to add additional test cases.

NSString *appAttestKeyID = @"app_attest_key_ID";

FBLPromise *setPromise = [self.storage setAppAttestKeyID:appAttestKeyID];
XCTAssertEqualObjects(setPromise.value, appAttestKeyID);
Copy link
Contributor

Choose a reason for hiding this comment

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

This currently works because the storage has currently synchronous implementation. If it was async, then waiting for async operations to complete with FBLWaitForPromisesWithTimeout like here is the way to make it working.

__auto_type getPromise = [storage2 getAppAttestKeyID];
XCTAssertNotNil(getPromise.error);
XCTAssertEqualObjects(getPromise.error, [FIRAppCheckErrorUtil appAttestKeyIDNotFound]);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think it is worth to assert a couple more cases:

  1. check the two storages updating the storages can be updated independently
  2. check the same for storages with the same app IDs and different app names

@ncooke3 ncooke3 marked this pull request as ready for review May 3, 2021 14:19
Copy link
Contributor

@maksymmalyhin maksymmalyhin left a comment

Choose a reason for hiding this comment

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

Great, thank you for the updates. Just one more optional recommendation on improving readability.


// 5. Cleanup other storages.
[storage2 setAppAttestKeyID:nil];
[storage3 setAppAttestKeyID:nil];
Copy link
Contributor

Choose a reason for hiding this comment

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

optional: now it looks a bit harder to follow the test. We probably may slightly improve readability here, e.g.:

  • create a helper method like - assertIndependentSetGetForStoragesWithAppName1:appID1:appName2:appID2: where we assert independent reads and writes
  • call this method with all combinations we are interested in

WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for taking a look. Yeah, I agree. I was thinking about how to make it easier to understand. I like this approach ^ though. 👍

Copy link
Contributor

@maksymmalyhin maksymmalyhin left a comment

Choose a reason for hiding this comment

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

Looks more readable now, thanks! LGTM on CI green.

@ncooke3 ncooke3 merged commit 869f87c into appcheck-appattest-main May 3, 2021
@ncooke3 ncooke3 deleted the nc/appcheck-keyid-storage branch May 3, 2021 16:48
maksymmalyhin added a commit that referenced this pull request Jun 1, 2021
* App Attest provider: attestation sequence (#7971)

* App Attest provider: attestation sequence (#761)

* App Attest draft WIP

* FIRAppAttestProvider initializers

* ./scripts/style.sh

* FIRAppAttestProvider implementation draft

* Basic FIRAppAttestProviderTests and fixes

* style

* testGetTokenWhenAppAttestIsNotSupported

* More FIRAppAttestProviderTests

* Cleanup

* Remove unused file

* Availability annotations on DCAppAttestService category.

* Guard FIRAppAttestProvider with #if TARGET_OS_IOS

* Formatting

* Fix SPM

* app_check.yaml: Add diagnostics SPM builds

* fix yaml

* Fix Firebase-Package scheme bad merge

* Fix typo

* FIRAppAttestProvider: hide default init

* FIRAppAttestKeyIDStorage: methods placeholders

* Comments

* Fix updated block definition

* Implement app attest key ID storage (#8014)

* Implement FIRAppAttestKeyIDStorage

* Add FIRAppAttestKeyIDStorageTests

* Review [Draft]

* Style

* Docs updates

* Docs updates 2

* Review [Draft] 2

* Improve tests

* Improve test readability

* Improve test readability 2

* App Check App Attest workflow updates: initial handshake (#8032)

* Handshake adjustments (WIP)

* Introduce FIRAppAttestProviderState

* WIP: calculate attestation state

* WIP: calculate attestation state 2

* formatting

* Comments and moving code around

* Fix init in tests

* Fix state calculation flow

* Cleanup state calculation and fix tests.

* Cleanup and fixes.

* Comments

* formatting

* Fix import

* Typo fixes and additional comments

* FIRAppAttestInitialHandshakeResponse API

* Cleanup state calculation using FBLPromiseAwait

* Cleanup

* style

* FIRAppAttestArtifactStorage implementation and tests (#8041)

* Update comments

* FIRAppAttestArtifactStorage implementation and tests

* Fix init

* API docs

* Clean up storage in tests

* Comments

* Disable Keychain dependent tests for SPM

* Implement App Attest `getRandomChallenge` (#8033)

* Initial implementation

* Parse response body for challenge and stub test cases

* Review [Draft]

* Avoid encoding challenge again

* Add tests

* Revert "Avoid encoding challenge again" and add TODO

This reverts commit 69eb00d.

* Document tests; Add test

* Tests: Add URL validation check

* Review

* Define Exchange AppAttest Assertion for FAC token API (#8058)

* App Check App Attest: attestation request (#8059)

* App Attest provider API integration WIP

* update tests

* Draft attestation response parsing

* Attestation request draft

* style

* AppAttest Attestation API tests draft

* Error cases tests

* style

* Cleanup and API docs

* Merge fix

* Fix OCMock imports

* Fix nullability modifier

* Formatting

* comments

* App Check App Attest initial handshake adjustments (#8067)

* calculatre sha256 of random challenge for attestation

* Test app adjustments

* cleanup

* use trailing closures in the test app

* Implement API for ExchangeAppAttestAssertionRequest endpoint (#8065)

* Implement assertion exchange

* Tweak existing tests

* Add tests

* Rename JSON to better match gRPC  message

* Add HTTPBody helper

* Review

* Review 2

* Review 3

* App Check App Attest assertion flow (#8083)

* App Attest assertion workflow draft

* send request

* assertion flow tests

* style

* App Check: store App Attest artifact per key ID (#8097)

* Update artifact storage API and tests

* Artifact storage implementation update

* Save artifact for a key ID

* Style

* typos

* App Check: prevent concurrent token requests (#8117)

* App Attest multiple get token method invocation tests

* Ensure a single App Attest handshake sequence at the time

* FIRAppCheckTests: get token request merging tests

* FIRAppCheck: Ensure a single get token operation at the time

* formatting

* Test new request after merged requests

* Release finished operation promise

* Style

* Typos

* typo

* Request merging tests for error cases

* formatting

* Changelog

* App Check App Attest: handle attestation rejection (#8170)

* Remove/update outdated TODOs

* [WIP] Attestation rejection handling draft

* style

* retry tests draft

* reset key ID before retry

* Reset attestation

* test error and fixes

* style

* More details in the name

* Some debug logging

* style

* Use specific codes for log messages

* style

* Add FIRAppAttestProvider.h the umbrella header

Co-authored-by: Nick Cooke <36927374+ncooke3@users.noreply.github.com>
@firebase firebase locked and limited conversation to collaborators Jun 3, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy