UICKeyChainStore.m 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392
  1. //
  2. // UICKeyChainStore.m
  3. // UICKeyChainStore
  4. //
  5. // Created by Kishikawa Katsumi on 11/11/20.
  6. // Copyright (c) 2011 Kishikawa Katsumi. All rights reserved.
  7. //
  8. #import "UICKeyChainStore.h"
  9. NSString * const UICKeyChainStoreErrorDomain = @"com.kishikawakatsumi.uickeychainstore";
  10. static NSString *_defaultService;
  11. @interface UICKeyChainStore ()
  12. @end
  13. @implementation UICKeyChainStore
  14. + (NSString *)defaultService
  15. {
  16. if (!_defaultService) {
  17. _defaultService = [[NSBundle mainBundle] bundleIdentifier] ?: @"";
  18. }
  19. return _defaultService;
  20. }
  21. + (void)setDefaultService:(NSString *)defaultService
  22. {
  23. _defaultService = defaultService;
  24. }
  25. #pragma mark -
  26. + (UICKeyChainStore *)keyChainStore
  27. {
  28. return [[self alloc] initWithService:nil accessGroup:nil];
  29. }
  30. + (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service
  31. {
  32. return [[self alloc] initWithService:service accessGroup:nil];
  33. }
  34. + (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service accessGroup:(NSString *)accessGroup
  35. {
  36. return [[self alloc] initWithService:service accessGroup:accessGroup];
  37. }
  38. #pragma mark -
  39. + (UICKeyChainStore *)keyChainStoreWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType
  40. {
  41. return [[self alloc] initWithServer:server protocolType:protocolType authenticationType:UICKeyChainStoreAuthenticationTypeDefault];
  42. }
  43. + (UICKeyChainStore *)keyChainStoreWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType authenticationType:(UICKeyChainStoreAuthenticationType)authenticationType
  44. {
  45. return [[self alloc] initWithServer:server protocolType:protocolType authenticationType:authenticationType];
  46. }
  47. #pragma mark -
  48. - (instancetype)init
  49. {
  50. return [self initWithService:[self.class defaultService] accessGroup:nil];
  51. }
  52. - (instancetype)initWithService:(NSString *)service
  53. {
  54. return [self initWithService:service accessGroup:nil];
  55. }
  56. - (instancetype)initWithService:(NSString *)service accessGroup:(NSString *)accessGroup
  57. {
  58. self = [super init];
  59. if (self) {
  60. _itemClass = UICKeyChainStoreItemClassGenericPassword;
  61. if (!service) {
  62. service = [self.class defaultService];
  63. }
  64. _service = service.copy;
  65. _accessGroup = accessGroup.copy;
  66. [self commonInit];
  67. }
  68. return self;
  69. }
  70. #pragma mark -
  71. - (instancetype)initWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType
  72. {
  73. return [self initWithServer:server protocolType:protocolType authenticationType:UICKeyChainStoreAuthenticationTypeDefault];
  74. }
  75. - (instancetype)initWithServer:(NSURL *)server protocolType:(UICKeyChainStoreProtocolType)protocolType authenticationType:(UICKeyChainStoreAuthenticationType)authenticationType
  76. {
  77. self = [super init];
  78. if (self) {
  79. _itemClass = UICKeyChainStoreItemClassInternetPassword;
  80. _server = server.copy;
  81. _protocolType = protocolType;
  82. _authenticationType = authenticationType;
  83. [self commonInit];
  84. }
  85. return self;
  86. }
  87. #pragma mark -
  88. - (void)commonInit
  89. {
  90. _accessibility = UICKeyChainStoreAccessibilityAfterFirstUnlock;
  91. }
  92. #pragma mark -
  93. + (NSString *)stringForKey:(NSString *)key
  94. {
  95. return [self stringForKey:key service:nil accessGroup:nil error:nil];
  96. }
  97. + (NSString *)stringForKey:(NSString *)key error:(NSError *__autoreleasing *)error
  98. {
  99. return [self stringForKey:key service:nil accessGroup:nil error:error];
  100. }
  101. + (NSString *)stringForKey:(NSString *)key service:(NSString *)service
  102. {
  103. return [self stringForKey:key service:service accessGroup:nil error:nil];
  104. }
  105. + (NSString *)stringForKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error
  106. {
  107. return [self stringForKey:key service:service accessGroup:nil error:error];
  108. }
  109. + (NSString *)stringForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup
  110. {
  111. return [self stringForKey:key service:service accessGroup:accessGroup error:nil];
  112. }
  113. + (NSString *)stringForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error
  114. {
  115. if (!key) {
  116. NSError *e = [self argumentError:NSLocalizedString(@"the key must not to be nil", nil)];
  117. if (error) {
  118. *error = e;
  119. }
  120. return nil;
  121. }
  122. if (!service) {
  123. service = [self defaultService];
  124. }
  125. UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup];
  126. return [keychain stringForKey:key error:error];
  127. }
  128. #pragma mark -
  129. + (BOOL)setString:(NSString *)value forKey:(NSString *)key
  130. {
  131. return [self setString:value forKey:key service:nil accessGroup:nil genericAttribute:nil error:nil];
  132. }
  133. + (BOOL)setString:(NSString *)value forKey:(NSString *)key error:(NSError *__autoreleasing *)error
  134. {
  135. return [self setString:value forKey:key service:nil accessGroup:nil genericAttribute:nil error:error];
  136. }
  137. + (BOOL)setString:(NSString *)value forKey:(NSString *)key genericAttribute:(id)genericAttribute
  138. {
  139. return [self setString:value forKey:key service:nil accessGroup:nil genericAttribute:genericAttribute error:nil];
  140. }
  141. + (BOOL)setString:(NSString *)value forKey:(NSString *)key genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error
  142. {
  143. return [self setString:value forKey:key service:nil accessGroup:nil genericAttribute:genericAttribute error:error];
  144. }
  145. + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service
  146. {
  147. return [self setString:value forKey:key service:service accessGroup:nil genericAttribute:nil error:nil];
  148. }
  149. + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error
  150. {
  151. return [self setString:value forKey:key service:service accessGroup:nil genericAttribute:nil error:error];
  152. }
  153. + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service genericAttribute:(id)genericAttribute
  154. {
  155. return [self setString:value forKey:key service:service accessGroup:nil genericAttribute:genericAttribute error:nil];
  156. }
  157. + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error
  158. {
  159. return [self setString:value forKey:key service:service accessGroup:nil genericAttribute:genericAttribute error:error];
  160. }
  161. + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup
  162. {
  163. return [self setString:value forKey:key service:service accessGroup:accessGroup genericAttribute:nil error:nil];
  164. }
  165. + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error
  166. {
  167. return [self setString:value forKey:key service:service accessGroup:accessGroup genericAttribute:nil error:error];
  168. }
  169. + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup genericAttribute:(id)genericAttribute
  170. {
  171. return [self setString:value forKey:key service:service accessGroup:accessGroup genericAttribute:genericAttribute error:nil];
  172. }
  173. + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error
  174. {
  175. if (!value) {
  176. return [self removeItemForKey:key service:service accessGroup:accessGroup error:error];
  177. }
  178. NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
  179. if (data) {
  180. return [self setData:data forKey:key service:service accessGroup:accessGroup genericAttribute:genericAttribute error:error];
  181. }
  182. NSError *e = [self conversionError:NSLocalizedString(@"failed to convert string to data", nil)];
  183. if (error) {
  184. *error = e;
  185. }
  186. return NO;
  187. }
  188. #pragma mark -
  189. + (NSData *)dataForKey:(NSString *)key
  190. {
  191. return [self dataForKey:key service:nil accessGroup:nil error:nil];
  192. }
  193. + (NSData *)dataForKey:(NSString *)key error:(NSError *__autoreleasing *)error
  194. {
  195. return [self dataForKey:key service:nil accessGroup:nil error:error];
  196. }
  197. + (NSData *)dataForKey:(NSString *)key service:(NSString *)service
  198. {
  199. return [self dataForKey:key service:service accessGroup:nil error:nil];
  200. }
  201. + (NSData *)dataForKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error
  202. {
  203. return [self dataForKey:key service:service accessGroup:nil error:error];
  204. }
  205. + (NSData *)dataForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup
  206. {
  207. return [self dataForKey:key service:service accessGroup:accessGroup error:nil];
  208. }
  209. + (NSData *)dataForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error
  210. {
  211. if (!key) {
  212. NSError *e = [self argumentError:NSLocalizedString(@"the key must not to be nil", nil)];
  213. if (error) {
  214. *error = e;
  215. }
  216. return nil;
  217. }
  218. if (!service) {
  219. service = [self defaultService];
  220. }
  221. UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup];
  222. return [keychain dataForKey:key error:error];
  223. }
  224. #pragma mark -
  225. + (BOOL)setData:(NSData *)data forKey:(NSString *)key
  226. {
  227. return [self setData:data forKey:key service:nil accessGroup:nil genericAttribute:nil error:nil];
  228. }
  229. + (BOOL)setData:(NSData *)data forKey:(NSString *)key error:(NSError *__autoreleasing *)error
  230. {
  231. return [self setData:data forKey:key service:nil accessGroup:nil genericAttribute:nil error:error];
  232. }
  233. + (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute
  234. {
  235. return [self setData:data forKey:key service:nil accessGroup:nil genericAttribute:genericAttribute error:nil];
  236. }
  237. + (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error
  238. {
  239. return [self setData:data forKey:key service:nil accessGroup:nil genericAttribute:genericAttribute error:error];
  240. }
  241. + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service
  242. {
  243. return [self setData:data forKey:key service:service accessGroup:nil genericAttribute:nil error:nil];
  244. }
  245. + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error
  246. {
  247. return [self setData:data forKey:key service:service accessGroup:nil genericAttribute:nil error:error];
  248. }
  249. + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service genericAttribute:(id)genericAttribute
  250. {
  251. return [self setData:data forKey:key service:service accessGroup:nil genericAttribute:genericAttribute error:nil];
  252. }
  253. + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error
  254. {
  255. return [self setData:data forKey:key service:service accessGroup:nil genericAttribute:genericAttribute error:error];
  256. }
  257. + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup
  258. {
  259. return [self setData:data forKey:key service:service accessGroup:accessGroup genericAttribute:nil error:nil];
  260. }
  261. + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error
  262. {
  263. return [self setData:data forKey:key service:service accessGroup:accessGroup genericAttribute:nil error:error];
  264. }
  265. + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup genericAttribute:(id)genericAttribute
  266. {
  267. return [self setData:data forKey:key service:service accessGroup:accessGroup genericAttribute:genericAttribute error:nil];
  268. }
  269. + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error
  270. {
  271. if (!key) {
  272. NSError *e = [self argumentError:NSLocalizedString(@"the key must not to be nil", nil)];
  273. if (error) {
  274. *error = e;
  275. }
  276. return NO;
  277. }
  278. if (!service) {
  279. service = [self defaultService];
  280. }
  281. UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup];
  282. return [keychain setData:data forKey:key genericAttribute:genericAttribute];
  283. }
  284. #pragma mark -
  285. - (BOOL)contains:(NSString *)key
  286. {
  287. NSMutableDictionary *query = [self query];
  288. query[(__bridge __strong id)kSecAttrAccount] = key;
  289. OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
  290. return status == errSecSuccess;
  291. }
  292. #pragma mark -
  293. - (NSString *)stringForKey:(id)key
  294. {
  295. return [self stringForKey:key error:nil];
  296. }
  297. - (NSString *)stringForKey:(id)key error:(NSError *__autoreleasing *)error
  298. {
  299. NSData *data = [self dataForKey:key error:error];
  300. if (data) {
  301. NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  302. if (string) {
  303. return string;
  304. }
  305. NSError *e = [self.class conversionError:NSLocalizedString(@"failed to convert data to string", nil)];
  306. if (error) {
  307. *error = e;
  308. }
  309. return nil;
  310. }
  311. return nil;
  312. }
  313. #pragma mark -
  314. - (BOOL)setString:(NSString *)string forKey:(NSString *)key
  315. {
  316. return [self setString:string forKey:key genericAttribute:nil label:nil comment:nil error:nil];
  317. }
  318. - (BOOL)setString:(NSString *)string forKey:(NSString *)key error:(NSError *__autoreleasing *)error
  319. {
  320. return [self setString:string forKey:key genericAttribute:nil label:nil comment:nil error:error];
  321. }
  322. - (BOOL)setString:(NSString *)string forKey:(NSString *)key genericAttribute:(id)genericAttribute
  323. {
  324. return [self setString:string forKey:key genericAttribute:genericAttribute label:nil comment:nil error:nil];
  325. }
  326. - (BOOL)setString:(NSString *)string forKey:(NSString *)key genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error
  327. {
  328. return [self setString:string forKey:key genericAttribute:genericAttribute label:nil comment:nil error:error];
  329. }
  330. - (BOOL)setString:(NSString *)string forKey:(NSString *)key label:(NSString *)label comment:(NSString *)comment
  331. {
  332. return [self setString:string forKey:key genericAttribute:nil label:label comment:comment error:nil];
  333. }
  334. - (BOOL)setString:(NSString *)string forKey:(NSString *)key label:(NSString *)label comment:(NSString *)comment error:(NSError *__autoreleasing *)error
  335. {
  336. return [self setString:string forKey:key genericAttribute:nil label:label comment:comment error:error];
  337. }
  338. - (BOOL)setString:(NSString *)string forKey:(NSString *)key genericAttribute:(id)genericAttribute label:(NSString *)label comment:(NSString *)comment error:(NSError *__autoreleasing *)error
  339. {
  340. if (!string) {
  341. return [self removeItemForKey:key error:error];
  342. }
  343. NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
  344. if (data) {
  345. return [self setData:data forKey:key genericAttribute:genericAttribute label:label comment:comment error:error];
  346. }
  347. NSError *e = [self.class conversionError:NSLocalizedString(@"failed to convert string to data", nil)];
  348. if (error) {
  349. *error = e;
  350. }
  351. return NO;
  352. }
  353. #pragma mark -
  354. - (NSData *)dataForKey:(NSString *)key
  355. {
  356. return [self dataForKey:key error:nil];
  357. }
  358. - (NSData *)dataForKey:(NSString *)key error:(NSError *__autoreleasing *)error
  359. {
  360. NSMutableDictionary *query = [self query];
  361. query[(__bridge __strong id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
  362. query[(__bridge __strong id)kSecReturnData] = (__bridge id)kCFBooleanTrue;
  363. query[(__bridge __strong id)kSecAttrAccount] = key;
  364. CFTypeRef data = nil;
  365. OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &data);
  366. if (status == errSecSuccess) {
  367. NSData *ret = [NSData dataWithData:(__bridge NSData *)data];
  368. if (data) {
  369. CFRelease(data);
  370. return ret;
  371. } else {
  372. NSError *e = [self.class unexpectedError:NSLocalizedString(@"Unexpected error has occurred.", nil)];
  373. if (error) {
  374. *error = e;
  375. }
  376. return nil;
  377. }
  378. } else if (status == errSecItemNotFound) {
  379. return nil;
  380. }
  381. NSError *e = [self.class securityError:status];
  382. if (error) {
  383. *error = e;
  384. }
  385. return nil;
  386. }
  387. #pragma mark -
  388. - (BOOL)setData:(NSData *)data forKey:(NSString *)key
  389. {
  390. return [self setData:data forKey:key genericAttribute:nil label:nil comment:nil error:nil];
  391. }
  392. - (BOOL)setData:(NSData *)data forKey:(NSString *)key error:(NSError *__autoreleasing *)error
  393. {
  394. return [self setData:data forKey:key genericAttribute:nil label:nil comment:nil error:error];
  395. }
  396. - (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute
  397. {
  398. return [self setData:data forKey:key genericAttribute:genericAttribute label:nil comment:nil error:nil];
  399. }
  400. - (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute error:(NSError * __autoreleasing *)error
  401. {
  402. return [self setData:data forKey:key genericAttribute:genericAttribute label:nil comment:nil error:error];
  403. }
  404. - (BOOL)setData:(NSData *)data forKey:(NSString *)key label:(NSString *)label comment:(NSString *)comment
  405. {
  406. return [self setData:data forKey:key genericAttribute:nil label:label comment:comment error:nil];
  407. }
  408. - (BOOL)setData:(NSData *)data forKey:(NSString *)key label:(NSString *)label comment:(NSString *)comment error:(NSError *__autoreleasing *)error
  409. {
  410. return [self setData:data forKey:key genericAttribute:nil label:label comment:comment error:error];
  411. }
  412. - (BOOL)setData:(NSData *)data forKey:(NSString *)key genericAttribute:(id)genericAttribute label:(NSString *)label comment:(NSString *)comment error:(NSError *__autoreleasing *)error
  413. {
  414. if (!key) {
  415. NSError *e = [self.class argumentError:NSLocalizedString(@"the key must not to be nil", nil)];
  416. if (error) {
  417. *error = e;
  418. }
  419. return NO;
  420. }
  421. if (!data) {
  422. return [self removeItemForKey:key error:error];
  423. }
  424. NSMutableDictionary *query = [self query];
  425. query[(__bridge __strong id)kSecAttrAccount] = key;
  426. #if TARGET_OS_IOS
  427. if (floor(NSFoundationVersionNumber) > floor(1144.17)) { // iOS 9+
  428. query[(__bridge __strong id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail;
  429. #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0
  430. } else if (floor(NSFoundationVersionNumber) > floor(1047.25)) { // iOS 8+
  431. query[(__bridge __strong id)kSecUseNoAuthenticationUI] = (__bridge id)kCFBooleanTrue;
  432. #endif
  433. }
  434. #elif TARGET_OS_WATCH || TARGET_OS_TV
  435. query[(__bridge __strong id)kSecUseAuthenticationUI] = (__bridge id)kSecUseAuthenticationUIFail;
  436. #endif
  437. OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
  438. if (status == errSecSuccess || status == errSecInteractionNotAllowed) {
  439. query = [self query];
  440. query[(__bridge __strong id)kSecAttrAccount] = key;
  441. NSError *unexpectedError = nil;
  442. NSMutableDictionary *attributes = [self attributesWithKey:nil value:data error:&unexpectedError];
  443. if (genericAttribute) {
  444. attributes[(__bridge __strong id)kSecAttrGeneric] = genericAttribute;
  445. }
  446. if (label) {
  447. attributes[(__bridge __strong id)kSecAttrLabel] = label;
  448. }
  449. if (comment) {
  450. attributes[(__bridge __strong id)kSecAttrComment] = comment;
  451. }
  452. if (unexpectedError) {
  453. NSLog(@"error: [%@] %@", @(unexpectedError.code), NSLocalizedString(@"Unexpected error has occurred.", nil));
  454. if (error) {
  455. *error = unexpectedError;
  456. }
  457. return NO;
  458. } else {
  459. if (status == errSecInteractionNotAllowed && floor(NSFoundationVersionNumber) <= floor(1140.11)) { // iOS 8.0.x
  460. if ([self removeItemForKey:key error:error]) {
  461. return [self setData:data forKey:key label:label comment:comment error:error];
  462. }
  463. } else {
  464. status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes);
  465. }
  466. if (status != errSecSuccess) {
  467. NSError *e = [self.class securityError:status];
  468. if (error) {
  469. *error = e;
  470. }
  471. return NO;
  472. }
  473. }
  474. } else if (status == errSecItemNotFound) {
  475. NSError *unexpectedError = nil;
  476. NSMutableDictionary *attributes = [self attributesWithKey:key value:data error:&unexpectedError];
  477. if (genericAttribute) {
  478. attributes[(__bridge __strong id)kSecAttrGeneric] = genericAttribute;
  479. }
  480. if (label) {
  481. attributes[(__bridge __strong id)kSecAttrLabel] = label;
  482. }
  483. if (comment) {
  484. attributes[(__bridge __strong id)kSecAttrComment] = comment;
  485. }
  486. if (unexpectedError) {
  487. NSLog(@"error: [%@] %@", @(unexpectedError.code), NSLocalizedString(@"Unexpected error has occurred.", nil));
  488. if (error) {
  489. *error = unexpectedError;
  490. }
  491. return NO;
  492. } else {
  493. status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
  494. if (status != errSecSuccess) {
  495. NSError *e = [self.class securityError:status];
  496. if (error) {
  497. *error = e;
  498. }
  499. return NO;
  500. }
  501. }
  502. } else {
  503. NSError *e = [self.class securityError:status];
  504. if (error) {
  505. *error = e;
  506. }
  507. return NO;
  508. }
  509. return YES;
  510. }
  511. #pragma mark -
  512. + (BOOL)removeItemForKey:(NSString *)key
  513. {
  514. return [self removeItemForKey:key service:nil accessGroup:nil error:nil];
  515. }
  516. + (BOOL)removeItemForKey:(NSString *)key error:(NSError *__autoreleasing *)error
  517. {
  518. return [self removeItemForKey:key service:nil accessGroup:nil error:error];
  519. }
  520. + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service
  521. {
  522. return [self removeItemForKey:key service:service accessGroup:nil error:nil];
  523. }
  524. + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service error:(NSError *__autoreleasing *)error
  525. {
  526. return [self removeItemForKey:key service:service accessGroup:nil error:error];
  527. }
  528. + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup
  529. {
  530. return [self removeItemForKey:key service:service accessGroup:accessGroup error:nil];
  531. }
  532. + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error
  533. {
  534. if (!key) {
  535. NSError *e = [self.class argumentError:NSLocalizedString(@"the key must not to be nil", nil)];
  536. if (error) {
  537. *error = e;
  538. }
  539. return NO;
  540. }
  541. if (!service) {
  542. service = [self defaultService];
  543. }
  544. UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup];
  545. return [keychain removeItemForKey:key error:error];
  546. }
  547. #pragma mark -
  548. + (BOOL)removeAllItems
  549. {
  550. return [self removeAllItemsForService:nil accessGroup:nil error:nil];
  551. }
  552. + (BOOL)removeAllItemsWithError:(NSError *__autoreleasing *)error
  553. {
  554. return [self removeAllItemsForService:nil accessGroup:nil error:error];
  555. }
  556. + (BOOL)removeAllItemsForService:(NSString *)service
  557. {
  558. return [self removeAllItemsForService:service accessGroup:nil error:nil];
  559. }
  560. + (BOOL)removeAllItemsForService:(NSString *)service error:(NSError *__autoreleasing *)error
  561. {
  562. return [self removeAllItemsForService:service accessGroup:nil error:error];
  563. }
  564. + (BOOL)removeAllItemsForService:(NSString *)service accessGroup:(NSString *)accessGroup
  565. {
  566. return [self removeAllItemsForService:service accessGroup:accessGroup error:nil];
  567. }
  568. + (BOOL)removeAllItemsForService:(NSString *)service accessGroup:(NSString *)accessGroup error:(NSError *__autoreleasing *)error
  569. {
  570. UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithService:service accessGroup:accessGroup];
  571. return [keychain removeAllItemsWithError:error];
  572. }
  573. #pragma mark -
  574. - (BOOL)removeItemForKey:(NSString *)key
  575. {
  576. return [self removeItemForKey:key error:nil];
  577. }
  578. - (BOOL)removeItemForKey:(NSString *)key error:(NSError *__autoreleasing *)error
  579. {
  580. NSMutableDictionary *query = [self query];
  581. query[(__bridge __strong id)kSecAttrAccount] = key;
  582. OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
  583. if (status != errSecSuccess && status != errSecItemNotFound) {
  584. NSError *e = [self.class securityError:status];
  585. if (error) {
  586. *error = e;
  587. }
  588. return NO;
  589. }
  590. return YES;
  591. }
  592. #pragma mark -
  593. - (BOOL)removeAllItems
  594. {
  595. return [self removeAllItemsWithError:nil];
  596. }
  597. - (BOOL)removeAllItemsWithError:(NSError *__autoreleasing *)error
  598. {
  599. NSMutableDictionary *query = [self query];
  600. #if !TARGET_OS_IPHONE
  601. query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
  602. #endif
  603. OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
  604. if (status != errSecSuccess && status != errSecItemNotFound) {
  605. NSError *e = [self.class securityError:status];
  606. if (error) {
  607. *error = e;
  608. }
  609. return NO;
  610. }
  611. return YES;
  612. }
  613. #pragma mark -
  614. - (NSString *)objectForKeyedSubscript:(NSString <NSCopying> *)key
  615. {
  616. return [self stringForKey:key];
  617. }
  618. - (void)setObject:(NSString *)obj forKeyedSubscript:(NSString <NSCopying> *)key
  619. {
  620. if (!obj) {
  621. [self removeItemForKey:key];
  622. } else {
  623. [self setString:obj forKey:key];
  624. }
  625. }
  626. #pragma mark -
  627. - (NSArray UIC_KEY_TYPE *)allKeys
  628. {
  629. NSArray *items = [self.class prettify:[self itemClassObject] items:[self items]];
  630. NSMutableArray *keys = [[NSMutableArray alloc] init];
  631. for (NSDictionary *item in items) {
  632. NSString *key = item[@"key"];
  633. if (key) {
  634. [keys addObject:key];
  635. }
  636. }
  637. return keys.copy;
  638. }
  639. + (NSArray UIC_KEY_TYPE *)allKeysWithItemClass:(UICKeyChainStoreItemClass)itemClass
  640. {
  641. CFTypeRef itemClassObject = kSecClassGenericPassword;
  642. if (itemClass == UICKeyChainStoreItemClassGenericPassword) {
  643. itemClassObject = kSecClassGenericPassword;
  644. } else if (itemClass == UICKeyChainStoreItemClassInternetPassword) {
  645. itemClassObject = kSecClassInternetPassword;
  646. }
  647. NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  648. query[(__bridge __strong id)kSecClass] = (__bridge id)itemClassObject;
  649. query[(__bridge __strong id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
  650. query[(__bridge __strong id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue;
  651. CFArrayRef result = nil;
  652. CFDictionaryRef cfquery = (CFDictionaryRef)CFBridgingRetain(query);
  653. OSStatus status = SecItemCopyMatching(cfquery, (CFTypeRef *)&result);
  654. CFRelease(cfquery);
  655. if (status == errSecSuccess) {
  656. NSArray *items = [self prettify:itemClassObject items:(__bridge NSArray *)result];
  657. NSMutableArray *keys = [[NSMutableArray alloc] init];
  658. for (NSDictionary *item in items) {
  659. if (itemClassObject == kSecClassGenericPassword) {
  660. [keys addObject:@{@"service": item[@"service"] ?: @"", @"key": item[@"key"] ?: @""}];
  661. } else if (itemClassObject == kSecClassInternetPassword) {
  662. [keys addObject:@{@"server": item[@"service"] ?: @"", @"key": item[@"key"] ?: @""}];
  663. }
  664. }
  665. return keys.copy;
  666. } else if (status == errSecItemNotFound) {
  667. return @[];
  668. }
  669. return nil;
  670. }
  671. + (NSArray *)allItemsWithItemClass:(UICKeyChainStoreItemClass)itemClass
  672. {
  673. CFTypeRef itemClassObject = kSecClassGenericPassword;
  674. if (itemClass == UICKeyChainStoreItemClassGenericPassword) {
  675. itemClassObject = kSecClassGenericPassword;
  676. } else if (itemClass == UICKeyChainStoreItemClassInternetPassword) {
  677. itemClassObject = kSecClassInternetPassword;
  678. }
  679. NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  680. query[(__bridge __strong id)kSecClass] = (__bridge id)itemClassObject;
  681. query[(__bridge __strong id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
  682. query[(__bridge __strong id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue;
  683. #if TARGET_OS_IPHONE
  684. query[(__bridge __strong id)kSecReturnData] = (__bridge id)kCFBooleanTrue;
  685. #endif
  686. CFArrayRef result = nil;
  687. CFDictionaryRef cfquery = (CFDictionaryRef)CFBridgingRetain(query);
  688. OSStatus status = SecItemCopyMatching(cfquery, (CFTypeRef *)&result);
  689. CFRelease(cfquery);
  690. if (status == errSecSuccess) {
  691. return [self prettify:itemClassObject items:(__bridge NSArray *)result];
  692. } else if (status == errSecItemNotFound) {
  693. return @[];
  694. }
  695. return nil;
  696. }
  697. - (NSArray *)allItems
  698. {
  699. return [self.class prettify:[self itemClassObject] items:[self items]];
  700. }
  701. - (NSArray *)items
  702. {
  703. NSMutableDictionary *query = [self query];
  704. query[(__bridge __strong id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
  705. query[(__bridge __strong id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue;
  706. #if TARGET_OS_IPHONE
  707. query[(__bridge __strong id)kSecReturnData] = (__bridge id)kCFBooleanTrue;
  708. #endif
  709. CFArrayRef result = nil;
  710. OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query,(CFTypeRef *)&result);
  711. if (status == errSecSuccess) {
  712. return CFBridgingRelease(result);
  713. } else if (status == errSecItemNotFound) {
  714. return @[];
  715. }
  716. return nil;
  717. }
  718. + (NSArray *)prettify:(CFTypeRef)itemClass items:(NSArray *)items
  719. {
  720. NSMutableArray *prettified = [[NSMutableArray alloc] init];
  721. for (NSDictionary *attributes in items) {
  722. NSMutableDictionary *item = [[NSMutableDictionary alloc] init];
  723. if (itemClass == kSecClassGenericPassword) {
  724. item[@"class"] = @"GenericPassword";
  725. id service = attributes[(__bridge id)kSecAttrService];
  726. if (service) {
  727. item[@"service"] = service;
  728. }
  729. id accessGroup = attributes[(__bridge id)kSecAttrAccessGroup];
  730. if (accessGroup) {
  731. item[@"accessGroup"] = accessGroup;
  732. }
  733. } else if (itemClass == kSecClassInternetPassword) {
  734. item[@"class"] = @"InternetPassword";
  735. id server = attributes[(__bridge id)kSecAttrServer];
  736. if (server) {
  737. item[@"server"] = server;
  738. }
  739. id protocolType = attributes[(__bridge id)kSecAttrProtocol];
  740. if (protocolType) {
  741. item[@"protocol"] = protocolType;
  742. }
  743. id authenticationType = attributes[(__bridge id)kSecAttrAuthenticationType];
  744. if (authenticationType) {
  745. item[@"authenticationType"] = authenticationType;
  746. }
  747. }
  748. id key = attributes[(__bridge id)kSecAttrAccount];
  749. if (key) {
  750. item[@"key"] = key;
  751. }
  752. NSData *data = attributes[(__bridge id)kSecValueData];
  753. NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  754. if (string) {
  755. item[@"value"] = string;
  756. } else {
  757. item[@"value"] = data;
  758. }
  759. id accessible = attributes[(__bridge id)kSecAttrAccessible];
  760. if (accessible) {
  761. item[@"accessibility"] = accessible;
  762. }
  763. if (floor(NSFoundationVersionNumber) > floor(993.00)) { // iOS 7+
  764. id synchronizable = attributes[(__bridge id)kSecAttrSynchronizable];
  765. if (synchronizable) {
  766. item[@"synchronizable"] = synchronizable;
  767. }
  768. }
  769. [prettified addObject:item];
  770. }
  771. return prettified.copy;
  772. }
  773. #pragma mark -
  774. - (void)setSynchronizable:(BOOL)synchronizable
  775. {
  776. _synchronizable = synchronizable;
  777. if (_authenticationPolicy) {
  778. NSLog(@"%@", @"Cannot specify both an authenticationPolicy and a synchronizable");
  779. }
  780. }
  781. - (void)setAccessibility:(UICKeyChainStoreAccessibility)accessibility authenticationPolicy:(UICKeyChainStoreAuthenticationPolicy)authenticationPolicy
  782. {
  783. _accessibility = accessibility;
  784. _authenticationPolicy = authenticationPolicy;
  785. if (_synchronizable) {
  786. NSLog(@"%@", @"Cannot specify both an authenticationPolicy and a synchronizable");
  787. }
  788. }
  789. #pragma mark -
  790. #if TARGET_OS_IOS
  791. - (void)sharedPasswordWithCompletion:(void (^)(NSString *account, NSString *password, NSError *error))completion
  792. {
  793. NSString *domain = self.server.host;
  794. if (domain.length > 0) {
  795. [self.class requestSharedWebCredentialForDomain:domain account:nil completion:^(NSArray *credentials, NSError *error) {
  796. NSDictionary *credential = credentials.firstObject;
  797. if (credential) {
  798. NSString *account = credential[@"account"];
  799. NSString *password = credential[@"password"];
  800. if (completion) {
  801. completion(account, password, error);
  802. }
  803. } else {
  804. if (completion) {
  805. completion(nil, nil, error);
  806. }
  807. }
  808. }];
  809. } else {
  810. NSError *error = [self.class argumentError:NSLocalizedString(@"the server property must not to be nil, should use 'keyChainStoreWithServer:protocolType:' initializer to instantiate keychain store", nil)];
  811. if (completion) {
  812. completion(nil, nil, error);
  813. }
  814. }
  815. }
  816. - (void)sharedPasswordForAccount:(NSString *)account completion:(void (^)(NSString *password, NSError *error))completion
  817. {
  818. NSString *domain = self.server.host;
  819. if (domain.length > 0) {
  820. [self.class requestSharedWebCredentialForDomain:domain account:account completion:^(NSArray *credentials, NSError *error) {
  821. NSDictionary *credential = credentials.firstObject;
  822. if (credential) {
  823. NSString *password = credential[@"password"];
  824. if (completion) {
  825. completion(password, error);
  826. }
  827. } else {
  828. if (completion) {
  829. completion(nil, error);
  830. }
  831. }
  832. }];
  833. } else {
  834. NSError *error = [self.class argumentError:NSLocalizedString(@"the server property must not to be nil, should use 'keyChainStoreWithServer:protocolType:' initializer to instantiate keychain store", nil)];
  835. if (completion) {
  836. completion(nil, error);
  837. }
  838. }
  839. }
  840. - (void)setSharedPassword:(NSString *)password forAccount:(NSString *)account completion:(void (^)(NSError *error))completion
  841. {
  842. NSString *domain = self.server.host;
  843. if (domain.length > 0) {
  844. SecAddSharedWebCredential((__bridge CFStringRef)domain, (__bridge CFStringRef)account, (__bridge CFStringRef)password, ^(CFErrorRef error) {
  845. if (completion) {
  846. completion((__bridge NSError *)error);
  847. }
  848. });
  849. } else {
  850. NSError *error = [self.class argumentError:NSLocalizedString(@"the server property must not to be nil, should use 'keyChainStoreWithServer:protocolType:' initializer to instantiate keychain store", nil)];
  851. if (completion) {
  852. completion(error);
  853. }
  854. }
  855. }
  856. - (void)removeSharedPasswordForAccount:(NSString *)account completion:(void (^)(NSError *error))completion
  857. {
  858. [self setSharedPassword:nil forAccount:account completion:completion];
  859. }
  860. + (void)requestSharedWebCredentialWithCompletion:(void (^)(NSArray UIC_CREDENTIAL_TYPE *credentials, NSError *error))completion
  861. {
  862. [self requestSharedWebCredentialForDomain:nil account:nil completion:completion];
  863. }
  864. + (void)requestSharedWebCredentialForDomain:(NSString *)domain account:(NSString *)account completion:(void (^)(NSArray UIC_CREDENTIAL_TYPE *credentials, NSError *error))completion
  865. {
  866. SecRequestSharedWebCredential((__bridge CFStringRef)domain, (__bridge CFStringRef)account, ^(CFArrayRef credentials, CFErrorRef error) {
  867. if (error) {
  868. NSError *e = (__bridge NSError *)error;
  869. if (e.code != errSecItemNotFound) {
  870. NSLog(@"error: [%@] %@", @(e.code), e.localizedDescription);
  871. }
  872. }
  873. NSMutableArray *sharedCredentials = [[NSMutableArray alloc] init];
  874. for (NSDictionary *credential in (__bridge NSArray *)credentials) {
  875. NSMutableDictionary *sharedCredential = [[NSMutableDictionary alloc] init];
  876. NSString *server = credential[(__bridge __strong id)kSecAttrServer];
  877. if (server) {
  878. sharedCredential[@"server"] = server;
  879. }
  880. NSString *account = credential[(__bridge __strong id)kSecAttrAccount];
  881. if (account) {
  882. sharedCredential[@"account"] = account;
  883. }
  884. NSString *password = credential[(__bridge __strong id)kSecSharedPassword];
  885. if (password) {
  886. sharedCredential[@"password"] = password;
  887. }
  888. [sharedCredentials addObject:sharedCredential];
  889. }
  890. if (completion) {
  891. completion(sharedCredentials.copy, (__bridge NSError *)error);
  892. }
  893. });
  894. }
  895. + (NSString *)generatePassword
  896. {
  897. return (NSString *)CFBridgingRelease(SecCreateSharedWebCredentialPassword());
  898. }
  899. #endif
  900. #pragma mark -
  901. - (NSString *)description
  902. {
  903. NSArray *items = [self allItems];
  904. if (items.count == 0) {
  905. return @"()";
  906. }
  907. NSMutableString *description = [[NSMutableString alloc] initWithString:@"(\n"];
  908. for (NSDictionary *item in items) {
  909. [description appendFormat:@" %@", item];
  910. }
  911. [description appendString:@")"];
  912. return description.copy;
  913. }
  914. - (NSString *)debugDescription
  915. {
  916. return [NSString stringWithFormat:@"%@", [self items]];
  917. }
  918. #pragma mark -
  919. - (NSMutableDictionary *)query
  920. {
  921. NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  922. CFTypeRef itemClass = [self itemClassObject];
  923. query[(__bridge __strong id)kSecClass] =(__bridge id)itemClass;
  924. if (floor(NSFoundationVersionNumber) > floor(993.00)) { // iOS 7+ (NSFoundationVersionNumber_iOS_6_1)
  925. query[(__bridge __strong id)kSecAttrSynchronizable] = (__bridge id)kSecAttrSynchronizableAny;
  926. }
  927. if (itemClass == kSecClassGenericPassword) {
  928. query[(__bridge __strong id)(kSecAttrService)] = _service;
  929. #if !TARGET_OS_SIMULATOR
  930. if (_accessGroup) {
  931. query[(__bridge __strong id)kSecAttrAccessGroup] = _accessGroup;
  932. }
  933. #endif
  934. } else {
  935. if (_server.host) {
  936. query[(__bridge __strong id)kSecAttrServer] = _server.host;
  937. }
  938. if (_server.port) {
  939. query[(__bridge __strong id)kSecAttrPort] = _server.port;
  940. }
  941. CFTypeRef protocolTypeObject = [self protocolTypeObject];
  942. if (protocolTypeObject) {
  943. query[(__bridge __strong id)kSecAttrProtocol] = (__bridge id)protocolTypeObject;
  944. }
  945. CFTypeRef authenticationTypeObject = [self authenticationTypeObject];
  946. if (authenticationTypeObject) {
  947. query[(__bridge __strong id)kSecAttrAuthenticationType] = (__bridge id)authenticationTypeObject;
  948. }
  949. }
  950. #if TARGET_OS_IOS
  951. if (_authenticationPrompt) {
  952. if (floor(NSFoundationVersionNumber) > floor(1047.25)) { // iOS 8+ (NSFoundationVersionNumber_iOS_7_1)
  953. query[(__bridge __strong id)kSecUseOperationPrompt] = _authenticationPrompt;
  954. } else {
  955. NSLog(@"%@", @"Unavailable 'authenticationPrompt' attribute on iOS versions prior to 8.0.");
  956. }
  957. }
  958. #endif
  959. return query;
  960. }
  961. - (NSMutableDictionary *)attributesWithKey:(NSString *)key value:(NSData *)value error:(NSError *__autoreleasing *)error
  962. {
  963. NSMutableDictionary *attributes;
  964. if (key) {
  965. attributes = [self query];
  966. attributes[(__bridge __strong id)kSecAttrAccount] = key;
  967. } else {
  968. attributes = [[NSMutableDictionary alloc] init];
  969. }
  970. attributes[(__bridge __strong id)kSecValueData] = value;
  971. #if TARGET_OS_IOS
  972. double iOS_7_1_or_10_9_2 = 1047.25; // NSFoundationVersionNumber_iOS_7_1
  973. #else
  974. double iOS_7_1_or_10_9_2 = 1056.13; // NSFoundationVersionNumber10_9_2
  975. #endif
  976. CFTypeRef accessibilityObject = [self accessibilityObject];
  977. if (_authenticationPolicy && accessibilityObject) {
  978. if (floor(NSFoundationVersionNumber) > floor(iOS_7_1_or_10_9_2)) { // iOS 8+ or OS X 10.10+
  979. CFErrorRef securityError = NULL;
  980. SecAccessControlRef accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibilityObject, (SecAccessControlCreateFlags)_authenticationPolicy, &securityError);
  981. if (securityError) {
  982. NSError *e = (__bridge NSError *)securityError;
  983. NSLog(@"error: [%@] %@", @(e.code), e.localizedDescription);
  984. if (error) {
  985. *error = e;
  986. CFRelease(accessControl);
  987. return nil;
  988. }
  989. }
  990. if (!accessControl) {
  991. NSString *message = NSLocalizedString(@"Unexpected error has occurred.", nil);
  992. NSError *e = [self.class unexpectedError:message];
  993. if (error) {
  994. *error = e;
  995. }
  996. return nil;
  997. }
  998. attributes[(__bridge __strong id)kSecAttrAccessControl] = (__bridge_transfer id)accessControl;
  999. } else {
  1000. #if TARGET_OS_IOS
  1001. NSLog(@"%@", @"Unavailable 'Touch ID integration' on iOS versions prior to 8.0.");
  1002. #else
  1003. NSLog(@"%@", @"Unavailable 'Touch ID integration' on OS X versions prior to 10.10.");
  1004. #endif
  1005. }
  1006. } else {
  1007. if (floor(NSFoundationVersionNumber) <= floor(iOS_7_1_or_10_9_2) && _accessibility == UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly) {
  1008. #if TARGET_OS_IOS
  1009. NSLog(@"%@", @"Unavailable 'UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly' attribute on iOS versions prior to 8.0.");
  1010. #else
  1011. NSLog(@"%@", @"Unavailable 'UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly' attribute on OS X versions prior to 10.10.");
  1012. #endif
  1013. } else {
  1014. if (accessibilityObject) {
  1015. attributes[(__bridge __strong id)kSecAttrAccessible] = (__bridge id)accessibilityObject;
  1016. }
  1017. }
  1018. }
  1019. if (floor(NSFoundationVersionNumber) > floor(993.00)) { // iOS 7+
  1020. attributes[(__bridge __strong id)kSecAttrSynchronizable] = @(_synchronizable);
  1021. }
  1022. return attributes;
  1023. }
  1024. #pragma mark -
  1025. - (CFTypeRef)itemClassObject
  1026. {
  1027. switch (_itemClass) {
  1028. case UICKeyChainStoreItemClassGenericPassword:
  1029. return kSecClassGenericPassword;
  1030. case UICKeyChainStoreItemClassInternetPassword:
  1031. return kSecClassInternetPassword;
  1032. default:
  1033. return nil;
  1034. }
  1035. }
  1036. - (CFTypeRef)protocolTypeObject
  1037. {
  1038. switch (_protocolType) {
  1039. case UICKeyChainStoreProtocolTypeFTP:
  1040. return kSecAttrProtocolFTP;
  1041. case UICKeyChainStoreProtocolTypeFTPAccount:
  1042. return kSecAttrProtocolFTPAccount;
  1043. case UICKeyChainStoreProtocolTypeHTTP:
  1044. return kSecAttrProtocolHTTP;
  1045. case UICKeyChainStoreProtocolTypeIRC:
  1046. return kSecAttrProtocolIRC;
  1047. case UICKeyChainStoreProtocolTypeNNTP:
  1048. return kSecAttrProtocolNNTP;
  1049. case UICKeyChainStoreProtocolTypePOP3:
  1050. return kSecAttrProtocolPOP3;
  1051. case UICKeyChainStoreProtocolTypeSMTP:
  1052. return kSecAttrProtocolSMTP;
  1053. case UICKeyChainStoreProtocolTypeSOCKS:
  1054. return kSecAttrProtocolSOCKS;
  1055. case UICKeyChainStoreProtocolTypeIMAP:
  1056. return kSecAttrProtocolIMAP;
  1057. case UICKeyChainStoreProtocolTypeLDAP:
  1058. return kSecAttrProtocolLDAP;
  1059. case UICKeyChainStoreProtocolTypeAppleTalk:
  1060. return kSecAttrProtocolAppleTalk;
  1061. case UICKeyChainStoreProtocolTypeAFP:
  1062. return kSecAttrProtocolAFP;
  1063. case UICKeyChainStoreProtocolTypeTelnet:
  1064. return kSecAttrProtocolTelnet;
  1065. case UICKeyChainStoreProtocolTypeSSH:
  1066. return kSecAttrProtocolSSH;
  1067. case UICKeyChainStoreProtocolTypeFTPS:
  1068. return kSecAttrProtocolFTPS;
  1069. case UICKeyChainStoreProtocolTypeHTTPS:
  1070. return kSecAttrProtocolHTTPS;
  1071. case UICKeyChainStoreProtocolTypeHTTPProxy:
  1072. return kSecAttrProtocolHTTPProxy;
  1073. case UICKeyChainStoreProtocolTypeHTTPSProxy:
  1074. return kSecAttrProtocolHTTPSProxy;
  1075. case UICKeyChainStoreProtocolTypeFTPProxy:
  1076. return kSecAttrProtocolFTPProxy;
  1077. case UICKeyChainStoreProtocolTypeSMB:
  1078. return kSecAttrProtocolSMB;
  1079. case UICKeyChainStoreProtocolTypeRTSP:
  1080. return kSecAttrProtocolRTSP;
  1081. case UICKeyChainStoreProtocolTypeRTSPProxy:
  1082. return kSecAttrProtocolRTSPProxy;
  1083. case UICKeyChainStoreProtocolTypeDAAP:
  1084. return kSecAttrProtocolDAAP;
  1085. case UICKeyChainStoreProtocolTypeEPPC:
  1086. return kSecAttrProtocolEPPC;
  1087. case UICKeyChainStoreProtocolTypeNNTPS:
  1088. return kSecAttrProtocolNNTPS;
  1089. case UICKeyChainStoreProtocolTypeLDAPS:
  1090. return kSecAttrProtocolLDAPS;
  1091. case UICKeyChainStoreProtocolTypeTelnetS:
  1092. return kSecAttrProtocolTelnetS;
  1093. case UICKeyChainStoreProtocolTypeIRCS:
  1094. return kSecAttrProtocolIRCS;
  1095. case UICKeyChainStoreProtocolTypePOP3S:
  1096. return kSecAttrProtocolPOP3S;
  1097. default:
  1098. return nil;
  1099. }
  1100. }
  1101. - (CFTypeRef)authenticationTypeObject
  1102. {
  1103. switch (_authenticationType) {
  1104. case UICKeyChainStoreAuthenticationTypeNTLM:
  1105. return kSecAttrAuthenticationTypeNTLM;
  1106. case UICKeyChainStoreAuthenticationTypeMSN:
  1107. return kSecAttrAuthenticationTypeMSN;
  1108. case UICKeyChainStoreAuthenticationTypeDPA:
  1109. return kSecAttrAuthenticationTypeDPA;
  1110. case UICKeyChainStoreAuthenticationTypeRPA:
  1111. return kSecAttrAuthenticationTypeRPA;
  1112. case UICKeyChainStoreAuthenticationTypeHTTPBasic:
  1113. return kSecAttrAuthenticationTypeHTTPBasic;
  1114. case UICKeyChainStoreAuthenticationTypeHTTPDigest:
  1115. return kSecAttrAuthenticationTypeHTTPDigest;
  1116. case UICKeyChainStoreAuthenticationTypeHTMLForm:
  1117. return kSecAttrAuthenticationTypeHTMLForm;
  1118. case UICKeyChainStoreAuthenticationTypeDefault:
  1119. return kSecAttrAuthenticationTypeDefault;
  1120. default:
  1121. return nil;
  1122. }
  1123. }
  1124. - (CFTypeRef)accessibilityObject
  1125. {
  1126. switch (_accessibility) {
  1127. case UICKeyChainStoreAccessibilityWhenUnlocked:
  1128. return kSecAttrAccessibleWhenUnlocked;
  1129. case UICKeyChainStoreAccessibilityAfterFirstUnlock:
  1130. return kSecAttrAccessibleAfterFirstUnlock;
  1131. case UICKeyChainStoreAccessibilityAlways:
  1132. return kSecAttrAccessibleAlways;
  1133. case UICKeyChainStoreAccessibilityWhenPasscodeSetThisDeviceOnly:
  1134. return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly;
  1135. case UICKeyChainStoreAccessibilityWhenUnlockedThisDeviceOnly:
  1136. return kSecAttrAccessibleWhenUnlockedThisDeviceOnly;
  1137. case UICKeyChainStoreAccessibilityAfterFirstUnlockThisDeviceOnly:
  1138. return kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
  1139. case UICKeyChainStoreAccessibilityAlwaysThisDeviceOnly:
  1140. return kSecAttrAccessibleAlwaysThisDeviceOnly;
  1141. default:
  1142. return nil;
  1143. }
  1144. }
  1145. + (NSError *)argumentError:(NSString *)message
  1146. {
  1147. NSError *error = [NSError errorWithDomain:UICKeyChainStoreErrorDomain code:UICKeyChainStoreErrorInvalidArguments userInfo:@{NSLocalizedDescriptionKey: message}];
  1148. NSLog(@"error: [%@] %@", @(error.code), error.localizedDescription);
  1149. return error;
  1150. }
  1151. + (NSError *)conversionError:(NSString *)message
  1152. {
  1153. NSError *error = [NSError errorWithDomain:UICKeyChainStoreErrorDomain code:-67594 userInfo:@{NSLocalizedDescriptionKey: message}];
  1154. NSLog(@"error: [%@] %@", @(error.code), error.localizedDescription);
  1155. return error;
  1156. }
  1157. + (NSError *)securityError:(OSStatus)status
  1158. {
  1159. NSString *message = @"Security error has occurred.";
  1160. #if TARGET_OS_MAC && !TARGET_OS_IPHONE
  1161. CFStringRef description = SecCopyErrorMessageString(status, NULL);
  1162. if (description) {
  1163. message = (__bridge_transfer NSString *)description;
  1164. }
  1165. #endif
  1166. NSError *error = [NSError errorWithDomain:UICKeyChainStoreErrorDomain code:status userInfo:@{NSLocalizedDescriptionKey: message}];
  1167. NSLog(@"OSStatus error: [%@] %@", @(error.code), error.localizedDescription);
  1168. return error;
  1169. }
  1170. + (NSError *)unexpectedError:(NSString *)message
  1171. {
  1172. NSError *error = [NSError errorWithDomain:UICKeyChainStoreErrorDomain code:-99999 userInfo:@{NSLocalizedDescriptionKey: message}];
  1173. NSLog(@"error: [%@] %@", @(error.code), error.localizedDescription);
  1174. return error;
  1175. }
  1176. @end
  1177. @implementation UICKeyChainStore (Deprecation)
  1178. - (void)synchronize
  1179. {
  1180. // Deprecated, calling this method is no longer required
  1181. }
  1182. - (BOOL)synchronizeWithError:(NSError *__autoreleasing *)error
  1183. {
  1184. // Deprecated, calling this method is no longer required
  1185. return true;
  1186. }
  1187. @end