FIRAppAttestArtifactStorageTests.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * Copyright 2021 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <TargetConditionals.h>
  17. // Tests that use the Keychain require a host app and Swift Package Manager
  18. // does not support adding a host app to test targets.
  19. #if !SWIFT_PACKAGE
  20. // Skip keychain tests on Catalyst and macOS. Tests are skipped because they
  21. // involve interactions with the keychain that require a provisioning profile.
  22. // See go/firebase-macos-keychain-popups for more details.
  23. #if !TARGET_OS_MACCATALYST && !TARGET_OS_OSX
  24. #import <XCTest/XCTest.h>
  25. #import <OCMock/OCMock.h>
  26. #import <GoogleUtilities/GULKeychainStorage.h>
  27. #import "FBLPromise+Testing.h"
  28. #import "FirebaseAppCheck/Sources/AppAttestProvider/Storage/FIRAppAttestArtifactStorage.h"
  29. #import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
  30. @interface FIRAppAttestArtifactStorageTests : XCTestCase
  31. @property(nonatomic) NSString *appName;
  32. @property(nonatomic) NSString *appID;
  33. @property(nonatomic) FIRAppAttestArtifactStorage *storage;
  34. @end
  35. @implementation FIRAppAttestArtifactStorageTests
  36. - (void)setUp {
  37. [super setUp];
  38. self.appName = @"FIRAppAttestArtifactStorageTests";
  39. self.appID = @"1:100000000000:ios:aaaaaaaaaaaaaaaaaaaaaaaa";
  40. self.storage = [[FIRAppAttestArtifactStorage alloc] initWithAppName:self.appName
  41. appID:self.appID
  42. accessGroup:nil];
  43. }
  44. - (void)tearDown {
  45. self.storage = nil;
  46. [super tearDown];
  47. }
  48. - (void)testSetAndGetArtifact {
  49. [self assertSetGetForStorage];
  50. }
  51. - (void)testRemoveArtifact {
  52. NSString *keyID = [NSUUID UUID].UUIDString;
  53. // 1. Save an artifact to storage and check it is stored.
  54. [self assertSetGetForStorage];
  55. // 2. Remove artifact.
  56. __auto_type setPromise = [self.storage setArtifact:nil forKey:keyID];
  57. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  58. XCTAssertNil(setPromise.value);
  59. XCTAssertNil(setPromise.error);
  60. // 3. Check it has been removed.
  61. __auto_type getPromise = [self.storage getArtifactForKey:keyID];
  62. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  63. XCTAssertNil(getPromise.value);
  64. XCTAssertNil(getPromise.error);
  65. }
  66. - (void)testSetAndGetPerApp {
  67. // Assert storages for apps with the same name can independently set/get artifact.
  68. [self assertIndependentSetGetForStoragesWithAppName1:self.appName
  69. appID1:@"app_id"
  70. appName2:self.appName
  71. appID2:@"app_id_2"];
  72. // Assert storages for apps with the same app ID can independently set/get artifact.
  73. [self assertIndependentSetGetForStoragesWithAppName1:@"app_1"
  74. appID1:self.appID
  75. appName2:@"app_2"
  76. appID2:self.appID];
  77. // Assert storages for apps with different info can independently set/get artifact.
  78. [self assertIndependentSetGetForStoragesWithAppName1:@"app_1"
  79. appID1:@"app_id_1"
  80. appName2:@"app_2"
  81. appID2:@"app_id_2"];
  82. }
  83. - (void)testSetArtifactForOneKeyGetForAnotherKey {
  84. // Set an artifact for a key.
  85. [self assertSetGetForStorage];
  86. // Try to get artifact for a different key.
  87. NSString *keyID = [NSUUID UUID].UUIDString;
  88. __auto_type getPromise = [self.storage getArtifactForKey:keyID];
  89. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  90. XCTAssertNil(getPromise.value);
  91. XCTAssertNil(getPromise.error);
  92. }
  93. - (void)testSetArtifactForNewKeyRemovesArtifactForOldKey {
  94. // 1. Store an artifact.
  95. NSString *oldKeyID = [self assertSetGetForStorage];
  96. // 2. Replace the artifact.
  97. NSString *newKeyID = [self assertSetGetForStorage];
  98. XCTAssertNotEqualObjects(oldKeyID, newKeyID);
  99. // 3. Check old artifact was removed.
  100. __auto_type getPromise = [self.storage getArtifactForKey:oldKeyID];
  101. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  102. XCTAssertNil(getPromise.value);
  103. XCTAssertNil(getPromise.error);
  104. }
  105. - (void)testGetArtifact_KeychainError {
  106. // 1. Set up storage mock.
  107. id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]);
  108. FIRAppAttestArtifactStorage *artifactStorage =
  109. [[FIRAppAttestArtifactStorage alloc] initWithAppName:self.appName
  110. appID:self.appID
  111. keychainStorage:mockKeychainStorage
  112. accessGroup:nil];
  113. // 2. Create and expect keychain error.
  114. NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil];
  115. OCMExpect([mockKeychainStorage getObjectForKey:[OCMArg any]
  116. objectClass:[OCMArg any]
  117. accessGroup:[OCMArg any]])
  118. .andReturn([FBLPromise resolvedWith:gulsKeychainError]);
  119. // 3. Get artifact and verify results.
  120. __auto_type getPromise = [artifactStorage getArtifactForKey:@"key"];
  121. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  122. XCTAssertNotNil(getPromise.error);
  123. XCTAssertEqualObjects(getPromise.error,
  124. [FIRAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]);
  125. // 4. Verify storage mock.
  126. OCMVerifyAll(mockKeychainStorage);
  127. }
  128. - (void)testSetArtifact_KeychainError {
  129. // 1. Set up storage mock.
  130. id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]);
  131. FIRAppAttestArtifactStorage *artifactStorage =
  132. [[FIRAppAttestArtifactStorage alloc] initWithAppName:self.appName
  133. appID:self.appID
  134. keychainStorage:mockKeychainStorage
  135. accessGroup:nil];
  136. // 2. Create and expect keychain error.
  137. NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil];
  138. OCMExpect([mockKeychainStorage setObject:[OCMArg any]
  139. forKey:[OCMArg any]
  140. accessGroup:[OCMArg any]])
  141. .andReturn([FBLPromise resolvedWith:gulsKeychainError]);
  142. // 3. Set artifact and verify results.
  143. NSData *artifact = [@"artifact" dataUsingEncoding:NSUTF8StringEncoding];
  144. __auto_type setPromise = [artifactStorage setArtifact:artifact forKey:@"key"];
  145. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  146. XCTAssertNotNil(setPromise.error);
  147. XCTAssertEqualObjects(setPromise.error,
  148. [FIRAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]);
  149. // 4. Verify storage mock.
  150. OCMVerifyAll(mockKeychainStorage);
  151. }
  152. - (void)testRemoveArtifact_KeychainError {
  153. // 1. Set up storage mock.
  154. id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]);
  155. FIRAppAttestArtifactStorage *artifactStorage =
  156. [[FIRAppAttestArtifactStorage alloc] initWithAppName:self.appName
  157. appID:self.appID
  158. keychainStorage:mockKeychainStorage
  159. accessGroup:nil];
  160. // 2. Create and expect keychain error.
  161. NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil];
  162. OCMExpect([mockKeychainStorage removeObjectForKey:[OCMArg any] accessGroup:[OCMArg any]])
  163. .andReturn([FBLPromise resolvedWith:gulsKeychainError]);
  164. // 3. Remove artifact and verify results.
  165. __auto_type setPromise = [artifactStorage setArtifact:nil forKey:@"key"];
  166. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  167. XCTAssertNotNil(setPromise.error);
  168. XCTAssertEqualObjects(setPromise.error,
  169. [FIRAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]);
  170. // 4. Verify storage mock.
  171. OCMVerifyAll(mockKeychainStorage);
  172. }
  173. #pragma mark - Helpers
  174. /// Sets a random artifact for a random key and asserts it can be read.
  175. /// @return The random key ID used to set and get the artifact.
  176. - (NSString *)assertSetGetForStorage {
  177. NSData *artifactToSet = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding];
  178. NSString *keyID = [NSUUID UUID].UUIDString;
  179. __auto_type setPromise = [self.storage setArtifact:artifactToSet forKey:keyID];
  180. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  181. XCTAssertEqualObjects(setPromise.value, artifactToSet);
  182. XCTAssertNil(setPromise.error);
  183. __auto_type getPromise = [self.storage getArtifactForKey:keyID];
  184. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  185. XCTAssertEqualObjects(getPromise.value, artifactToSet);
  186. XCTAssertNil(getPromise.error);
  187. __weak __auto_type weakSelf = self;
  188. [self addTeardownBlock:^{
  189. // Cleanup storage.
  190. [weakSelf.storage setArtifact:nil forKey:keyID];
  191. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  192. }];
  193. return keyID;
  194. }
  195. - (void)assertIndependentSetGetForStoragesWithAppName1:(NSString *)appName1
  196. appID1:(NSString *)appID1
  197. appName2:(NSString *)appName2
  198. appID2:(NSString *)appID2 {
  199. NSString *keyID = [NSUUID UUID].UUIDString;
  200. // Create two storages.
  201. FIRAppAttestArtifactStorage *storage1 =
  202. [[FIRAppAttestArtifactStorage alloc] initWithAppName:appName1 appID:appID1 accessGroup:nil];
  203. FIRAppAttestArtifactStorage *storage2 =
  204. [[FIRAppAttestArtifactStorage alloc] initWithAppName:appName2 appID:appID2 accessGroup:nil];
  205. // 1. Independently set artifacts for the two storages.
  206. NSData *artifact1 = [@"app_attest_artifact1" dataUsingEncoding:NSUTF8StringEncoding];
  207. FBLPromise *setPromise1 = [storage1 setArtifact:artifact1 forKey:keyID];
  208. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  209. XCTAssertEqualObjects(setPromise1.value, artifact1);
  210. XCTAssertNil(setPromise1.error);
  211. NSData *artifact2 = [@"app_attest_artifact2" dataUsingEncoding:NSUTF8StringEncoding];
  212. __auto_type setPromise2 = [storage2 setArtifact:artifact2 forKey:keyID];
  213. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  214. XCTAssertEqualObjects(setPromise2.value, artifact2);
  215. XCTAssertNil(setPromise2.error);
  216. // 2. Get artifacts for the two storages.
  217. __auto_type getPromise1 = [storage1 getArtifactForKey:keyID];
  218. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  219. XCTAssertEqualObjects(getPromise1.value, artifact1);
  220. XCTAssertNil(getPromise1.error);
  221. __auto_type getPromise2 = [storage2 getArtifactForKey:keyID];
  222. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  223. XCTAssertEqualObjects(getPromise2.value, artifact2);
  224. XCTAssertNil(getPromise2.error);
  225. // 3. Assert that artifacts were set and retrieved independently of one another.
  226. XCTAssertNotEqualObjects(getPromise1.value, getPromise2.value);
  227. // Cleanup storages.
  228. [storage1 setArtifact:nil forKey:keyID];
  229. [storage2 setArtifact:nil forKey:keyID];
  230. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  231. }
  232. @end
  233. #endif // !TARGET_OS_MACCATALYST && !TARGET_OS_OSX
  234. #endif // !SWIFT_PACKAGE