FIRInstanceIDKeyPairMigrationTest.m 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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(atomic, readwrite, strong) FIRInstanceIDKeyPair *keyPair;
  29. BOOL FIRInstanceIDHasMigratedKeyPair(NSString *legacyPublicKeyTag, NSString *newPublicKeyTag);
  30. NSString *FIRInstanceIDLegacyPublicTagWithSubtype(NSString *subtype);
  31. NSString *FIRInstanceIDLegacyPrivateTagWithSubtype(NSString *subtype);
  32. NSString *FIRInstanceIDPublicTagWithSubtype(NSString *subtype);
  33. NSString *FIRInstanceIDPrivateTagWithSubtype(NSString *subtype);
  34. + (FIRInstanceIDKeyPair *)keyPairForPrivateKeyTag:(NSString *)privateKeyTag
  35. publicKeyTag:(NSString *)publicKeyTag
  36. error:(NSError *__autoreleasing *)error;
  37. + (void)deleteKeyPairWithPrivateTag:(NSString *)privateTag
  38. publicTag:(NSString *)publicTag
  39. handler:(void (^)(NSError *))handler;
  40. - (void)migrateKeyPairCacheIfNeededWithHandler:(void (^)(NSError *error))handler;
  41. + (NSString *)keyStoreFileName;
  42. - (void)updateKeyRef:(SecKeyRef)keyRef
  43. withTag:(NSString *)tag
  44. handler:(void (^)(NSError *error))handler;
  45. @end
  46. // Need to separate the tests from FIRInstanceIDKeyPairStoreTest for separate keychain operations
  47. @interface FIRInstanceIDKeyPairMigrationTest : XCTestCase
  48. @property(nonatomic, readwrite, strong) FIRInstanceIDKeyPairStore *keyPairStore;
  49. @end
  50. @implementation FIRInstanceIDKeyPairMigrationTest
  51. - (void)setUp {
  52. [super setUp];
  53. id mockStoreClass = OCMClassMock([FIRInstanceIDKeyPairStore class]);
  54. [[[mockStoreClass stub] andReturn:@"com.google.iid-keypairmanager-test"] keyStoreFileName];
  55. _keyPairStore = [[FIRInstanceIDKeyPairStore alloc] init];
  56. }
  57. - (void)tearDown {
  58. [super tearDown];
  59. NSError *error = nil;
  60. [self.keyPairStore removeKeyPairCreationTimePlistWithError:&error];
  61. // TODO: Real FIRInstanceIDKeychain should not be used for the tests, it should be mocked instead.
  62. // Drain Keychain private queue before exiting.
  63. [[FIRInstanceIDKeychain sharedInstance] itemWithQuery:@{}];
  64. }
  65. - (void)testMigrationDataIfLegacyKeyPairsNotExist {
  66. NSString *legacyPublicKeyTag =
  67. FIRInstanceIDLegacyPublicTagWithSubtype(kFIRInstanceIDKeyPairSubType);
  68. NSString *publicKeyTag = FIRInstanceIDPublicTagWithSubtype(kFIRInstanceIDKeyPairSubType);
  69. XCTAssertFalse(FIRInstanceIDHasMigratedKeyPair(legacyPublicKeyTag, publicKeyTag));
  70. NSString *legacyPrivateKeyTag =
  71. FIRInstanceIDLegacyPrivateTagWithSubtype(kFIRInstanceIDKeyPairSubType);
  72. NSError *error;
  73. FIRInstanceIDKeyPair *keyPair =
  74. [FIRInstanceIDKeyPairStore keyPairForPrivateKeyTag:legacyPrivateKeyTag
  75. publicKeyTag:legacyPublicKeyTag
  76. error:&error];
  77. XCTAssertFalse([keyPair isValid]);
  78. }
  79. - (void)testMigrationIfLegacyKeyPairsExist {
  80. // Legacy keypair should only exist in iOS/tvOS.
  81. #if TARGET_OS_IOS || TARGET_OS_TV
  82. XCTestExpectation *migrationCompleteExpectation =
  83. [self expectationWithDescription:@"migration should be done"];
  84. XCTestExpectation *deletionCompleteExpectation =
  85. [self expectationWithDescription:@"keychain should be cleared"];
  86. // create legacy key pairs
  87. NSString *legacyPublicKeyTag =
  88. FIRInstanceIDLegacyPublicTagWithSubtype(kFIRInstanceIDKeyPairSubType);
  89. NSString *legacyPrivateKeyTag =
  90. FIRInstanceIDLegacyPrivateTagWithSubtype(kFIRInstanceIDKeyPairSubType);
  91. FIRInstanceIDKeyPair *keyPair =
  92. [[FIRInstanceIDKeychain sharedInstance] generateKeyPairWithPrivateTag:legacyPrivateKeyTag
  93. publicTag:legacyPublicKeyTag];
  94. XCTAssertTrue([keyPair isValid]);
  95. NSError *error;
  96. NSString *publicKeyTag = FIRInstanceIDPublicTagWithSubtype(kFIRInstanceIDKeyPairSubType);
  97. NSString *privateKeyTag = FIRInstanceIDPrivateTagWithSubtype(kFIRInstanceIDKeyPairSubType);
  98. XCTAssertFalse(FIRInstanceIDHasMigratedKeyPair(legacyPublicKeyTag, publicKeyTag));
  99. FIRInstanceIDKeyPair *keyPair1 =
  100. [FIRInstanceIDKeyPairStore keyPairForPrivateKeyTag:legacyPrivateKeyTag
  101. publicKeyTag:legacyPublicKeyTag
  102. error:&error];
  103. XCTAssertTrue([keyPair1 isValid]);
  104. [self.keyPairStore migrateKeyPairCacheIfNeededWithHandler:^(NSError *error) {
  105. XCTAssertNil(error);
  106. XCTAssertTrue(FIRInstanceIDHasMigratedKeyPair(legacyPublicKeyTag, publicKeyTag));
  107. FIRInstanceIDKeyPair *keyPair2 =
  108. [FIRInstanceIDKeyPairStore keyPairForPrivateKeyTag:privateKeyTag
  109. publicKeyTag:publicKeyTag
  110. error:&error];
  111. XCTAssertTrue([keyPair2 isValid]);
  112. XCTAssertEqualObjects(keyPair.publicKeyData, keyPair2.publicKeyData);
  113. XCTAssertEqualObjects(keyPair.privateKeyData, keyPair2.privateKeyData);
  114. // Clear the keychain data after tests
  115. [FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:legacyPrivateKeyTag
  116. publicTag:legacyPublicKeyTag
  117. handler:^(NSError *error) {
  118. XCTAssertNil(error);
  119. [migrationCompleteExpectation fulfill];
  120. }];
  121. [FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:privateKeyTag
  122. publicTag:publicKeyTag
  123. handler:^(NSError *error) {
  124. XCTAssertNil(error);
  125. [deletionCompleteExpectation fulfill];
  126. }];
  127. }];
  128. [self waitForExpectations:@[ migrationCompleteExpectation, deletionCompleteExpectation ]
  129. timeout:10.0];
  130. #endif
  131. }
  132. - (void)testUpdateKeyRefWithTagRetainsAndReleasesKeyRef {
  133. #if TARGET_OS_IOS || TARGET_OS_TV
  134. __weak id weakKeyRef;
  135. // Use a local autorelease pool to make sure any autorelease objects allocated will be released.
  136. @autoreleasepool {
  137. SecKeyRef keyRef = [self generateKeyRef];
  138. weakKeyRef = (__bridge id)(keyRef);
  139. XCTestExpectation *completionExpectation =
  140. [self expectationWithDescription:@"completionExpectation"];
  141. [self.keyPairStore updateKeyRef:keyRef
  142. withTag:@"test"
  143. handler:^(NSError *error) {
  144. [completionExpectation fulfill];
  145. }];
  146. // Release locally allocated CoreFoundation object.
  147. CFRelease(keyRef);
  148. }
  149. // Should be still alive until execution finished
  150. XCTAssertNotNil(weakKeyRef);
  151. [self waitForExpectationsWithTimeout:0.5 handler:NULL];
  152. // Should be released once finished
  153. // The check below is flaky for build under DEBUG (petentially due to ARC specifics).
  154. // Comment it so far as not-so-important one.
  155. // XCTAssertNil(weakKeyRef);
  156. #endif
  157. }
  158. - (SecKeyRef)generateKeyRef {
  159. NSDictionary *keyAttributes = @{(__bridge id)kSecAttrIsPermanent : @YES};
  160. NSDictionary *keyPairAttributes = @{
  161. (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeRSA,
  162. (__bridge id)kSecAttrLabel : @"[FIRInstanceIDKeyPairMigrationTest generateKeyRef]",
  163. (__bridge id)kSecAttrKeySizeInBits : @(2048),
  164. (__bridge id)kSecPrivateKeyAttrs : keyAttributes,
  165. (__bridge id)kSecPublicKeyAttrs : keyAttributes,
  166. };
  167. SecKeyRef publicKey = NULL;
  168. SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttributes, &publicKey, NULL);
  169. return publicKey;
  170. }
  171. @end