GULKeychainUtils.m 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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 <GoogleUtilities/GULKeychainUtils.h>
  17. NSString *const kGULKeychainUtilsErrorDomain = @"com.gul.keychain.ErrorDomain";
  18. @implementation GULKeychainUtils
  19. + (nullable NSData *)getItemWithQuery:(NSDictionary *)query
  20. error:(NSError *_Nullable *_Nullable)outError {
  21. NSMutableDictionary *mutableQuery = [query mutableCopy];
  22. mutableQuery[(__bridge id)kSecReturnData] = @YES;
  23. mutableQuery[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
  24. CFDataRef result = NULL;
  25. OSStatus status =
  26. SecItemCopyMatching((__bridge CFDictionaryRef)mutableQuery, (CFTypeRef *)&result);
  27. if (status == errSecSuccess && result != NULL) {
  28. if (outError) {
  29. *outError = nil;
  30. }
  31. return (__bridge_transfer NSData *)result;
  32. }
  33. if (status == errSecItemNotFound) {
  34. if (outError) {
  35. *outError = nil;
  36. }
  37. } else {
  38. if (outError) {
  39. *outError = [self keychainErrorWithFunction:@"SecItemCopyMatching" status:status];
  40. }
  41. }
  42. return nil;
  43. }
  44. + (BOOL)setItem:(NSData *)item
  45. withQuery:(NSDictionary *)query
  46. error:(NSError *_Nullable *_Nullable)outError {
  47. NSData *existingItem = [self getItemWithQuery:query error:outError];
  48. if (outError && *outError) {
  49. return NO;
  50. }
  51. NSMutableDictionary *mutableQuery = [query mutableCopy];
  52. mutableQuery[(__bridge id)kSecAttrAccessible] =
  53. (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
  54. OSStatus status;
  55. if (!existingItem) {
  56. mutableQuery[(__bridge id)kSecValueData] = item;
  57. status = SecItemAdd((__bridge CFDictionaryRef)mutableQuery, NULL);
  58. } else {
  59. NSDictionary *attributes = @{(__bridge id)kSecValueData : item};
  60. status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes);
  61. }
  62. if (status == noErr) {
  63. if (outError) {
  64. *outError = nil;
  65. }
  66. return YES;
  67. }
  68. NSString *function = existingItem ? @"SecItemUpdate" : @"SecItemAdd";
  69. if (outError) {
  70. *outError = [self keychainErrorWithFunction:function status:status];
  71. }
  72. return NO;
  73. }
  74. + (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError {
  75. OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
  76. if (status == noErr || status == errSecItemNotFound) {
  77. if (outError) {
  78. *outError = nil;
  79. }
  80. return YES;
  81. }
  82. if (outError) {
  83. *outError = [self keychainErrorWithFunction:@"SecItemDelete" status:status];
  84. }
  85. return NO;
  86. }
  87. #pragma mark - Errors
  88. + (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status {
  89. NSString *failureReason = [NSString stringWithFormat:@"%@ (%li)", keychainFunction, (long)status];
  90. NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey : failureReason};
  91. return [NSError errorWithDomain:kGULKeychainUtilsErrorDomain code:0 userInfo:userInfo];
  92. }
  93. @end