FIRAppAttestArtifactStorageTests.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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 <XCTest/XCTest.h>
  17. #import <OCMock/OCMock.h>
  18. #import <GoogleUtilities/GULKeychainStorage.h>
  19. #import "FBLPromise+Testing.h"
  20. #import "FirebaseAppCheck/Sources/AppAttestProvider/Storage/FIRAppAttestArtifactStorage.h"
  21. #import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
  22. @interface FIRAppAttestArtifactStorageTests : XCTestCase
  23. @property(nonatomic) NSString *appName;
  24. @property(nonatomic) NSString *appID;
  25. @property(nonatomic) FIRAppAttestArtifactStorage *storage;
  26. @end
  27. @implementation FIRAppAttestArtifactStorageTests
  28. - (void)setUp {
  29. [super setUp];
  30. self.appName = @"FIRAppAttestArtifactStorageTests";
  31. self.appID = @"1:100000000000:ios:aaaaaaaaaaaaaaaaaaaaaaaa";
  32. self.storage = [[FIRAppAttestArtifactStorage alloc] initWithAppName:self.appName
  33. appID:self.appID
  34. accessGroup:nil];
  35. }
  36. - (void)tearDown {
  37. self.storage = nil;
  38. [super tearDown];
  39. }
  40. #if !TARGET_OS_MACCATALYST // Catalyst should be possible with Xcode 12.5+
  41. - (void)testSetAndGetArtifact {
  42. [self assertSetGetForStorage];
  43. }
  44. - (void)testRemoveArtifact {
  45. NSString *keyID = [NSUUID UUID].UUIDString;
  46. // 1. Save an artifact to storage and check it is stored.
  47. [self assertSetGetForStorage];
  48. // 2. Remove artifact.
  49. __auto_type setPromise = [self.storage setArtifact:nil forKey:keyID];
  50. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  51. XCTAssertNil(setPromise.value);
  52. XCTAssertNil(setPromise.error);
  53. // 3. Check it has been removed.
  54. __auto_type getPromise = [self.storage getArtifactForKey:keyID];
  55. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  56. XCTAssertNil(getPromise.value);
  57. XCTAssertNil(getPromise.error);
  58. }
  59. - (void)testSetAndGetPerApp {
  60. // Assert storages for apps with the same name can independently set/get artifact.
  61. [self assertIndependentSetGetForStoragesWithAppName1:self.appName
  62. appID1:@"app_id"
  63. appName2:self.appName
  64. appID2:@"app_id_2"];
  65. // Assert storages for apps with the same app ID can independently set/get artifact.
  66. [self assertIndependentSetGetForStoragesWithAppName1:@"app_1"
  67. appID1:self.appID
  68. appName2:@"app_2"
  69. appID2:self.appID];
  70. // Assert storages for apps with different info can independently set/get artifact.
  71. [self assertIndependentSetGetForStoragesWithAppName1:@"app_1"
  72. appID1:@"app_id_1"
  73. appName2:@"app_2"
  74. appID2:@"app_id_2"];
  75. }
  76. - (void)testSetArtifactForOneKeyGetForAnotherKey {
  77. // Set an artifact for a key.
  78. [self assertSetGetForStorage];
  79. // Try to get artifact for a different key.
  80. NSString *keyID = [NSUUID UUID].UUIDString;
  81. __auto_type getPromise = [self.storage getArtifactForKey:keyID];
  82. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  83. XCTAssertNil(getPromise.value);
  84. XCTAssertNil(getPromise.error);
  85. }
  86. - (void)testSetArtifactForNewKeyRemovesArtifactForOldKey {
  87. // 1. Store an artifact.
  88. NSString *oldKeyID = [self assertSetGetForStorage];
  89. // 2. Replace the artifact.
  90. NSString *newKeyID = [self assertSetGetForStorage];
  91. XCTAssertNotEqualObjects(oldKeyID, newKeyID);
  92. // 3. Check old artifact was removed.
  93. __auto_type getPromise = [self.storage getArtifactForKey:oldKeyID];
  94. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  95. XCTAssertNil(getPromise.value);
  96. XCTAssertNil(getPromise.error);
  97. }
  98. - (void)testGetArtifact_KeychainError {
  99. // 1. Set up storage mock.
  100. id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]);
  101. FIRAppAttestArtifactStorage *artifactStorage =
  102. [[FIRAppAttestArtifactStorage alloc] initWithAppName:self.appName
  103. appID:self.appID
  104. keychainStorage:mockKeychainStorage
  105. accessGroup:nil];
  106. // 2. Create and expect keychain error.
  107. NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil];
  108. OCMExpect([mockKeychainStorage getObjectForKey:[OCMArg any]
  109. objectClass:[OCMArg any]
  110. accessGroup:[OCMArg any]])
  111. .andReturn([FBLPromise resolvedWith:gulsKeychainError]);
  112. // 3. Get artifact and verify results.
  113. __auto_type getPromise = [artifactStorage getArtifactForKey:@"key"];
  114. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  115. XCTAssertNotNil(getPromise.error);
  116. XCTAssertEqualObjects(getPromise.error,
  117. [FIRAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]);
  118. // 4. Verify storage mock.
  119. OCMVerifyAll(mockKeychainStorage);
  120. }
  121. - (void)testSetArtifact_KeychainError {
  122. // 1. Set up storage mock.
  123. id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]);
  124. FIRAppAttestArtifactStorage *artifactStorage =
  125. [[FIRAppAttestArtifactStorage alloc] initWithAppName:self.appName
  126. appID:self.appID
  127. keychainStorage:mockKeychainStorage
  128. accessGroup:nil];
  129. // 2. Create and expect keychain error.
  130. NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil];
  131. OCMExpect([mockKeychainStorage setObject:[OCMArg any]
  132. forKey:[OCMArg any]
  133. accessGroup:[OCMArg any]])
  134. .andReturn([FBLPromise resolvedWith:gulsKeychainError]);
  135. // 3. Set artifact and verify results.
  136. NSData *artifact = [@"artifact" dataUsingEncoding:NSUTF8StringEncoding];
  137. __auto_type setPromise = [artifactStorage setArtifact:artifact forKey:@"key"];
  138. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  139. XCTAssertNotNil(setPromise.error);
  140. XCTAssertEqualObjects(setPromise.error,
  141. [FIRAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]);
  142. // 4. Verify storage mock.
  143. OCMVerifyAll(mockKeychainStorage);
  144. }
  145. - (void)testRemoveArtifact_KeychainError {
  146. // 1. Set up storage mock.
  147. id mockKeychainStorage = OCMClassMock([GULKeychainStorage class]);
  148. FIRAppAttestArtifactStorage *artifactStorage =
  149. [[FIRAppAttestArtifactStorage alloc] initWithAppName:self.appName
  150. appID:self.appID
  151. keychainStorage:mockKeychainStorage
  152. accessGroup:nil];
  153. // 2. Create and expect keychain error.
  154. NSError *gulsKeychainError = [NSError errorWithDomain:@"com.guls.keychain" code:-1 userInfo:nil];
  155. OCMExpect([mockKeychainStorage removeObjectForKey:[OCMArg any] accessGroup:[OCMArg any]])
  156. .andReturn([FBLPromise resolvedWith:gulsKeychainError]);
  157. // 3. Remove artifact and verify results.
  158. __auto_type setPromise = [artifactStorage setArtifact:nil forKey:@"key"];
  159. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  160. XCTAssertNotNil(setPromise.error);
  161. XCTAssertEqualObjects(setPromise.error,
  162. [FIRAppCheckErrorUtil keychainErrorWithError:gulsKeychainError]);
  163. // 4. Verify storage mock.
  164. OCMVerifyAll(mockKeychainStorage);
  165. }
  166. #pragma mark - Helpers
  167. /// Sets a random artifact for a random key and asserts it can be read.
  168. /// @return The random key ID used to set and get the artifact.
  169. - (NSString *)assertSetGetForStorage {
  170. NSData *artifactToSet = [[NSUUID UUID].UUIDString dataUsingEncoding:NSUTF8StringEncoding];
  171. NSString *keyID = [NSUUID UUID].UUIDString;
  172. __auto_type setPromise = [self.storage setArtifact:artifactToSet forKey:keyID];
  173. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  174. XCTAssertEqualObjects(setPromise.value, artifactToSet);
  175. XCTAssertNil(setPromise.error);
  176. __auto_type getPromise = [self.storage getArtifactForKey:keyID];
  177. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  178. XCTAssertEqualObjects(getPromise.value, artifactToSet);
  179. XCTAssertNil(getPromise.error);
  180. __weak __auto_type weakSelf = self;
  181. [self addTeardownBlock:^{
  182. // Cleanup storage.
  183. [weakSelf.storage setArtifact:nil forKey:keyID];
  184. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  185. }];
  186. return keyID;
  187. }
  188. - (void)assertIndependentSetGetForStoragesWithAppName1:(NSString *)appName1
  189. appID1:(NSString *)appID1
  190. appName2:(NSString *)appName2
  191. appID2:(NSString *)appID2 {
  192. NSString *keyID = [NSUUID UUID].UUIDString;
  193. // Create two storages.
  194. FIRAppAttestArtifactStorage *storage1 =
  195. [[FIRAppAttestArtifactStorage alloc] initWithAppName:appName1 appID:appID1 accessGroup:nil];
  196. FIRAppAttestArtifactStorage *storage2 =
  197. [[FIRAppAttestArtifactStorage alloc] initWithAppName:appName2 appID:appID2 accessGroup:nil];
  198. // 1. Independently set artifacts for the two storages.
  199. NSData *artifact1 = [@"app_attest_artifact1" dataUsingEncoding:NSUTF8StringEncoding];
  200. FBLPromise *setPromise1 = [storage1 setArtifact:artifact1 forKey:keyID];
  201. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  202. XCTAssertEqualObjects(setPromise1.value, artifact1);
  203. XCTAssertNil(setPromise1.error);
  204. NSData *artifact2 = [@"app_attest_artifact2" dataUsingEncoding:NSUTF8StringEncoding];
  205. __auto_type setPromise2 = [storage2 setArtifact:artifact2 forKey:keyID];
  206. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  207. XCTAssertEqualObjects(setPromise2.value, artifact2);
  208. XCTAssertNil(setPromise2.error);
  209. // 2. Get artifacts for the two storages.
  210. __auto_type getPromise1 = [storage1 getArtifactForKey:keyID];
  211. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  212. XCTAssertEqualObjects(getPromise1.value, artifact1);
  213. XCTAssertNil(getPromise1.error);
  214. __auto_type getPromise2 = [storage2 getArtifactForKey:keyID];
  215. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  216. XCTAssertEqualObjects(getPromise2.value, artifact2);
  217. XCTAssertNil(getPromise2.error);
  218. // 3. Assert that artifacts were set and retrieved independently of one another.
  219. XCTAssertNotEqualObjects(getPromise1.value, getPromise2.value);
  220. // Cleanup storages.
  221. [storage1 setArtifact:nil forKey:keyID];
  222. [storage2 setArtifact:nil forKey:keyID];
  223. XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
  224. }
  225. #endif // !TARGET_OS_MACCATALYST
  226. @end