No Description

FIRInstanceIDKeychain.m 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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(kFIRInstanceIDKeychainReadItemError,
  53. @"Info is not found in Keychain. OSStatus: %d. Keychain query: %@",
  54. (int)status, keychainQuery);
  55. }
  56. });
  57. return keyRef;
  58. }
  59. - (void)removeItemWithQuery:(NSDictionary *)keychainQuery
  60. handler:(void (^)(NSError *error))handler {
  61. dispatch_async(_keychainOperationQueue, ^{
  62. OSStatus status = SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
  63. if (status != noErr) {
  64. FIRInstanceIDLoggerDebug(
  65. kFIRInstanceIDKeychainDeleteItemError,
  66. @"Couldn't delete item from Keychain OSStatus: %d with the keychain query %@",
  67. (int)status, keychainQuery);
  68. }
  69. if (handler) {
  70. NSError *error;
  71. // When item is not found, it should NOT be considered as an error. The operation should
  72. // continue.
  73. if (status != noErr && status != errSecItemNotFound) {
  74. error = [NSError errorWithDomain:kFIRInstanceIDKeychainErrorDomain
  75. code:status
  76. userInfo:nil];
  77. }
  78. dispatch_async(dispatch_get_main_queue(), ^{
  79. handler(error);
  80. });
  81. }
  82. });
  83. }
  84. - (void)addItemWithQuery:(NSDictionary *)keychainQuery handler:(void (^)(NSError *))handler {
  85. dispatch_async(_keychainOperationQueue, ^{
  86. OSStatus status = SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
  87. if (handler) {
  88. NSError *error;
  89. if (status != noErr) {
  90. FIRInstanceIDLoggerWarning(kFIRInstanceIDKeychainAddItemError,
  91. @"Couldn't add item to Keychain OSStatus: %d", (int)status);
  92. error = [NSError errorWithDomain:kFIRInstanceIDKeychainErrorDomain
  93. code:status
  94. userInfo:nil];
  95. }
  96. dispatch_async(dispatch_get_main_queue(), ^{
  97. handler(error);
  98. });
  99. }
  100. });
  101. }
  102. - (FIRInstanceIDKeyPair *)generateKeyPairWithPrivateTag:(NSString *)privateTag
  103. publicTag:(NSString *)publicTag {
  104. // TODO(chliangGoogle) this is called by appInstanceID, which is an internal API used by other
  105. // Firebase teams, will see if we can make it async.
  106. NSData *publicTagData = [publicTag dataUsingEncoding:NSUTF8StringEncoding];
  107. NSData *privateTagData = [privateTag dataUsingEncoding:NSUTF8StringEncoding];
  108. NSDictionary *privateKeyAttr = @{
  109. (__bridge id)kSecAttrIsPermanent : @YES,
  110. (__bridge id)kSecAttrApplicationTag : privateTagData,
  111. (__bridge id)kSecAttrLabel : @"Firebase InstanceID Key Pair Private Key",
  112. (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
  113. };
  114. NSDictionary *publicKeyAttr = @{
  115. (__bridge id)kSecAttrIsPermanent : @YES,
  116. (__bridge id)kSecAttrApplicationTag : publicTagData,
  117. (__bridge id)kSecAttrLabel : @"Firebase InstanceID Key Pair Public Key",
  118. (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
  119. };
  120. NSDictionary *keyPairAttributes = @{
  121. (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeRSA,
  122. (__bridge id)kSecAttrLabel : @"Firebase InstanceID Key Pair",
  123. (__bridge id)kSecAttrKeySizeInBits : @(kRSA2048KeyPairSize),
  124. (__bridge id)kSecPrivateKeyAttrs : privateKeyAttr,
  125. (__bridge id)kSecPublicKeyAttrs : publicKeyAttr,
  126. };
  127. __block SecKeyRef privateKey = NULL;
  128. __block SecKeyRef publicKey = NULL;
  129. dispatch_sync(_keychainOperationQueue, ^{
  130. // SecKeyGeneratePair does not allow you to set kSetAttrAccessible on the keys. We need the keys
  131. // to be accessible even when the device is locked (i.e. app is woken up during a push
  132. // notification, or some background refresh).
  133. OSStatus status =
  134. SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttributes, &publicKey, &privateKey);
  135. if (status != noErr || publicKey == NULL || privateKey == NULL) {
  136. FIRInstanceIDLoggerWarning(kFIRInstanceIDKeychainCreateKeyPairError,
  137. @"Couldn't create keypair from Keychain OSStatus: %d",
  138. (int)status);
  139. }
  140. });
  141. // Extract the actual public and private key data from the Keychain
  142. NSDictionary *publicKeyDataQuery = FIRInstanceIDKeyPairQuery(publicTag, YES, YES);
  143. NSDictionary *privateKeyDataQuery = FIRInstanceIDKeyPairQuery(privateTag, YES, YES);
  144. NSData *publicKeyData = (__bridge NSData *)[self itemWithQuery:publicKeyDataQuery];
  145. NSData *privateKeyData = (__bridge NSData *)[self itemWithQuery:privateKeyDataQuery];
  146. return [[FIRInstanceIDKeyPair alloc] initWithPrivateKey:privateKey
  147. publicKey:publicKey
  148. publicKeyData:publicKeyData
  149. privateKeyData:privateKeyData];
  150. }
  151. @end