RNCryptor.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. //
  2. // RNCryptor.m
  3. //
  4. // Copyright (c) 2012 Rob Napier
  5. //
  6. // This code is licensed under the MIT License:
  7. //
  8. // Permission is hereby granted, free of charge, to any person obtaining a
  9. // copy of this software and associated documentation files (the "Software"),
  10. // to deal in the Software without restriction, including without limitation
  11. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  12. // and/or sell copies of the Software, and to permit persons to whom the
  13. // Software is furnished to do so, subject to the following conditions:
  14. //
  15. // The above copyright notice and this permission notice shall be included in
  16. // all copies or substantial portions of the Software.
  17. //
  18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
  21. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. // DEALINGS IN THE SOFTWARE.
  25. //
  26. //
  27. #import "RNCryptor.h"
  28. #import "RNCryptor+Private.h"
  29. #import <CommonCrypto/CommonCryptor.h>
  30. #import <CommonCrypto/CommonKeyDerivation.h>
  31. #import <Security/SecRandom.h>
  32. #import <fcntl.h>
  33. const RNCryptorSettings kRNCryptorAES256Settings = {
  34. .algorithm = kCCAlgorithmAES128,
  35. .blockSize = kCCBlockSizeAES128,
  36. .IVSize = kCCBlockSizeAES128,
  37. .options = kCCOptionPKCS7Padding,
  38. .HMACAlgorithm = kCCHmacAlgSHA256,
  39. .HMACLength = CC_SHA256_DIGEST_LENGTH,
  40. .keySettings = {
  41. .keySize = kCCKeySizeAES256,
  42. .saltSize = 8,
  43. .PBKDFAlgorithm = kCCPBKDF2,
  44. .PRF = kCCPRFHmacAlgSHA1,
  45. .rounds = 10000
  46. },
  47. .HMACKeySettings = {
  48. .keySize = kCCKeySizeAES256,
  49. .saltSize = 8,
  50. .PBKDFAlgorithm = kCCPBKDF2,
  51. .PRF = kCCPRFHmacAlgSHA1,
  52. .rounds = 10000
  53. }
  54. };
  55. extern int SecRandomCopyBytes(SecRandomRef rnd, size_t count, uint8_t *bytes) __attribute__((weak_import));
  56. extern int
  57. CCKeyDerivationPBKDF( CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen,
  58. const uint8_t *salt, size_t saltLen,
  59. CCPseudoRandomAlgorithm prf, uint rounds,
  60. uint8_t *derivedKey, size_t derivedKeyLen) __attribute__((weak_import));
  61. NSString *const kRNCryptorErrorDomain = @"net.robnapier.RNCryptManager";
  62. const uint8_t kRNCryptorFileVersion = 3;
  63. // TODO: This is a slightly expensive solution, but it's convenient. May want to create a "walkable" data object
  64. @implementation NSMutableData (RNCryptor)
  65. - (NSData *)_RNConsumeToIndex:(NSUInteger)index
  66. {
  67. NSData *removed = [self subdataWithRange:NSMakeRange(0, index)];
  68. [self replaceBytesInRange:NSMakeRange(0, self.length - index) withBytes:([self mutableBytes] + index)];
  69. [self setLength:self.length - index];
  70. return removed;
  71. }
  72. @end
  73. @implementation RNCryptor
  74. @synthesize responseQueue = _responseQueue;
  75. @synthesize engine = _engine;
  76. @synthesize outData = __outData;
  77. @synthesize queue = _queue;
  78. @synthesize HMACLength = __HMACLength;
  79. @synthesize error = _error;
  80. @synthesize finished = _finished;
  81. @synthesize options = _options;
  82. @synthesize handler = _handler;
  83. + (NSData *)synchronousResultForCryptor:(RNCryptor *)cryptor data:(NSData *)inData error:(NSError **)anError
  84. {
  85. dispatch_semaphore_t sem = dispatch_semaphore_create(0);
  86. NSMutableData *data = [NSMutableData data];
  87. __block NSError *returnedError = nil;
  88. RNCryptorHandler handler = ^(RNCryptor *c, NSData *d) {
  89. [data appendData:d];
  90. if (c.isFinished) {
  91. returnedError = c.error;
  92. dispatch_semaphore_signal(sem);
  93. }
  94. };
  95. cryptor.handler = handler;
  96. dispatch_queue_t queue = dispatch_queue_create("net.robnapier.RNEncryptor.response", DISPATCH_QUEUE_SERIAL);
  97. cryptor.responseQueue = queue;
  98. [cryptor addData:inData];
  99. [cryptor finish];
  100. dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
  101. #if !OS_OBJECT_USE_OBJC
  102. dispatch_release(sem);
  103. if (queue) {
  104. dispatch_release(queue);
  105. }
  106. #endif
  107. if (returnedError) {
  108. if (anError) {
  109. *anError = returnedError;
  110. }
  111. return nil;
  112. }
  113. else {
  114. return data;
  115. }
  116. }
  117. // For use with OS X 10.6
  118. // Based on http://opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/API/CommonKeyDerivation.c
  119. /*-
  120. * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
  121. *
  122. * Permission to use, copy, modify, and distribute this software for any
  123. * purpose with or without fee is hereby granted, provided that the above
  124. * copyright notice and this permission notice appear in all copies.
  125. *
  126. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  127. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  128. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  129. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  130. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  131. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  132. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  133. */
  134. #define CC_MAX_PRF_WORKSPACE 128+4
  135. #define kCCPRFHmacAlgSHA1hlen CC_SHA1_DIGEST_LENGTH
  136. #define kCCPRFHmacAlgSHA224hlen CC_SHA224_DIGEST_LENGTH
  137. #define kCCPRFHmacAlgSHA256hlen CC_SHA256_DIGEST_LENGTH
  138. #define kCCPRFHmacAlgSHA384hlen CC_SHA384_DIGEST_LENGTH
  139. #define kCCPRFHmacAlgSHA512hlen CC_SHA512_DIGEST_LENGTH
  140. static size_t
  141. getPRFhlen(CCPseudoRandomAlgorithm prf)
  142. {
  143. switch(prf) {
  144. case kCCPRFHmacAlgSHA1: return kCCPRFHmacAlgSHA1hlen;
  145. case kCCPRFHmacAlgSHA224: return kCCPRFHmacAlgSHA224hlen;
  146. case kCCPRFHmacAlgSHA256: return kCCPRFHmacAlgSHA256hlen;
  147. case kCCPRFHmacAlgSHA384: return kCCPRFHmacAlgSHA384hlen;
  148. case kCCPRFHmacAlgSHA512: return kCCPRFHmacAlgSHA512hlen;
  149. default:
  150. NSCAssert(NO, @"Unknown prf: %d", prf);
  151. return 1;
  152. }
  153. }
  154. static void
  155. PRF(CCPseudoRandomAlgorithm prf, const char *password, size_t passwordLen, u_int8_t *salt, size_t saltLen, u_int8_t *output)
  156. {
  157. switch(prf) {
  158. case kCCPRFHmacAlgSHA1:
  159. CCHmac(kCCHmacAlgSHA1, password, passwordLen, salt, saltLen, output);
  160. break;
  161. case kCCPRFHmacAlgSHA224:
  162. CCHmac(kCCHmacAlgSHA224, password, passwordLen, salt, saltLen, output);
  163. break;
  164. case kCCPRFHmacAlgSHA256:
  165. CCHmac(kCCHmacAlgSHA256, password, passwordLen, salt, saltLen, output);
  166. break;
  167. case kCCPRFHmacAlgSHA384:
  168. CCHmac(kCCHmacAlgSHA384, password, passwordLen, salt, saltLen, output);
  169. break;
  170. case kCCPRFHmacAlgSHA512:
  171. CCHmac(kCCHmacAlgSHA512, password, passwordLen, salt, saltLen, output);
  172. break;
  173. }
  174. }
  175. static int
  176. RN_CCKeyDerivationPBKDF( CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen,
  177. const uint8_t *salt, size_t saltLen,
  178. CCPseudoRandomAlgorithm prf, uint rounds,
  179. uint8_t *derivedKey, size_t derivedKeyLen)
  180. {
  181. u_int8_t oldbuffer[CC_MAX_PRF_WORKSPACE], newbuffer[CC_MAX_PRF_WORKSPACE],
  182. saltCopy[CC_MAX_PRF_WORKSPACE+4], collector[CC_MAX_PRF_WORKSPACE];
  183. int rawblock, i, j;
  184. size_t r, nblocks;
  185. size_t hlen, offset;
  186. if(algorithm != kCCPBKDF2) return -1;
  187. /*
  188. * Check initial parameters
  189. */
  190. if (rounds < 1 || derivedKeyLen == 0)
  191. return -1; // bad parameters
  192. if (saltLen == 0 || saltLen > CC_MAX_PRF_WORKSPACE)
  193. return -1; // out of bounds parameters
  194. hlen = getPRFhlen(prf);
  195. /*
  196. * FromSpec: Let l be the number of hLen-octet blocks in the derived key, rounding up,
  197. * and let r be the number of octets in the last block:
  198. */
  199. nblocks = (derivedKeyLen+hlen-1)/hlen; // in the spec nblocks is referred to as l
  200. r = derivedKeyLen % hlen;
  201. r = (r) ? r: hlen;
  202. /*
  203. * Make a copy of the salt buffer so we can concatenate the
  204. * block counter for each series of rounds.
  205. */
  206. memcpy(saltCopy, salt, saltLen);
  207. bzero(derivedKey, derivedKeyLen);
  208. /*
  209. * FromSpec:
  210. *
  211. * For each block of the derived key apply the function F defined below to the password P,
  212. * the salt S, the iteration count c, and the block index to compute the block:
  213. *
  214. * F(P,S,c,i)=U1 \xorU2 \xor⋅⋅⋅\xorUc
  215. *
  216. * where
  217. * U1 =PRF(P,S||INT (i)),
  218. * U2 =PRF(P,U1),
  219. * ...
  220. * Uc = PRF (P, Uc-1) .
  221. */
  222. for(rawblock = 0; rawblock < nblocks; rawblock++) {
  223. int block = rawblock+1;
  224. size_t copyLen;
  225. offset = rawblock * hlen;
  226. copyLen = (block != nblocks) ? hlen: r;
  227. /*
  228. * FromSpec: Here, INT (i) is a four-octet encoding of the integer i, most significant octet first.
  229. */
  230. for(i=0; i<4; i++) saltCopy[saltLen+i] = (block >> 8*(3-i)) & 0xff;
  231. PRF(prf, password, passwordLen, saltCopy, saltLen+4, oldbuffer); // Initial PRF with the modified salt
  232. memcpy(collector, oldbuffer, hlen); // Initial value for this block of the derived key.
  233. for(i = 1; i < rounds; i++) {
  234. PRF(prf, password, passwordLen, oldbuffer, hlen, newbuffer); // Subsequent PRF with the previous result as the salt
  235. memcpy(oldbuffer, newbuffer, hlen);
  236. for(j = 0; j < hlen; j++) collector[j] ^= newbuffer[j]; // Xoring the round into the collector
  237. }
  238. memcpy(derivedKey+offset, collector, copyLen);
  239. }
  240. /*
  241. * Clear temp buffers.
  242. */
  243. bzero(oldbuffer, CC_MAX_PRF_WORKSPACE);
  244. bzero(newbuffer, CC_MAX_PRF_WORKSPACE);
  245. bzero(collector, CC_MAX_PRF_WORKSPACE);
  246. bzero(saltCopy, CC_MAX_PRF_WORKSPACE+4);
  247. return 0;
  248. }
  249. /* End code derived from CommonKeyDerivation.c */
  250. + (NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt settings:(RNCryptorKeyDerivationSettings)keySettings
  251. {
  252. NSMutableData *derivedKey = [NSMutableData dataWithLength:keySettings.keySize];
  253. // See Issue #77. V2 incorrectly calculated key for multi-byte characters.
  254. NSData *passwordData;
  255. if (keySettings.hasV2Password) {
  256. passwordData = [NSData dataWithBytes:[password UTF8String] length:[password length]];
  257. }
  258. else {
  259. passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
  260. }
  261. // Use the built-in PBKDF2 if it's available. Otherwise, we have our own. Hello crazy function pointer.
  262. int result;
  263. int (*PBKDF)(CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen,
  264. const uint8_t *salt, size_t saltLen,
  265. CCPseudoRandomAlgorithm prf, uint rounds,
  266. uint8_t *derivedKey, size_t derivedKeyLen);
  267. PBKDF = CCKeyDerivationPBKDF ?: RN_CCKeyDerivationPBKDF;
  268. result = PBKDF(keySettings.PBKDFAlgorithm, // algorithm
  269. passwordData.bytes, // password
  270. passwordData.length, // passwordLength
  271. salt.bytes, // salt
  272. salt.length, // saltLen
  273. keySettings.PRF, // PRF
  274. keySettings.rounds, // rounds
  275. derivedKey.mutableBytes, // derivedKey
  276. derivedKey.length); // derivedKeyLen
  277. // Do not log password here
  278. NSAssert(result == kCCSuccess, @"Unable to create AES key for password: %d", result);
  279. return derivedKey;
  280. }
  281. // For use on OS X 10.6
  282. // Based on http://www.opensource.apple.com/source/Security/Security-55179.1/sec/Security/SecFramework.c
  283. // Modified by Rob Napier April, 2013.
  284. /*
  285. * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved.
  286. *
  287. * @APPLE_LICENSE_HEADER_START@
  288. *
  289. * This file contains Original Code and/or Modifications of Original Code
  290. * as defined in and that are subject to the Apple Public Source License
  291. * Version 2.0 (the 'License'). You may not use this file except in
  292. * compliance with the License. Please obtain a copy of the License at
  293. * http://www.opensource.apple.com/apsl/ and read it before using this
  294. * file.
  295. *
  296. * The Original Code and all software distributed under the License are
  297. * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  298. * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  299. * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  300. * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  301. * Please see the License for the specific language governing rights and
  302. * limitations under the License.
  303. *
  304. * @APPLE_LICENSE_HEADER_END@
  305. */
  306. static int RN_SecRandomCopyBytes(void *rnd, size_t count, uint8_t *bytes) {
  307. static int kSecRandomFD;
  308. static dispatch_once_t onceToken;
  309. dispatch_once(&onceToken, ^{
  310. kSecRandomFD = open("/dev/random", O_RDONLY);
  311. });
  312. if (kSecRandomFD < 0)
  313. return -1;
  314. while (count) {
  315. ssize_t bytes_read = read(kSecRandomFD, bytes, count);
  316. if (bytes_read == -1) {
  317. if (errno == EINTR)
  318. continue;
  319. return -1;
  320. }
  321. if (bytes_read == 0) {
  322. return -1;
  323. }
  324. bytes += bytes_read;
  325. count -= bytes_read;
  326. }
  327. return 0;
  328. }
  329. /* End code dervied from SecFramework.c */
  330. + (NSData *)randomDataOfLength:(size_t)length
  331. {
  332. NSMutableData *data = [NSMutableData dataWithLength:length];
  333. int result;
  334. if (SecRandomCopyBytes != NULL) {
  335. result = SecRandomCopyBytes(NULL, length, data.mutableBytes);
  336. }
  337. else {
  338. result = RN_SecRandomCopyBytes(NULL, length, data.mutableBytes);
  339. }
  340. NSAssert(result == 0, @"Unable to generate random bytes: %d", errno);
  341. return data;
  342. }
  343. - (id)initWithHandler:(RNCryptorHandler)handler
  344. {
  345. NSParameterAssert(handler);
  346. self = [super init];
  347. if (self) {
  348. NSString *responseQueueName = [@"net.robnapier.response." stringByAppendingString:NSStringFromClass([self class])];
  349. _responseQueue = dispatch_queue_create([responseQueueName UTF8String], NULL);
  350. NSString *queueName = [@"net.robnapier." stringByAppendingString:NSStringFromClass([self class])];
  351. _queue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL);
  352. __outData = [NSMutableData data];
  353. _handler = [handler copy];
  354. }
  355. return self;
  356. }
  357. - (void)dealloc
  358. {
  359. if (_responseQueue) {
  360. #if !OS_OBJECT_USE_OBJC
  361. dispatch_release(_responseQueue);
  362. #endif
  363. _responseQueue = NULL;
  364. }
  365. if (_queue) {
  366. #if !OS_OBJECT_USE_OBJC
  367. dispatch_release(_queue);
  368. #endif
  369. _queue = NULL;
  370. }
  371. }
  372. - (void)setResponseQueue:(dispatch_queue_t)aResponseQueue
  373. {
  374. if (aResponseQueue) {
  375. #if !OS_OBJECT_USE_OBJC
  376. dispatch_retain(aResponseQueue);
  377. #endif
  378. }
  379. if (_responseQueue) {
  380. #if !OS_OBJECT_USE_OBJC
  381. dispatch_release(_responseQueue);
  382. #endif
  383. }
  384. _responseQueue = aResponseQueue;
  385. }
  386. - (void)addData:(NSData *)data
  387. {
  388. }
  389. - (void)finish
  390. {
  391. }
  392. - (void)cleanupAndNotifyWithError:(NSError *)error
  393. {
  394. self.error = error;
  395. self.finished = YES;
  396. if (self.handler) {
  397. dispatch_sync(self.responseQueue, ^{
  398. self.handler(self, self.outData);
  399. });
  400. self.handler = nil;
  401. }
  402. }
  403. - (BOOL)hasHMAC
  404. {
  405. return self.HMACLength > 0;
  406. }
  407. @end