FIRInstanceIDKeyPairStoreTest.m 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * Copyright 2019 Google
  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 "Firebase/InstanceID/FIRInstanceIDBackupExcludedPlist.h"
  18. #import "Firebase/InstanceID/FIRInstanceIDConstants.h"
  19. #import "Firebase/InstanceID/FIRInstanceIDKeyPair.h"
  20. #import "Firebase/InstanceID/FIRInstanceIDKeychain.h"
  21. #import <OCMock/OCMock.h>
  22. #import "Firebase/InstanceID/FIRInstanceIDKeyPair.h"
  23. #import "Firebase/InstanceID/FIRInstanceIDKeyPairStore.h"
  24. #import "Firebase/InstanceID/FIRInstanceIDKeyPairUtilities.h"
  25. #import "Firebase/InstanceID/FIRInstanceIDUtilities.h"
  26. @interface FIRInstanceIDKeyPairStore (ExposedForTest)
  27. @property(nonatomic, readwrite, strong) FIRInstanceIDBackupExcludedPlist *plist;
  28. @property(nonatomic, readwrite, strong) FIRInstanceIDKeyPair *keyPair;
  29. + (NSString *)appIDKeyWithSubtype:(NSString *)subtype;
  30. + (NSString *)creationTimeKeyWithSubtype:(NSString *)subtype;
  31. - (FIRInstanceIDKeyPair *)generateAndSaveKeyWithSubtype:(NSString *)subtype
  32. creationTime:(int64_t)creationTime
  33. error:(NSError **)error;
  34. - (FIRInstanceIDKeyPair *)validCachedKeyPairWithSubtype:(NSString *)subtype error:(NSError **)error;
  35. + (NSString *)keyStoreFileName;
  36. - (void)migrateKeyPairCacheIfNeededWithHandler:(void (^)(NSError *error))handler;
  37. @end
  38. @interface FIRInstanceIDKeyPairStoreTest : XCTestCase
  39. @property(nonatomic, readwrite, strong) FIRInstanceIDKeyPairStore *keyPairStore;
  40. @end
  41. @implementation FIRInstanceIDKeyPairStoreTest
  42. - (void)setUp {
  43. [super setUp];
  44. id mockStoreClass = OCMClassMock([FIRInstanceIDKeyPairStore class]);
  45. [[[mockStoreClass stub] andReturn:@"com.google.iid-keypairmanager-test"] keyStoreFileName];
  46. _keyPairStore = [[FIRInstanceIDKeyPairStore alloc] init];
  47. }
  48. - (void)tearDown {
  49. [super tearDown];
  50. NSError *error = nil;
  51. [self.keyPairStore removeKeyPairCreationTimePlistWithError:&error];
  52. [self.keyPairStore deleteSavedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType handler:nil];
  53. }
  54. /**
  55. * The app identity generated should be 11 chars and start with k, l, m, n. It should
  56. * not have "=" as suffix since we do not allow wrapping.
  57. */
  58. - (void)testIdentity {
  59. NSError *error;
  60. FIRInstanceIDKeyPair *keyPair = [self.keyPairStore loadKeyPairWithError:&error];
  61. NSString *iid = FIRInstanceIDAppIdentity(keyPair);
  62. XCTAssertEqual(11, iid.length);
  63. XCTAssertFalse([iid hasSuffix:@"="]);
  64. }
  65. /**
  66. * All identities should be cleared if the associated keypair plist file is missing.
  67. * This indicates that the app is either a fresh install, or was removed and reinstalled.
  68. *
  69. * If/when iOS changes the behavior of the Keychain to also invalidate items when an app is
  70. * installed, this check will no longer be required (both the plist file and the keychain items
  71. * would be missing).
  72. */
  73. - (void)testIdentityIsInvalidatedWithMissingPlist {
  74. // Mock that the plist doesn't exist, and call the invalidation check. It should
  75. // trigger the identities to be deleted.
  76. id plistMock = OCMPartialMock(self.keyPairStore.plist);
  77. [[[plistMock stub] andReturnValue:OCMOCK_VALUE(NO)] doesFileExist];
  78. // Mock the keypair store, to check if key pair deletes are requested
  79. id storeMock = OCMPartialMock(self.keyPairStore);
  80. // Now trigger a possible invalidation.
  81. [self.keyPairStore invalidateKeyPairsIfNeeded];
  82. // Verify that delete was called
  83. OCMVerify([storeMock deleteSavedKeyPairWithSubtype:[OCMArg any] handler:[OCMArg any]]);
  84. }
  85. - (void)testMigrationWhenPlistExist {
  86. // Mock that the plist doesn't exist, and call the invalidation check. It should
  87. // trigger the identities to be deleted.
  88. id plistMock = OCMPartialMock(self.keyPairStore.plist);
  89. [[[plistMock stub] andReturnValue:OCMOCK_VALUE(YES)] doesFileExist];
  90. // Mock the keypair store, to check if key pair deletes are requested
  91. id storeMock = OCMPartialMock(self.keyPairStore);
  92. // Now trigger a possible invalidation.
  93. [self.keyPairStore invalidateKeyPairsIfNeeded];
  94. // Verify that delete was called
  95. OCMVerify([storeMock migrateKeyPairCacheIfNeededWithHandler:nil]);
  96. }
  97. /**
  98. * The app identity should change when deleted and regenerated.
  99. */
  100. - (void)testResetIdentity {
  101. XCTestExpectation *identityResetExpectation =
  102. [self expectationWithDescription:@"Identity should be reset"];
  103. NSError *error;
  104. FIRInstanceIDKeyPair *keyPair = [self.keyPairStore loadKeyPairWithError:&error];
  105. XCTAssertNil(error);
  106. NSString *iid1 = FIRInstanceIDAppIdentity(keyPair);
  107. [self.keyPairStore
  108. deleteSavedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType
  109. handler:^(NSError *error) {
  110. XCTAssertNil(error);
  111. [self.keyPairStore removeKeyPairCreationTimePlistWithError:&error];
  112. XCTAssertNil(error);
  113. // regenerate instance-id
  114. FIRInstanceIDKeyPair *keyPair =
  115. [self.keyPairStore loadKeyPairWithError:&error];
  116. XCTAssertNil(error);
  117. NSString *iid2 = FIRInstanceIDAppIdentity(keyPair);
  118. XCTAssertNotEqualObjects(iid1, iid2);
  119. [identityResetExpectation fulfill];
  120. }];
  121. [self waitForExpectationsWithTimeout:1 handler:nil];
  122. }
  123. /**
  124. * We should always cache a valid keypair.
  125. */
  126. - (void)testCachedKeyPair {
  127. NSError *error;
  128. FIRInstanceIDKeyPair *keyPair = [self.keyPairStore loadKeyPairWithError:&error];
  129. XCTAssertNil(error);
  130. NSString *iid1 = FIRInstanceIDAppIdentity(keyPair);
  131. // sleep for some time
  132. [NSThread sleepForTimeInterval:2.0];
  133. keyPair = [self.keyPairStore loadKeyPairWithError:&error];
  134. XCTAssertNil(error);
  135. NSString *iid2 = FIRInstanceIDAppIdentity(keyPair);
  136. XCTAssertTrue([self.keyPairStore hasCachedKeyPairs]);
  137. XCTAssertEqualObjects(iid1, iid2);
  138. }
  139. - (void)testAppIdentity {
  140. NSError *error;
  141. NSString *iid1 = [self.keyPairStore appIdentityWithError:&error];
  142. // sleep for some time
  143. [NSThread sleepForTimeInterval:2.0];
  144. NSString *iid2 = [self.keyPairStore appIdentityWithError:&error];
  145. XCTAssertEqualObjects(iid1, iid2);
  146. }
  147. /**
  148. * Test KeyPair cache. After generating a new keyPair requesting it from the cache
  149. * should be successfull and return the same keyPair.
  150. */
  151. - (void)testKeyPairCache {
  152. NSError *error;
  153. FIRInstanceIDKeyPair *keyPair1 =
  154. [self.keyPairStore generateAndSaveKeyWithSubtype:kFIRInstanceIDKeyPairSubType
  155. creationTime:FIRInstanceIDCurrentTimestampInSeconds()
  156. error:&error];
  157. XCTAssertNotNil(keyPair1);
  158. NSString *iid1 = FIRInstanceIDAppIdentity(keyPair1);
  159. [NSThread sleepForTimeInterval:2.0];
  160. FIRInstanceIDKeyPair *keyPair2 =
  161. [self.keyPairStore validCachedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType error:&error];
  162. XCTAssertNil(error);
  163. NSString *iid2 = FIRInstanceIDAppIdentity(keyPair2);
  164. XCTAssertEqualObjects(iid1, iid2);
  165. }
  166. /**
  167. * Test that if the Keychain preferences does not store any KeyPair, trying to
  168. * load one from the cache should return nil.
  169. */
  170. - (void)testInvalidKeyPair {
  171. NSError *error;
  172. FIRInstanceIDKeyPair *keyPair =
  173. [self.keyPairStore validCachedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType error:&error];
  174. XCTAssertFalse([keyPair isValid]);
  175. }
  176. /**
  177. * Test deleting the keyPair from Keychain preferences.
  178. */
  179. - (void)testDeleteKeyPair {
  180. XCTestExpectation *deleteKeyPairExpectation =
  181. [self expectationWithDescription:@"Keypair should be deleted"];
  182. NSError *error;
  183. [self.keyPairStore generateAndSaveKeyWithSubtype:kFIRInstanceIDKeyPairSubType
  184. creationTime:FIRInstanceIDCurrentTimestampInSeconds()
  185. error:&error];
  186. XCTAssertNil(error);
  187. [self.keyPairStore
  188. deleteSavedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType
  189. handler:^(NSError *error) {
  190. XCTAssertNil(error);
  191. FIRInstanceIDKeyPair *keyPair2 = [self.keyPairStore
  192. validCachedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType
  193. error:&error];
  194. XCTAssertNotNil(error);
  195. XCTAssertNil(keyPair2);
  196. [deleteKeyPairExpectation fulfill];
  197. }];
  198. [self waitForExpectationsWithTimeout:1 handler:nil];
  199. }
  200. @end