NSData+CommonCrypto.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*
  2. * NSData+CommonCrypto.m
  3. * AQToolkit
  4. *
  5. * Created by Jim Dovey on 31/8/2008.
  6. *
  7. * Copyright (c) 2008-2009, Jim Dovey
  8. * All rights reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * Redistributions of source code must retain the above copyright notice,
  15. * this list of conditions and the following disclaimer.
  16. *
  17. * Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in the
  19. * documentation and/or other materials provided with the distribution.
  20. *
  21. * Neither the name of this project's author nor the names of its
  22. * contributors may be used to endorse or promote products derived from
  23. * this software without specific prior written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  28. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29. * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  31. * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  32. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  33. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  34. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. */
  38. #import <Foundation/Foundation.h>
  39. #import "NSData+CommonCrypto.h"
  40. #import <CommonCrypto/CommonDigest.h>
  41. #import <CommonCrypto/CommonCryptor.h>
  42. #import <CommonCrypto/CommonHMAC.h>
  43. NSString * const kCommonCryptoErrorDomain = @"CommonCryptoErrorDomain";
  44. @implementation NSError (CommonCryptoErrorDomain)
  45. + (NSError *) errorWithCCCryptorStatus: (CCCryptorStatus) status
  46. {
  47. NSString * description = nil, * reason = nil;
  48. switch ( status )
  49. {
  50. case kCCSuccess:
  51. description = NSLocalizedString(@"Success", @"Error description");
  52. break;
  53. case kCCParamError:
  54. description = NSLocalizedString(@"Parameter Error", @"Error description");
  55. reason = NSLocalizedString(@"Illegal parameter supplied to encryption/decryption algorithm", @"Error reason");
  56. break;
  57. case kCCBufferTooSmall:
  58. description = NSLocalizedString(@"Buffer Too Small", @"Error description");
  59. reason = NSLocalizedString(@"Insufficient buffer provided for specified operation", @"Error reason");
  60. break;
  61. case kCCMemoryFailure:
  62. description = NSLocalizedString(@"Memory Failure", @"Error description");
  63. reason = NSLocalizedString(@"Failed to allocate memory", @"Error reason");
  64. break;
  65. case kCCAlignmentError:
  66. description = NSLocalizedString(@"Alignment Error", @"Error description");
  67. reason = NSLocalizedString(@"Input size to encryption algorithm was not aligned correctly", @"Error reason");
  68. break;
  69. case kCCDecodeError:
  70. description = NSLocalizedString(@"Decode Error", @"Error description");
  71. reason = NSLocalizedString(@"Input data did not decode or decrypt correctly", @"Error reason");
  72. break;
  73. case kCCUnimplemented:
  74. description = NSLocalizedString(@"Unimplemented Function", @"Error description");
  75. reason = NSLocalizedString(@"Function not implemented for the current algorithm", @"Error reason");
  76. break;
  77. default:
  78. description = NSLocalizedString(@"Unknown Error", @"Error description");
  79. break;
  80. }
  81. NSMutableDictionary * userInfo = [[NSMutableDictionary alloc] init];
  82. [userInfo setObject: description forKey: NSLocalizedDescriptionKey];
  83. if ( reason != nil )
  84. [userInfo setObject: reason forKey: NSLocalizedFailureReasonErrorKey];
  85. NSError * result = [NSError errorWithDomain: kCommonCryptoErrorDomain code: status userInfo: userInfo];
  86. #if !__has_feature(objc_arc)
  87. [userInfo release];
  88. #endif
  89. return ( result );
  90. }
  91. @end
  92. #pragma mark -
  93. @implementation NSData (CommonDigest)
  94. //- (NSData *) MD2Sum
  95. //{
  96. // unsigned char hash[CC_MD2_DIGEST_LENGTH];
  97. // (void) CC_MD2( [self bytes], (CC_LONG)[self length], hash );
  98. // return ( [NSData dataWithBytes: hash length: CC_MD2_DIGEST_LENGTH] );
  99. //}
  100. //
  101. //- (NSData *) MD4Sum
  102. //{
  103. // unsigned char hash[CC_MD4_DIGEST_LENGTH];
  104. // (void) CC_MD4( [self bytes], (CC_LONG)[self length], hash );
  105. // return ( [NSData dataWithBytes: hash length: CC_MD4_DIGEST_LENGTH] );
  106. //}
  107. //
  108. //- (NSData *) MD5Sum
  109. //{
  110. // unsigned char hash[CC_MD5_DIGEST_LENGTH];
  111. // (void) CC_MD5( [self bytes], (CC_LONG)[self length], hash );
  112. // return ( [NSData dataWithBytes: hash length: CC_MD5_DIGEST_LENGTH] );
  113. //}
  114. - (NSData *) SHA1Hash
  115. {
  116. unsigned char hash[CC_SHA1_DIGEST_LENGTH];
  117. (void) CC_SHA1( [self bytes], (CC_LONG)[self length], hash );
  118. return ( [NSData dataWithBytes: hash length: CC_SHA1_DIGEST_LENGTH] );
  119. }
  120. - (NSData *) SHA224Hash
  121. {
  122. unsigned char hash[CC_SHA224_DIGEST_LENGTH];
  123. (void) CC_SHA224( [self bytes], (CC_LONG)[self length], hash );
  124. return ( [NSData dataWithBytes: hash length: CC_SHA224_DIGEST_LENGTH] );
  125. }
  126. - (NSData *) SHA256Hash
  127. {
  128. unsigned char hash[CC_SHA256_DIGEST_LENGTH];
  129. (void) CC_SHA256( [self bytes], (CC_LONG)[self length], hash );
  130. return ( [NSData dataWithBytes: hash length: CC_SHA256_DIGEST_LENGTH] );
  131. }
  132. - (NSData *) SHA384Hash
  133. {
  134. unsigned char hash[CC_SHA384_DIGEST_LENGTH];
  135. (void) CC_SHA384( [self bytes], (CC_LONG)[self length], hash );
  136. return ( [NSData dataWithBytes: hash length: CC_SHA384_DIGEST_LENGTH] );
  137. }
  138. - (NSData *) SHA512Hash
  139. {
  140. unsigned char hash[CC_SHA512_DIGEST_LENGTH];
  141. (void) CC_SHA512( [self bytes], (CC_LONG)[self length], hash );
  142. return ( [NSData dataWithBytes: hash length: CC_SHA512_DIGEST_LENGTH] );
  143. }
  144. @end
  145. @implementation NSData (CommonCryptor)
  146. - (NSData *) AES256EncryptedDataUsingKey: (id) key error: (NSError **) error
  147. {
  148. CCCryptorStatus status = kCCSuccess;
  149. NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmAES128
  150. key: key
  151. options: kCCOptionPKCS7Padding
  152. error: &status];
  153. if ( result != nil )
  154. return ( result );
  155. if ( error != NULL )
  156. *error = [NSError errorWithCCCryptorStatus: status];
  157. return ( nil );
  158. }
  159. - (NSData *) decryptedAES256DataUsingKey: (id) key error: (NSError **) error
  160. {
  161. CCCryptorStatus status = kCCSuccess;
  162. NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmAES128
  163. key: key
  164. options: kCCOptionPKCS7Padding
  165. error: &status];
  166. if ( result != nil )
  167. return ( result );
  168. if ( error != NULL )
  169. *error = [NSError errorWithCCCryptorStatus: status];
  170. return ( nil );
  171. }
  172. - (NSData *) DESEncryptedDataUsingKey: (id) key error: (NSError **) error
  173. {
  174. CCCryptorStatus status = kCCSuccess;
  175. NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmDES
  176. key: key
  177. options: kCCOptionPKCS7Padding
  178. error: &status];
  179. if ( result != nil )
  180. return ( result );
  181. if ( error != NULL )
  182. *error = [NSError errorWithCCCryptorStatus: status];
  183. return ( nil );
  184. }
  185. - (NSData *) decryptedDESDataUsingKey: (id) key error: (NSError **) error
  186. {
  187. CCCryptorStatus status = kCCSuccess;
  188. NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmDES
  189. key: key
  190. options: kCCOptionPKCS7Padding
  191. error: &status];
  192. if ( result != nil )
  193. return ( result );
  194. if ( error != NULL )
  195. *error = [NSError errorWithCCCryptorStatus: status];
  196. return ( nil );
  197. }
  198. - (NSData *) CASTEncryptedDataUsingKey: (id) key error: (NSError **) error
  199. {
  200. CCCryptorStatus status = kCCSuccess;
  201. NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmCAST
  202. key: key
  203. options: kCCOptionPKCS7Padding
  204. error: &status];
  205. if ( result != nil )
  206. return ( result );
  207. if ( error != NULL )
  208. *error = [NSError errorWithCCCryptorStatus: status];
  209. return ( nil );
  210. }
  211. - (NSData *) decryptedCASTDataUsingKey: (id) key error: (NSError **) error
  212. {
  213. CCCryptorStatus status = kCCSuccess;
  214. NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmCAST
  215. key: key
  216. options: kCCOptionPKCS7Padding
  217. error: &status];
  218. if ( result != nil )
  219. return ( result );
  220. if ( error != NULL )
  221. *error = [NSError errorWithCCCryptorStatus: status];
  222. return ( nil );
  223. }
  224. @end
  225. static void FixKeyLengths( CCAlgorithm algorithm, NSMutableData * keyData, NSMutableData * ivData )
  226. {
  227. NSUInteger keyLength = [keyData length];
  228. switch ( algorithm )
  229. {
  230. case kCCAlgorithmAES128:
  231. {
  232. if ( keyLength < 16 )
  233. {
  234. [keyData setLength: 16];
  235. }
  236. else if ( keyLength < 24 )
  237. {
  238. [keyData setLength: 24];
  239. }
  240. else
  241. {
  242. [keyData setLength: 32];
  243. }
  244. break;
  245. }
  246. case kCCAlgorithmDES:
  247. {
  248. [keyData setLength: 8];
  249. break;
  250. }
  251. case kCCAlgorithm3DES:
  252. {
  253. [keyData setLength: 24];
  254. break;
  255. }
  256. case kCCAlgorithmCAST:
  257. {
  258. if ( keyLength < 5 )
  259. {
  260. [keyData setLength: 5];
  261. }
  262. else if ( keyLength > 16 )
  263. {
  264. [keyData setLength: 16];
  265. }
  266. break;
  267. }
  268. case kCCAlgorithmRC4:
  269. {
  270. if ( keyLength > 512 )
  271. [keyData setLength: 512];
  272. break;
  273. }
  274. default:
  275. break;
  276. }
  277. [ivData setLength: [keyData length]];
  278. }
  279. @implementation NSData (LowLevelCommonCryptor)
  280. - (NSData *) _runCryptor: (CCCryptorRef) cryptor result: (CCCryptorStatus *) status
  281. {
  282. size_t bufsize = CCCryptorGetOutputLength( cryptor, (size_t)[self length], true );
  283. void * buf = malloc( bufsize );
  284. size_t bufused = 0;
  285. size_t bytesTotal = 0;
  286. *status = CCCryptorUpdate( cryptor, [self bytes], (size_t)[self length],
  287. buf, bufsize, &bufused );
  288. if ( *status != kCCSuccess )
  289. {
  290. free( buf );
  291. return ( nil );
  292. }
  293. bytesTotal += bufused;
  294. // From Brent Royal-Gordon (Twitter: architechies):
  295. // Need to update buf ptr past used bytes when calling CCCryptorFinal()
  296. *status = CCCryptorFinal( cryptor, buf + bufused, bufsize - bufused, &bufused );
  297. if ( *status != kCCSuccess )
  298. {
  299. free( buf );
  300. return ( nil );
  301. }
  302. bytesTotal += bufused;
  303. return ( [NSData dataWithBytesNoCopy: buf length: bytesTotal] );
  304. }
  305. - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
  306. key: (id) key
  307. error: (CCCryptorStatus *) error
  308. {
  309. return ( [self dataEncryptedUsingAlgorithm: algorithm
  310. key: key
  311. initializationVector: nil
  312. options: 0
  313. error: error] );
  314. }
  315. - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
  316. key: (id) key
  317. options: (CCOptions) options
  318. error: (CCCryptorStatus *) error
  319. {
  320. return ( [self dataEncryptedUsingAlgorithm: algorithm
  321. key: key
  322. initializationVector: nil
  323. options: options
  324. error: error] );
  325. }
  326. - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
  327. key: (id) key
  328. initializationVector: (id) iv
  329. options: (CCOptions) options
  330. error: (CCCryptorStatus *) error
  331. {
  332. CCCryptorRef cryptor = NULL;
  333. CCCryptorStatus status = kCCSuccess;
  334. NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
  335. NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]);
  336. NSMutableData * keyData, * ivData;
  337. if ( [key isKindOfClass: [NSData class]] )
  338. keyData = (NSMutableData *) [key mutableCopy];
  339. else
  340. keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
  341. if ( [iv isKindOfClass: [NSString class]] )
  342. ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
  343. else
  344. ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
  345. #if !__has_feature(objc_arc)
  346. [keyData autorelease];
  347. [ivData autorelease];
  348. #endif
  349. // ensure correct lengths for key and iv data, based on algorithms
  350. FixKeyLengths( algorithm, keyData, ivData );
  351. status = CCCryptorCreate( kCCEncrypt, algorithm, options,
  352. [keyData bytes], [keyData length], [ivData bytes],
  353. &cryptor );
  354. if ( status != kCCSuccess )
  355. {
  356. if ( error != NULL )
  357. *error = status;
  358. return ( nil );
  359. }
  360. NSData * result = [self _runCryptor: cryptor result: &status];
  361. if ( (result == nil) && (error != NULL) )
  362. *error = status;
  363. CCCryptorRelease( cryptor );
  364. return ( result );
  365. }
  366. - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
  367. key: (id) key // data or string
  368. error: (CCCryptorStatus *) error
  369. {
  370. return ( [self decryptedDataUsingAlgorithm: algorithm
  371. key: key
  372. initializationVector: nil
  373. options: 0
  374. error: error] );
  375. }
  376. - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
  377. key: (id) key // data or string
  378. options: (CCOptions) options
  379. error: (CCCryptorStatus *) error
  380. {
  381. return ( [self decryptedDataUsingAlgorithm: algorithm
  382. key: key
  383. initializationVector: nil
  384. options: options
  385. error: error] );
  386. }
  387. - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
  388. key: (id) key // data or string
  389. initializationVector: (id) iv // data or string
  390. options: (CCOptions) options
  391. error: (CCCryptorStatus *) error
  392. {
  393. CCCryptorRef cryptor = NULL;
  394. CCCryptorStatus status = kCCSuccess;
  395. NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
  396. NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]);
  397. NSMutableData * keyData, * ivData;
  398. if ( [key isKindOfClass: [NSData class]] )
  399. keyData = (NSMutableData *) [key mutableCopy];
  400. else
  401. keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
  402. if ( [iv isKindOfClass: [NSString class]] )
  403. ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
  404. else
  405. ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
  406. #if !__has_feature(objc_arc)
  407. [keyData autorelease];
  408. [ivData autorelease];
  409. #endif
  410. // ensure correct lengths for key and iv data, based on algorithms
  411. FixKeyLengths( algorithm, keyData, ivData );
  412. status = CCCryptorCreate( kCCDecrypt, algorithm, options,
  413. [keyData bytes], [keyData length], [ivData bytes],
  414. &cryptor );
  415. if ( status != kCCSuccess )
  416. {
  417. if ( error != NULL )
  418. *error = status;
  419. return ( nil );
  420. }
  421. NSData * result = [self _runCryptor: cryptor result: &status];
  422. if ( (result == nil) && (error != NULL) )
  423. *error = status;
  424. CCCryptorRelease( cryptor );
  425. return ( result );
  426. }
  427. @end
  428. @implementation NSData (CommonHMAC)
  429. - (NSData *) HMACWithAlgorithm: (CCHmacAlgorithm) algorithm
  430. {
  431. return ( [self HMACWithAlgorithm: algorithm key: nil] );
  432. }
  433. - (NSData *) HMACWithAlgorithm: (CCHmacAlgorithm) algorithm key: (id) key
  434. {
  435. NSParameterAssert(key == nil || [key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
  436. NSData * keyData = nil;
  437. if ( [key isKindOfClass: [NSString class]] )
  438. keyData = [key dataUsingEncoding: NSUTF8StringEncoding];
  439. else
  440. keyData = (NSData *) key;
  441. // this could be either CC_SHA1_DIGEST_LENGTH or CC_MD5_DIGEST_LENGTH. SHA1 is larger.
  442. unsigned char buf[CC_SHA1_DIGEST_LENGTH];
  443. CCHmac( algorithm, [keyData bytes], [keyData length], [self bytes], [self length], buf );
  444. return ( [NSData dataWithBytes: buf length: (algorithm == kCCHmacAlgMD5 ? CC_MD5_DIGEST_LENGTH : CC_SHA1_DIGEST_LENGTH)] );
  445. }
  446. @end