Nav apraksta

FIRInstanceIDKeychain.m 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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 "FIRInstanceIDKeychain.h"
  17. #import "FIRInstanceIDKeyPair.h"
  18. #import "FIRInstanceIDKeyPairUtilities.h"
  19. #import "FIRInstanceIDLogger.h"
  20. NSString *const kFIRInstanceIDKeychainErrorDomain = @"com.google.iid";
  21. static const NSUInteger kRSA2048KeyPairSize = 2048;
  22. @interface FIRInstanceIDKeychain () {
  23. dispatch_queue_t _keychainOperationQueue;
  24. }
  25. @end
  26. @implementation FIRInstanceIDKeychain
  27. + (instancetype)sharedInstance {
  28. static FIRInstanceIDKeychain *sharedInstance;
  29. static dispatch_once_t onceToken;
  30. dispatch_once(&onceToken, ^{
  31. sharedInstance = [[FIRInstanceIDKeychain alloc] init];
  32. });
  33. return sharedInstance;
  34. }
  35. - (instancetype)init {
  36. self = [super init];
  37. if (self) {
  38. _keychainOperationQueue =
  39. dispatch_queue_create("com.google.FirebaseInstanceID.Keychain", DISPATCH_QUEUE_SERIAL);
  40. }
  41. return self;
  42. }
  43. - (CFTypeRef)itemWithQuery:(NSDictionary *)keychainQuery {
  44. __block SecKeyRef keyRef = NULL;
  45. dispatch_sync(_keychainOperationQueue, ^{
  46. OSStatus status =
  47. SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyRef);
  48. if (status != noErr) {
  49. if (keyRef) {
  50. CFRelease(keyRef);
  51. }
  52. FIRInstanceIDLoggerDebug(
  53. kFIRInstanceIDKeychainReadItemError,
  54. @"No info is retrieved from Keychain OSStatus: %d with the keychain query %@",
  55. (int)status, keychainQuery);
  56. }
  57. });
  58. return keyRef;
  59. }
  60. - (void)removeItemWithQuery:(NSDictionary *)keychainQuery
  61. handler:(void (^)(NSError *error))handler {
  62. dispatch_async(_keychainOperationQueue, ^{
  63. OSStatus status = SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
  64. if (status != noErr) {
  65. FIRInstanceIDLoggerDebug(
  66. kFIRInstanceIDKeychainDeleteItemError,
  67. @"Couldn't delete item from Keychain OSStatus: %d with the keychain query %@",
  68. (int)status, keychainQuery);
  69. }
  70. if (handler) {
  71. NSError *error;
  72. // When item is not found, it should NOT be considered as an error. The operation should
  73. // continue.
  74. if (status != noErr && status != errSecItemNotFound) {
  75. error = [NSError errorWithDomain:kFIRInstanceIDKeychainErrorDomain
  76. code:status
  77. userInfo:nil];
  78. }
  79. dispatch_async(dispatch_get_main_queue(), ^{
  80. handler(error);
  81. });
  82. }
  83. });
  84. }
  85. - (void)addItemWithQuery:(NSDictionary *)keychainQuery handler:(void (^)(NSError *))handler {
  86. dispatch_async(_keychainOperationQueue, ^{
  87. OSStatus status = SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
  88. if (handler) {
  89. NSError *error;
  90. if (status != noErr) {
  91. FIRInstanceIDLoggerWarning(kFIRInstanceIDKeychainAddItemError,
  92. @"Couldn't add item to Keychain OSStatus: %d", (int)status);
  93. error = [NSError errorWithDomain:kFIRInstanceIDKeychainErrorDomain
  94. code:status
  95. userInfo:nil];
  96. }
  97. dispatch_async(dispatch_get_main_queue(), ^{
  98. handler(error);
  99. });
  100. }
  101. });
  102. }
  103. - (FIRInstanceIDKeyPair *)generateKeyPairWithPrivateTag:(NSString *)privateTag
  104. publicTag:(NSString *)publicTag {
  105. // TODO(chliangGoogle) this is called by appInstanceID, which is an internal API used by other
  106. // Firebase teams, will see if we can make it async.
  107. NSData *publicTagData = [publicTag dataUsingEncoding:NSUTF8StringEncoding];
  108. NSData *privateTagData = [privateTag dataUsingEncoding:NSUTF8StringEncoding];
  109. NSDictionary *privateKeyAttr = @{
  110. (__bridge id)kSecAttrIsPermanent : @YES,
  111. (__bridge id)kSecAttrApplicationTag : privateTagData,
  112. (__bridge id)kSecAttrLabel : @"Firebase InstanceID Key Pair Private Key",
  113. (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
  114. };
  115. NSDictionary *publicKeyAttr = @{
  116. (__bridge id)kSecAttrIsPermanent : @YES,
  117. (__bridge id)kSecAttrApplicationTag : publicTagData,
  118. (__bridge id)kSecAttrLabel : @"Firebase InstanceID Key Pair Public Key",
  119. (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
  120. };
  121. NSDictionary *keyPairAttributes = @{
  122. (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeRSA,
  123. (__bridge id)kSecAttrLabel : @"Firebase InstanceID Key Pair",
  124. (__bridge id)kSecAttrKeySizeInBits : @(kRSA2048KeyPairSize),
  125. (__bridge id)kSecPrivateKeyAttrs : privateKeyAttr,
  126. (__bridge id)kSecPublicKeyAttrs : publicKeyAttr,
  127. };
  128. __block SecKeyRef privateKey = NULL;
  129. __block SecKeyRef publicKey = NULL;
  130. dispatch_sync(_keychainOperationQueue, ^{
  131. // SecKeyGeneratePair does not allow you to set kSetAttrAccessible on the keys. We need the keys
  132. // to be accessible even when the device is locked (i.e. app is woken up during a push
  133. // notification, or some background refresh).
  134. OSStatus status =
  135. SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttributes, &publicKey, &privateKey);
  136. if (status != noErr || publicKey == NULL || privateKey == NULL) {
  137. FIRInstanceIDLoggerWarning(kFIRInstanceIDKeychainCreateKeyPairError,
  138. @"Couldn't create keypair from Keychain OSStatus: %d",
  139. (int)status);
  140. }
  141. });
  142. // Extract the actual public and private key data from the Keychain
  143. NSDictionary *publicKeyDataQuery = FIRInstanceIDKeyPairQuery(publicTag, YES, YES);
  144. NSDictionary *privateKeyDataQuery = FIRInstanceIDKeyPairQuery(privateTag, YES, YES);
  145. NSData *publicKeyData = (__bridge NSData *)[self itemWithQuery:publicKeyDataQuery];
  146. NSData *privateKeyData = (__bridge NSData *)[self itemWithQuery:privateKeyDataQuery];
  147. return [[FIRInstanceIDKeyPair alloc] initWithPrivateKey:privateKey
  148. publicKey:publicKey
  149. publicKeyData:publicKeyData
  150. privateKeyData:privateKeyData];
  151. }
  152. @end