SSKeychain.m 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. //
  2. // SSKeychain.m
  3. // SSToolkit
  4. //
  5. // Created by Sam Soffes on 5/19/10.
  6. // Copyright (c) 2009-2011 Sam Soffes. All rights reserved.
  7. //
  8. #import "SSKeychain.h"
  9. NSString *const kSSKeychainErrorDomain = @"com.samsoffes.sskeychain";
  10. NSString *const kSSKeychainAccountKey = @"acct";
  11. NSString *const kSSKeychainCreatedAtKey = @"cdat";
  12. NSString *const kSSKeychainClassKey = @"labl";
  13. NSString *const kSSKeychainDescriptionKey = @"desc";
  14. NSString *const kSSKeychainLabelKey = @"labl";
  15. NSString *const kSSKeychainLastModifiedKey = @"mdat";
  16. NSString *const kSSKeychainWhereKey = @"svce";
  17. #if __IPHONE_4_0 && TARGET_OS_IPHONE
  18. CFTypeRef SSKeychainAccessibilityType = NULL;
  19. #endif
  20. @interface SSKeychain ()
  21. + (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account;
  22. @end
  23. @implementation SSKeychain
  24. #pragma mark - Getting Accounts
  25. + (NSArray *)allAccounts {
  26. return [self accountsForService:nil error:nil];
  27. }
  28. + (NSArray *)allAccounts:(NSError **)error {
  29. return [self accountsForService:nil error:error];
  30. }
  31. + (NSArray *)accountsForService:(NSString *)service {
  32. return [self accountsForService:service error:nil];
  33. }
  34. + (NSArray *)accountsForService:(NSString *)service error:(NSError **)error {
  35. OSStatus status = SSKeychainErrorBadArguments;
  36. NSMutableDictionary *query = [self _queryForService:service account:nil];
  37. #if __has_feature(objc_arc)
  38. [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
  39. [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
  40. #else
  41. [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
  42. [query setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit];
  43. #endif
  44. CFTypeRef result = NULL;
  45. #if __has_feature(objc_arc)
  46. status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
  47. #else
  48. status = SecItemCopyMatching((CFDictionaryRef)query, &result);
  49. #endif
  50. if (status != noErr && error != NULL) {
  51. *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
  52. return nil;
  53. }
  54. #if __has_feature(objc_arc)
  55. return (__bridge_transfer NSArray *)result;
  56. #else
  57. return [(NSArray *)result autorelease];
  58. #endif
  59. }
  60. #pragma mark - Getting Passwords
  61. + (NSString *)passwordForService:(NSString *)service account:(NSString *)account {
  62. return [self passwordForService:service account:account error:nil];
  63. }
  64. + (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
  65. NSData *data = [self passwordDataForService:service account:account error:error];
  66. if (data.length > 0) {
  67. NSString *string = [[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding];
  68. #if !__has_feature(objc_arc)
  69. [string autorelease];
  70. #endif
  71. return string;
  72. }
  73. return nil;
  74. }
  75. + (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account {
  76. return [self passwordDataForService:service account:account error:nil];
  77. }
  78. + (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
  79. OSStatus status = SSKeychainErrorBadArguments;
  80. if (!service || !account) {
  81. if (error) {
  82. *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
  83. }
  84. return nil;
  85. }
  86. CFTypeRef result = NULL;
  87. NSMutableDictionary *query = [self _queryForService:service account:account];
  88. #if __has_feature(objc_arc)
  89. [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
  90. [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
  91. status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
  92. #else
  93. [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
  94. [query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
  95. status = SecItemCopyMatching((CFDictionaryRef)query, &result);
  96. #endif
  97. if (status != noErr && error != NULL) {
  98. *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
  99. return nil;
  100. }
  101. #if __has_feature(objc_arc)
  102. return (__bridge_transfer NSData *)result;
  103. #else
  104. return [(NSData *)result autorelease];
  105. #endif
  106. }
  107. #pragma mark - Deleting Passwords
  108. + (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account {
  109. return [self deletePasswordForService:service account:account error:nil];
  110. }
  111. + (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error {
  112. OSStatus status = SSKeychainErrorBadArguments;
  113. if (service && account) {
  114. NSMutableDictionary *query = [self _queryForService:service account:account];
  115. #if __has_feature(objc_arc)
  116. status = SecItemDelete((__bridge CFDictionaryRef)query);
  117. #else
  118. status = SecItemDelete((CFDictionaryRef)query);
  119. #endif
  120. }
  121. if (status != noErr && error != NULL) {
  122. *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
  123. }
  124. return (status == noErr);
  125. }
  126. #pragma mark - Setting Passwords
  127. + (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account {
  128. return [self setPassword:password forService:service account:account error:nil];
  129. }
  130. + (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error {
  131. NSData *data = [password dataUsingEncoding:NSUTF8StringEncoding];
  132. return [self setPasswordData:data forService:service account:account error:error];
  133. }
  134. + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account {
  135. return [self setPasswordData:password forService:service account:account error:nil];
  136. }
  137. + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error {
  138. OSStatus status = SSKeychainErrorBadArguments;
  139. if (password && service && account) {
  140. [self deletePasswordForService:service account:account];
  141. NSMutableDictionary *query = [self _queryForService:service account:account];
  142. #if __has_feature(objc_arc)
  143. [query setObject:password forKey:(__bridge id)kSecValueData];
  144. #else
  145. [query setObject:password forKey:(id)kSecValueData];
  146. #endif
  147. #if __IPHONE_4_0 && TARGET_OS_IPHONE
  148. if (SSKeychainAccessibilityType) {
  149. #if __has_feature(objc_arc)
  150. [query setObject:(id)[self accessibilityType] forKey:(__bridge id)kSecAttrAccessible];
  151. #else
  152. [query setObject:(id)[self accessibilityType] forKey:(id)kSecAttrAccessible];
  153. #endif
  154. }
  155. #endif
  156. #if __has_feature(objc_arc)
  157. status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
  158. #else
  159. status = SecItemAdd((CFDictionaryRef)query, NULL);
  160. #endif
  161. }
  162. if (status != noErr && error != NULL) {
  163. *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil];
  164. }
  165. return (status == noErr);
  166. }
  167. #pragma mark - Configuration
  168. #if __IPHONE_4_0 && TARGET_OS_IPHONE
  169. + (CFTypeRef)accessibilityType {
  170. return SSKeychainAccessibilityType;
  171. }
  172. + (void)setAccessibilityType:(CFTypeRef)accessibilityType {
  173. CFRetain(accessibilityType);
  174. if (SSKeychainAccessibilityType) {
  175. CFRelease(SSKeychainAccessibilityType);
  176. }
  177. SSKeychainAccessibilityType = accessibilityType;
  178. }
  179. #endif
  180. #pragma mark - Private
  181. + (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account {
  182. NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3];
  183. #if __has_feature(objc_arc)
  184. [dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
  185. #else
  186. [dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
  187. #endif
  188. if (service) {
  189. #if __has_feature(objc_arc)
  190. [dictionary setObject:service forKey:(__bridge id)kSecAttrService];
  191. #else
  192. [dictionary setObject:service forKey:(id)kSecAttrService];
  193. #endif
  194. }
  195. if (account) {
  196. #if __has_feature(objc_arc)
  197. [dictionary setObject:account forKey:(__bridge id)kSecAttrAccount];
  198. #else
  199. [dictionary setObject:account forKey:(id)kSecAttrAccount];
  200. #endif
  201. }
  202. return dictionary;
  203. }
  204. @end