RNDecryptor.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. //
  2. // RNDecryptor
  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. #import "RNDecryptor.h"
  27. #import "RNCryptor+Private.h"
  28. #import "RNCryptorEngine.h"
  29. #import <CommonCrypto/CommonHMAC.h>
  30. static const NSUInteger kPreambleSize = 2;
  31. @interface NSData (RNCryptor_ConsistentCompare)
  32. /** Compare two NSData in time proportional to the compared data (otherData)
  33. *
  34. * isEqual:-based comparisons stop comparing at the first difference. This can be used by attackers, in some situations,
  35. * to determine a secret value by considering the time required to compare the values.
  36. *
  37. * It is slightly better to call this as [secret rnc_isEqualInConsistentTime:attackersData] rather than the reverse,
  38. * but it is not a major issue either way. In the first case, the time required is proportional to the attacker's data,
  39. * which provides the attacker no information about the length of secret. In the second case, the time is proportional
  40. * to the length of secret, which leaks a small amount of informaiont, but in a way that does not varry in proportion to
  41. * the attacker's data.
  42. *
  43. * @param otherData data to compare
  44. * @returns YES if values are equal
  45. */
  46. - (BOOL)rnc_isEqualInConsistentTime:(NSData *)otherData;
  47. @end
  48. @implementation NSData (RNCryptor_ConstantCompare)
  49. - (BOOL)rnc_isEqualInConsistentTime:(NSData *)otherData {
  50. // The point of this routine is XOR the bytes of each data and accumulate the results with OR.
  51. // If any bytes are different, then the OR will accumulate some non-0 value.
  52. const uint8_t *myBytes = [self bytes];
  53. const NSUInteger myLength = [self length];
  54. const uint8_t *otherBytes = [otherData bytes];
  55. const NSUInteger otherLength = [otherData length];
  56. uint8_t result = otherLength != myLength; // Start with 0 (equal) only if our lengths are equal
  57. for (NSUInteger i = 0; i < otherLength; ++i) {
  58. // Use mod to wrap around ourselves if they are longer than we are.
  59. // Remember, we already broke equality if our lengths are different.
  60. result |= myBytes[i % myLength] ^ otherBytes[i];
  61. }
  62. return result == 0;
  63. }
  64. @end
  65. @interface RNDecryptor ()
  66. @property (nonatomic, readonly, strong) NSMutableData *inData;
  67. @property (nonatomic, readwrite, copy) NSData *encryptionKey;
  68. @property (nonatomic, readwrite, copy) NSData *HMACKey;
  69. @property (nonatomic, readwrite, copy) NSString *password;
  70. @property (nonatomic, readwrite, assign) BOOL hasV1HMAC;
  71. @property (nonatomic, readwrite, assign) RNCryptorSettings settings;
  72. @end
  73. @implementation RNDecryptor
  74. {
  75. CCHmacContext _HMACContext;
  76. NSMutableData *__inData;
  77. }
  78. @synthesize encryptionKey = _encryptionKey;
  79. @synthesize HMACKey = _HMACKey;
  80. @synthesize password = _password;
  81. @synthesize settings = _settings;
  82. + (NSData *)decryptData:(NSData *)theCipherText withSettings:(RNCryptorSettings)settings password:(NSString *)aPassword error:(NSError **)anError
  83. {
  84. RNDecryptor *cryptor = [[self alloc] initWithPassword:aPassword
  85. handler:^(RNCryptor *c, NSData *d) {}];
  86. cryptor.settings = settings;
  87. return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError];
  88. }
  89. + (NSData *)decryptData:(NSData *)theCipherText withSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey error:(NSError **)anError
  90. {
  91. RNDecryptor *cryptor = [[self alloc] initWithEncryptionKey:encryptionKey
  92. HMACKey:HMACKey
  93. handler:^(RNCryptor *c, NSData *d) {}];
  94. cryptor.settings = settings;
  95. return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError];
  96. }
  97. + (NSData *)decryptData:(NSData *)theCipherText withPassword:(NSString *)aPassword error:(NSError **)anError
  98. {
  99. RNDecryptor *cryptor = [[self alloc] initWithPassword:aPassword
  100. handler:^(RNCryptor *c, NSData *d) {}];
  101. return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError];
  102. }
  103. + (NSData *)decryptData:(NSData *)theCipherText withEncryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey error:(NSError **)anError;
  104. {
  105. RNDecryptor *cryptor = [[self alloc] initWithEncryptionKey:encryptionKey
  106. HMACKey:HMACKey
  107. handler:^(RNCryptor *c, NSData *d) {}];
  108. return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError];
  109. }
  110. - (RNDecryptor *)initWithEncryptionKey:(NSData *)anEncryptionKey HMACKey:(NSData *)anHMACKey handler:(RNCryptorHandler)aHandler
  111. {
  112. self = [super initWithHandler:aHandler];
  113. if (self) {
  114. _encryptionKey = [anEncryptionKey copy];
  115. _HMACKey = [anHMACKey copy];
  116. _settings = kRNCryptorAES256Settings;
  117. }
  118. return self;
  119. }
  120. - (RNDecryptor *)initWithPassword:(NSString *)aPassword handler:(RNCryptorHandler)aHandler
  121. {
  122. NSParameterAssert(aPassword != nil);
  123. self = [self initWithEncryptionKey:nil HMACKey:nil handler:aHandler];
  124. if (self) {
  125. _password = [aPassword copy];
  126. _settings = kRNCryptorAES256Settings;
  127. }
  128. return self;
  129. }
  130. - (NSMutableData *)inData
  131. {
  132. if (!__inData) {
  133. __inData = [NSMutableData data];
  134. }
  135. return __inData;
  136. }
  137. - (void)decryptData:(NSData *)data
  138. {
  139. dispatch_async(self.queue, ^{
  140. if (self.hasHMAC) {
  141. CCHmacUpdate(&_HMACContext, data.bytes, data.length);
  142. }
  143. NSError *error = nil;
  144. NSData *decryptedData = [self.engine addData:data error:&error];
  145. if (!decryptedData) {
  146. [self cleanupAndNotifyWithError:error];
  147. return;
  148. }
  149. [self.outData appendData:decryptedData];
  150. dispatch_sync(self.responseQueue, ^{
  151. self.handler(self, self.outData);
  152. });
  153. [self.outData setLength:0];
  154. });
  155. }
  156. - (void)addData:(NSData *)theData
  157. {
  158. if (self.isFinished) {
  159. return;
  160. }
  161. [self.inData appendData:theData];
  162. if (!self.engine) {
  163. [self consumeHeaderFromData:self.inData];
  164. }
  165. if (self.engine) {
  166. NSUInteger HMACLength = self.HMACLength;
  167. if (self.inData.length > HMACLength) {
  168. NSData *data = [self.inData _RNConsumeToIndex:self.inData.length - HMACLength];
  169. [self decryptData:data];
  170. }
  171. }
  172. }
  173. - (BOOL)updateOptionsForPreamble:(NSData *)preamble
  174. {
  175. const uint8_t *bytes = [preamble bytes];
  176. // See http://robnapier.net/blog/rncryptor-hmac-vulnerability-827 for information on the v1 bad HMAC
  177. #ifdef RNCRYPTOR_ALLOW_V1_BAD_HMAC
  178. if (bytes[0] == 1) {
  179. self.options = bytes[1];
  180. self.hasV1HMAC = YES;
  181. return YES;
  182. }
  183. #endif
  184. if (bytes[0] == 2) {
  185. self.options = bytes[1];
  186. RNCryptorSettings settings = self.settings;
  187. settings.keySettings.hasV2Password = YES;
  188. settings.HMACKeySettings.hasV2Password = YES;
  189. self.settings = settings;
  190. return YES;
  191. }
  192. if (bytes[0] == kRNCryptorFileVersion) {
  193. self.options = bytes[1];
  194. return YES;
  195. }
  196. return NO;
  197. }
  198. - (void)consumeHeaderFromData:(NSMutableData *)data
  199. {
  200. if (data.length < kPreambleSize) {
  201. return;
  202. }
  203. if (![self updateOptionsForPreamble:[data subdataWithRange:NSMakeRange(0, kPreambleSize)]]) {
  204. [self cleanupAndNotifyWithError:[NSError errorWithDomain:kRNCryptorErrorDomain
  205. code:kRNCryptorUnknownHeader
  206. userInfo:[NSDictionary dictionaryWithObject:@"Unknown header" /* DNL */
  207. forKey:NSLocalizedDescriptionKey]]];
  208. return;
  209. }
  210. NSUInteger headerSize = kPreambleSize + self.settings.IVSize;
  211. if (self.options & kRNCryptorOptionHasPassword) {
  212. headerSize += self.settings.keySettings.saltSize + self.settings.HMACKeySettings.saltSize;
  213. }
  214. if (data.length < headerSize) {
  215. return;
  216. }
  217. NSData *header = [data subdataWithRange:NSMakeRange(0, headerSize)]; // We'll need this for the HMAC later
  218. [[data _RNConsumeToIndex:kPreambleSize] mutableCopy]; // Throw away the preamble
  219. NSError *error = nil;
  220. if (self.options & kRNCryptorOptionHasPassword) {
  221. NSAssert(!self.encryptionKey && !self.HMACKey, @"Both password and the key (%d) or HMACKey (%d) are set.", self.encryptionKey != nil, self.HMACKey != nil);
  222. NSData *encryptionKeySalt = [data _RNConsumeToIndex:self.settings.keySettings.saltSize];
  223. NSData *HMACKeySalt = [data _RNConsumeToIndex:self.settings.HMACKeySettings.saltSize];
  224. self.encryptionKey = [[self class] keyForPassword:self.password salt:encryptionKeySalt settings:self.settings.keySettings];
  225. self.HMACKey = [[self class] keyForPassword:self.password salt:HMACKeySalt settings:self.settings.HMACKeySettings];
  226. self.password = nil; // Don't need this anymore.
  227. }
  228. NSData *IV = [data _RNConsumeToIndex:self.settings.IVSize];
  229. self.engine = [[RNCryptorEngine alloc] initWithOperation:kCCDecrypt settings:self.settings key:self.encryptionKey IV:IV error:&error];
  230. self.encryptionKey = nil; // Don't need this anymore
  231. if (!self.engine) {
  232. [self cleanupAndNotifyWithError:error];
  233. return;
  234. }
  235. if (self.HMACKey) {
  236. CCHmacInit(&_HMACContext, self.settings.HMACAlgorithm, self.HMACKey.bytes, self.HMACKey.length);
  237. self.HMACLength = self.settings.HMACLength;
  238. self.HMACKey = nil; // Don't need this anymore
  239. if (! self.hasV1HMAC) {
  240. CCHmacUpdate(&_HMACContext, [header bytes], [header length]);
  241. }
  242. }
  243. }
  244. - (void)finish
  245. {
  246. if (self.isFinished) {
  247. return;
  248. }
  249. dispatch_async(self.queue, ^{
  250. NSError *error = nil;
  251. NSData *decryptedData = [self.engine finishWithError:&error];
  252. if (!decryptedData) {
  253. [self cleanupAndNotifyWithError:error];
  254. return;
  255. }
  256. [self.outData appendData:decryptedData];
  257. if (self.hasHMAC) {
  258. NSMutableData *HMACData = [NSMutableData dataWithLength:self.HMACLength];
  259. CCHmacFinal(&_HMACContext, [HMACData mutableBytes]);
  260. if (![HMACData rnc_isEqualInConsistentTime:self.inData]) {
  261. [self cleanupAndNotifyWithError:[NSError errorWithDomain:kRNCryptorErrorDomain
  262. code:kRNCryptorHMACMismatch
  263. userInfo:[NSDictionary dictionaryWithObject:@"HMAC Mismatch" /* DNL */
  264. forKey:NSLocalizedDescriptionKey]]];
  265. return;
  266. }
  267. }
  268. [self cleanupAndNotifyWithError:nil];
  269. });
  270. }
  271. @end