UICKeyChainStore.m 50 KB

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