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. - (NSData *) MD4Sum
  101. {
  102. unsigned char hash[CC_MD4_DIGEST_LENGTH];
  103. (void) CC_MD4( [self bytes], (CC_LONG)[self length], hash );
  104. return ( [NSData dataWithBytes: hash length: CC_MD4_DIGEST_LENGTH] );
  105. }
  106. - (NSData *) MD5Sum
  107. {
  108. unsigned char hash[CC_MD5_DIGEST_LENGTH];
  109. (void) CC_MD5( [self bytes], (CC_LONG)[self length], hash );
  110. return ( [NSData dataWithBytes: hash length: CC_MD5_DIGEST_LENGTH] );
  111. }
  112. - (NSData *) SHA1Hash
  113. {
  114. unsigned char hash[CC_SHA1_DIGEST_LENGTH];
  115. (void) CC_SHA1( [self bytes], (CC_LONG)[self length], hash );
  116. return ( [NSData dataWithBytes: hash length: CC_SHA1_DIGEST_LENGTH] );
  117. }
  118. - (NSData *) SHA224Hash
  119. {
  120. unsigned char hash[CC_SHA224_DIGEST_LENGTH];
  121. (void) CC_SHA224( [self bytes], (CC_LONG)[self length], hash );
  122. return ( [NSData dataWithBytes: hash length: CC_SHA224_DIGEST_LENGTH] );
  123. }
  124. - (NSData *) SHA256Hash
  125. {
  126. unsigned char hash[CC_SHA256_DIGEST_LENGTH];
  127. (void) CC_SHA256( [self bytes], (CC_LONG)[self length], hash );
  128. return ( [NSData dataWithBytes: hash length: CC_SHA256_DIGEST_LENGTH] );
  129. }
  130. - (NSData *) SHA384Hash
  131. {
  132. unsigned char hash[CC_SHA384_DIGEST_LENGTH];
  133. (void) CC_SHA384( [self bytes], (CC_LONG)[self length], hash );
  134. return ( [NSData dataWithBytes: hash length: CC_SHA384_DIGEST_LENGTH] );
  135. }
  136. - (NSData *) SHA512Hash
  137. {
  138. unsigned char hash[CC_SHA512_DIGEST_LENGTH];
  139. (void) CC_SHA512( [self bytes], (CC_LONG)[self length], hash );
  140. return ( [NSData dataWithBytes: hash length: CC_SHA512_DIGEST_LENGTH] );
  141. }
  142. @end
  143. @implementation NSData (CommonCryptor)
  144. - (NSData *) AES256EncryptedDataUsingKey: (id) key error: (NSError **) error
  145. {
  146. CCCryptorStatus status = kCCSuccess;
  147. NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmAES128
  148. key: key
  149. options: kCCOptionPKCS7Padding
  150. error: &status];
  151. if ( result != nil )
  152. return ( result );
  153. if ( error != NULL )
  154. *error = [NSError errorWithCCCryptorStatus: status];
  155. return ( nil );
  156. }
  157. - (NSData *) decryptedAES256DataUsingKey: (id) key error: (NSError **) error
  158. {
  159. CCCryptorStatus status = kCCSuccess;
  160. NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmAES128
  161. key: key
  162. options: kCCOptionPKCS7Padding
  163. error: &status];
  164. if ( result != nil )
  165. return ( result );
  166. if ( error != NULL )
  167. *error = [NSError errorWithCCCryptorStatus: status];
  168. return ( nil );
  169. }
  170. - (NSData *) DESEncryptedDataUsingKey: (id) key error: (NSError **) error
  171. {
  172. CCCryptorStatus status = kCCSuccess;
  173. NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmDES
  174. key: key
  175. options: kCCOptionPKCS7Padding
  176. error: &status];
  177. if ( result != nil )
  178. return ( result );
  179. if ( error != NULL )
  180. *error = [NSError errorWithCCCryptorStatus: status];
  181. return ( nil );
  182. }
  183. - (NSData *) decryptedDESDataUsingKey: (id) key error: (NSError **) error
  184. {
  185. CCCryptorStatus status = kCCSuccess;
  186. NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmDES
  187. key: key
  188. options: kCCOptionPKCS7Padding
  189. error: &status];
  190. if ( result != nil )
  191. return ( result );
  192. if ( error != NULL )
  193. *error = [NSError errorWithCCCryptorStatus: status];
  194. return ( nil );
  195. }
  196. - (NSData *) CASTEncryptedDataUsingKey: (id) key error: (NSError **) error
  197. {
  198. CCCryptorStatus status = kCCSuccess;
  199. NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmCAST
  200. key: key
  201. options: kCCOptionPKCS7Padding
  202. error: &status];
  203. if ( result != nil )
  204. return ( result );
  205. if ( error != NULL )
  206. *error = [NSError errorWithCCCryptorStatus: status];
  207. return ( nil );
  208. }
  209. - (NSData *) decryptedCASTDataUsingKey: (id) key error: (NSError **) error
  210. {
  211. CCCryptorStatus status = kCCSuccess;
  212. NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmCAST
  213. key: key
  214. options: kCCOptionPKCS7Padding
  215. error: &status];
  216. if ( result != nil )
  217. return ( result );
  218. if ( error != NULL )
  219. *error = [NSError errorWithCCCryptorStatus: status];
  220. return ( nil );
  221. }
  222. @end
  223. static void FixKeyLengths( CCAlgorithm algorithm, NSMutableData * keyData, NSMutableData * ivData )
  224. {
  225. NSUInteger keyLength = [keyData length];
  226. switch ( algorithm )
  227. {
  228. case kCCAlgorithmAES128:
  229. {
  230. if ( keyLength < 16 )
  231. {
  232. [keyData setLength: 16];
  233. }
  234. else if ( keyLength < 24 )
  235. {
  236. [keyData setLength: 24];
  237. }
  238. else
  239. {
  240. [keyData setLength: 32];
  241. }
  242. break;
  243. }
  244. case kCCAlgorithmDES:
  245. {
  246. [keyData setLength: 8];
  247. break;
  248. }
  249. case kCCAlgorithm3DES:
  250. {
  251. [keyData setLength: 24];
  252. break;
  253. }
  254. case kCCAlgorithmCAST:
  255. {
  256. if ( keyLength < 5 )
  257. {
  258. [keyData setLength: 5];
  259. }
  260. else if ( keyLength > 16 )
  261. {
  262. [keyData setLength: 16];
  263. }
  264. break;
  265. }
  266. case kCCAlgorithmRC4:
  267. {
  268. if ( keyLength > 512 )
  269. [keyData setLength: 512];
  270. break;
  271. }
  272. default:
  273. break;
  274. }
  275. [ivData setLength: [keyData length]];
  276. }
  277. @implementation NSData (LowLevelCommonCryptor)
  278. - (NSData *) _runCryptor: (CCCryptorRef) cryptor result: (CCCryptorStatus *) status
  279. {
  280. size_t bufsize = CCCryptorGetOutputLength( cryptor, (size_t)[self length], true );
  281. void * buf = malloc( bufsize );
  282. size_t bufused = 0;
  283. size_t bytesTotal = 0;
  284. *status = CCCryptorUpdate( cryptor, [self bytes], (size_t)[self length],
  285. buf, bufsize, &bufused );
  286. if ( *status != kCCSuccess )
  287. {
  288. free( buf );
  289. return ( nil );
  290. }
  291. bytesTotal += bufused;
  292. // From Brent Royal-Gordon (Twitter: architechies):
  293. // Need to update buf ptr past used bytes when calling CCCryptorFinal()
  294. *status = CCCryptorFinal( cryptor, buf + bufused, bufsize - bufused, &bufused );
  295. if ( *status != kCCSuccess )
  296. {
  297. free( buf );
  298. return ( nil );
  299. }
  300. bytesTotal += bufused;
  301. return ( [NSData dataWithBytesNoCopy: buf length: bytesTotal] );
  302. }
  303. - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
  304. key: (id) key
  305. error: (CCCryptorStatus *) error
  306. {
  307. return ( [self dataEncryptedUsingAlgorithm: algorithm
  308. key: key
  309. initializationVector: nil
  310. options: 0
  311. error: error] );
  312. }
  313. - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
  314. key: (id) key
  315. options: (CCOptions) options
  316. error: (CCCryptorStatus *) error
  317. {
  318. return ( [self dataEncryptedUsingAlgorithm: algorithm
  319. key: key
  320. initializationVector: nil
  321. options: options
  322. error: error] );
  323. }
  324. - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
  325. key: (id) key
  326. initializationVector: (id) iv
  327. options: (CCOptions) options
  328. error: (CCCryptorStatus *) error
  329. {
  330. CCCryptorRef cryptor = NULL;
  331. CCCryptorStatus status = kCCSuccess;
  332. NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
  333. NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]);
  334. NSMutableData * keyData, * ivData;
  335. if ( [key isKindOfClass: [NSData class]] )
  336. keyData = (NSMutableData *) [key mutableCopy];
  337. else
  338. keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
  339. if ( [iv isKindOfClass: [NSString class]] )
  340. ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
  341. else
  342. ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
  343. #if !__has_feature(objc_arc)
  344. [keyData autorelease];
  345. [ivData autorelease];
  346. #endif
  347. // ensure correct lengths for key and iv data, based on algorithms
  348. FixKeyLengths( algorithm, keyData, ivData );
  349. status = CCCryptorCreate( kCCEncrypt, algorithm, options,
  350. [keyData bytes], [keyData length], [ivData bytes],
  351. &cryptor );
  352. if ( status != kCCSuccess )
  353. {
  354. if ( error != NULL )
  355. *error = status;
  356. return ( nil );
  357. }
  358. NSData * result = [self _runCryptor: cryptor result: &status];
  359. if ( (result == nil) && (error != NULL) )
  360. *error = status;
  361. CCCryptorRelease( cryptor );
  362. return ( result );
  363. }
  364. - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
  365. key: (id) key // data or string
  366. error: (CCCryptorStatus *) error
  367. {
  368. return ( [self decryptedDataUsingAlgorithm: algorithm
  369. key: key
  370. initializationVector: nil
  371. options: 0
  372. error: error] );
  373. }
  374. - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
  375. key: (id) key // data or string
  376. options: (CCOptions) options
  377. error: (CCCryptorStatus *) error
  378. {
  379. return ( [self decryptedDataUsingAlgorithm: algorithm
  380. key: key
  381. initializationVector: nil
  382. options: options
  383. error: error] );
  384. }
  385. - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
  386. key: (id) key // data or string
  387. initializationVector: (id) iv // data or string
  388. options: (CCOptions) options
  389. error: (CCCryptorStatus *) error
  390. {
  391. CCCryptorRef cryptor = NULL;
  392. CCCryptorStatus status = kCCSuccess;
  393. NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
  394. NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]);
  395. NSMutableData * keyData, * ivData;
  396. if ( [key isKindOfClass: [NSData class]] )
  397. keyData = (NSMutableData *) [key mutableCopy];
  398. else
  399. keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
  400. if ( [iv isKindOfClass: [NSString class]] )
  401. ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
  402. else
  403. ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
  404. #if !__has_feature(objc_arc)
  405. [keyData autorelease];
  406. [ivData autorelease];
  407. #endif
  408. // ensure correct lengths for key and iv data, based on algorithms
  409. FixKeyLengths( algorithm, keyData, ivData );
  410. status = CCCryptorCreate( kCCDecrypt, algorithm, options,
  411. [keyData bytes], [keyData length], [ivData bytes],
  412. &cryptor );
  413. if ( status != kCCSuccess )
  414. {
  415. if ( error != NULL )
  416. *error = status;
  417. return ( nil );
  418. }
  419. NSData * result = [self _runCryptor: cryptor result: &status];
  420. if ( (result == nil) && (error != NULL) )
  421. *error = status;
  422. CCCryptorRelease( cryptor );
  423. return ( result );
  424. }
  425. @end
  426. @implementation NSData (CommonHMAC)
  427. - (NSData *) HMACWithAlgorithm: (CCHmacAlgorithm) algorithm
  428. {
  429. return ( [self HMACWithAlgorithm: algorithm key: nil] );
  430. }
  431. - (NSData *) HMACWithAlgorithm: (CCHmacAlgorithm) algorithm key: (id) key
  432. {
  433. NSParameterAssert(key == nil || [key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
  434. NSData * keyData = nil;
  435. if ( [key isKindOfClass: [NSString class]] )
  436. keyData = [key dataUsingEncoding: NSUTF8StringEncoding];
  437. else
  438. keyData = (NSData *) key;
  439. // this could be either CC_SHA1_DIGEST_LENGTH or CC_MD5_DIGEST_LENGTH. SHA1 is larger.
  440. unsigned char buf[CC_SHA1_DIGEST_LENGTH];
  441. CCHmac( algorithm, [keyData bytes], [keyData length], [self bytes], [self length], buf );
  442. return ( [NSData dataWithBytes: buf length: (algorithm == kCCHmacAlgMD5 ? CC_MD5_DIGEST_LENGTH : CC_SHA1_DIGEST_LENGTH)] );
  443. }
  444. @end