├── .gitignore ├── LICENSE ├── README.md ├── RNOpenSSLCryptor.h ├── RNOpenSSLCryptor.m ├── RNOpenSSLDecryptor.h ├── RNOpenSSLDecryptor.m ├── RNOpenSSLEncryptor.h ├── RNOpenSSLEncryptor.m └── RNOpenSSLTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Rob Napier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is an RNCryptor-style API in front of an OpenSSL-compatible encryption 2 | format. I don't recommend the OpenSSL format. It has several security problems 3 | that the [RNCryptor](https://github.com/RNCryptor/RNCryptor) format was designed 4 | to improve on. This used to be part of RNCryptor respository. I don't support 5 | this module anymore, but I know some people are using it, so I'm making it 6 | available here. -------------------------------------------------------------------------------- /RNOpenSSLCryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNOpenSSLCryptor 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 "RNOpenSSLDecryptor.h" 28 | #import "RNOpenSSLEncryptor.h" 29 | 30 | 31 | extern NSString *const kRNCryptorOpenSSLSaltedString; 32 | 33 | NSData *RNOpenSSLCryptorGetKey(NSString *password, NSData *salt, RNCryptorKeyDerivationSettings keySettings); 34 | NSData *RNOpenSSLCryptorGetIV(NSData *key, NSString *password, NSData *salt, RNCryptorKeyDerivationSettings keySettings); 35 | 36 | 37 | // 38 | //#import "RNCryptor.h" 39 | // 40 | // 41 | //@interface RNOpenSSLCryptor : NSObject 42 | //+ (RNOpenSSLCryptor *)openSSLCryptor; 43 | // 44 | //- (BOOL)encryptFromStream:(NSInputStream *)fromStream 45 | // toStream:(NSOutputStream *)toStream 46 | // password:(NSString *)password 47 | // error:(NSError **)error; 48 | // 49 | //- (BOOL)decryptFromStream:(NSInputStream *)fromStream 50 | // toStream:(NSOutputStream *)toStream 51 | // password:(NSString *)password 52 | // error:(NSError **)error; 53 | // 54 | //@end 55 | // 56 | //static const RNCryptorSettings kRNCryptorOpenSSLSettings = { 57 | // .algorithm = kCCAlgorithmAES128, 58 | // .mode = kCCModeCBC, 59 | // .blockSize = kCCBlockSizeAES128, 60 | // .IVSize = kCCBlockSizeAES128, 61 | // .padding = ccPKCS7Padding, 62 | // 63 | // .keySettings = { 64 | // .keySize = kCCKeySizeAES256, 65 | // .saltSize = 8, 66 | // .rounds = 1, 67 | // .PRF = kCCPRFHmacAlgSHA1 68 | // }, 69 | //}; 70 | -------------------------------------------------------------------------------- /RNOpenSSLCryptor.m: -------------------------------------------------------------------------------- 1 | //// 2 | //// RNOpenSSLCryptor 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 | 28 | // For aes-128: 29 | // 30 | // key = MD5(password + salt) 31 | // IV = MD5(Key + password + salt) 32 | 33 | // 34 | // For aes-256: 35 | // 36 | // Hash0 = '' 37 | // Hash1 = MD5(Hash0 + Password + Salt) 38 | // Hash2 = MD5(Hash1 + Password + Salt) 39 | // Hash3 = MD5(Hash2 + Password + Salt) 40 | // Hash4 = MD5(Hash3 + Password + Salt) 41 | // 42 | // Key = Hash1 + Hash2 43 | // IV = Hash3 + Hash4 44 | // 45 | 46 | // File Format: 47 | // 48 | // |Salted___||| 49 | // 50 | 51 | #import "RNOpenSSLCryptor.h" 52 | 53 | NSString *const kRNCryptorOpenSSLSaltedString = @"Salted__"; 54 | 55 | static NSData *GetHashForHash(NSData *hash, NSData *passwordSalt) { 56 | unsigned char md[CC_MD5_DIGEST_LENGTH]; 57 | 58 | NSMutableData *hashMaterial = [NSMutableData dataWithData:hash]; 59 | [hashMaterial appendData:passwordSalt]; 60 | CC_MD5([hashMaterial bytes], (CC_LONG)[hashMaterial length], md); 61 | 62 | return [NSData dataWithBytes:md length:sizeof(md)]; 63 | } 64 | 65 | 66 | NSData *RNOpenSSLCryptorGetKey(NSString *password, NSData *salt, RNCryptorKeyDerivationSettings keySettings) { 67 | // FIXME: This is all very inefficient; we repeat ourselves in IVForKey:... 68 | 69 | NSMutableData *key; 70 | NSMutableData *passwordSalt = [[password dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]; 71 | [passwordSalt appendData:salt]; 72 | 73 | if (keySettings.keySize != kCCKeySizeAES256) { 74 | // For aes-128: 75 | // 76 | // key = MD5(password + salt) 77 | // IV = MD5(Key + password + salt) 78 | unsigned char md[CC_MD5_DIGEST_LENGTH]; 79 | CC_MD5([passwordSalt bytes], (CC_LONG)[passwordSalt length], md); 80 | key = [NSData dataWithBytes:md length:sizeof(md)]; 81 | 82 | } else { 83 | // Hash0 = '' 84 | // Hash1 = MD5(Hash0 + Password + Salt) 85 | // Hash2 = MD5(Hash1 + Password + Salt) 86 | // Hash3 = MD5(Hash2 + Password + Salt) 87 | // Hash4 = MD5(Hash3 + Password + Salt) 88 | // 89 | // Key = Hash1 + Hash2 90 | // IV = Hash3 + Hash4 91 | 92 | NSData *hash1 = GetHashForHash(nil, passwordSalt); 93 | NSData *hash2 = GetHashForHash(hash1, passwordSalt); 94 | 95 | key = [hash1 mutableCopy]; 96 | [key appendData:hash2]; 97 | } 98 | return key; 99 | } 100 | 101 | NSData *RNOpenSSLCryptorGetIV(NSData *key, NSString *password, NSData *salt, RNCryptorKeyDerivationSettings keySettings) { 102 | 103 | NSMutableData *IV; 104 | NSMutableData *passwordSalt = [[password dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]; 105 | [passwordSalt appendData:salt]; 106 | 107 | if (keySettings.keySize != kCCKeySizeAES256) { 108 | // For aes-128: 109 | // 110 | // key = MD5(password + salt) 111 | // IV = MD5(Key + password + salt) 112 | IV = [GetHashForHash(key, passwordSalt) mutableCopy]; 113 | 114 | } else { 115 | 116 | // 117 | // For aes-256: 118 | // 119 | // Hash0 = '' 120 | // Hash1 = MD5(Hash0 + Password + Salt) 121 | // Hash2 = MD5(Hash1 + Password + Salt) 122 | // Hash3 = MD5(Hash2 + Password + Salt) 123 | // Hash4 = MD5(Hash3 + Password + Salt) 124 | // 125 | // Key = Hash1 + Hash2 126 | // IV = Hash3 + Hash4 127 | NSData *hash1 = GetHashForHash(nil, passwordSalt); 128 | NSData *hash2 = GetHashForHash(hash1, passwordSalt); 129 | NSData *hash3 = GetHashForHash(hash2, passwordSalt); 130 | NSData *hash4 = GetHashForHash(hash3, passwordSalt); 131 | 132 | IV = [hash3 mutableCopy]; 133 | [IV appendData:hash4]; 134 | } 135 | return IV; 136 | } 137 | 138 | 139 | 140 | // 141 | //const NSUInteger kSaltSize = 8; 142 | //NSString *const kSaltedString = @"Salted__"; 143 | // 144 | //@interface NSInputStream (RNCryptor) 145 | //- (BOOL)_RNGetData:(NSData **)data maxLength:(NSUInteger)maxLength error:(NSError **)error; 146 | //@end 147 | // 148 | //@implementation NSInputStream (RNCryptor) 149 | //- (BOOL)_RNGetData:(NSData **)data maxLength:(NSUInteger)maxLength error:(NSError **)error 150 | //{ 151 | // NSMutableData *buffer = [NSMutableData dataWithLength:maxLength]; 152 | // if ([self read:buffer.mutableBytes maxLength:maxLength] < 0) { 153 | // if (error) { 154 | // *error = [self streamError]; 155 | // return NO; 156 | // } 157 | // } 158 | // 159 | // *data = buffer; 160 | // return YES; 161 | //} 162 | //@end 163 | // 164 | //@interface NSOutputStream (RNCryptor) 165 | //- (BOOL)_RNWriteData:(NSData *)data error:(NSError **)error; 166 | //@end 167 | // 168 | //@implementation NSOutputStream (RNCryptor) 169 | //- (BOOL)_RNWriteData:(NSData *)data error:(NSError **)error 170 | //{ 171 | // // Writing 0 bytes will close the output stream. 172 | // // This is an undocumented side-effect. radar://9930518 173 | // if (data.length > 0) { 174 | // NSInteger bytesWritten = [self write:data.bytes 175 | // maxLength:data.length]; 176 | // if (bytesWritten != data.length) { 177 | // if (error) { 178 | // *error = [self streamError]; 179 | // } 180 | // return NO; 181 | // } 182 | // } 183 | // return YES; 184 | //} 185 | //@end 186 | // 187 | //@interface RNOpenSSLCryptor () 188 | //@end 189 | // 190 | //@implementation RNOpenSSLCryptor 191 | //+ (RNOpenSSLCryptor *)openSSLCryptor 192 | //{ 193 | // static dispatch_once_t once; 194 | // static id openSSLCryptor = nil; 195 | // 196 | // dispatch_once(&once, ^{openSSLCryptor = [[self alloc] init];}); 197 | // return openSSLCryptor; 198 | //} 199 | // 200 | //- (NSData *)hashForHash:(NSData *)hash passwordSalt:(NSData *)passwordSalt 201 | //{ 202 | // unsigned char md[CC_MD5_DIGEST_LENGTH]; 203 | // 204 | // NSMutableData *hashMaterial = [NSMutableData dataWithData:hash]; 205 | // [hashMaterial appendData:passwordSalt]; 206 | // CC_MD5([hashMaterial bytes], [hashMaterial length], md); 207 | // 208 | // return [NSData dataWithBytes:md length:sizeof(md)]; 209 | //} 210 | // 211 | //- (NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt 212 | //{ 213 | // // FIXME: This is all very inefficient; we repeat ourselves in IVForKey:... 214 | // 215 | // // Hash0 = '' 216 | // // Hash1 = MD5(Hash0 + Password + Salt) 217 | // // Hash2 = MD5(Hash1 + Password + Salt) 218 | // // Hash3 = MD5(Hash2 + Password + Salt) 219 | // // Hash4 = MD5(Hash3 + Password + Salt) 220 | // // 221 | // // Key = Hash1 + Hash2 222 | // // IV = Hash3 + Hash4 223 | // 224 | // NSMutableData *passwordSalt = [[password dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]; 225 | // [passwordSalt appendData:salt]; 226 | // 227 | // NSData *hash1 = [self hashForHash:nil passwordSalt:passwordSalt]; 228 | // NSData *hash2 = [self hashForHash:hash1 passwordSalt:passwordSalt]; 229 | // 230 | // NSMutableData *key = [hash1 mutableCopy]; 231 | // [key appendData:hash2]; 232 | // 233 | // return key; 234 | // 235 | //// // key = MD5(password + salt) 236 | //// unsigned char md[CC_MD5_DIGEST_LENGTH]; 237 | //// NSMutableData *keyMaterial = [NSMutableData dataWithData:[password dataUsingEncoding:NSUTF8StringEncoding]]; 238 | //// [keyMaterial appendData:salt]; 239 | //// CC_MD5([keyMaterial bytes], [keyMaterial length], md); 240 | //// NSData *key = [NSData dataWithBytes:md length:sizeof(md)]; 241 | //// return key; 242 | //} 243 | // 244 | //- (NSData *)IVForKey:(NSData *)key password:(NSString *)password salt:(NSData *)salt 245 | //{ 246 | // NSMutableData *passwordSalt = [[password dataUsingEncoding:NSUTF8StringEncoding] mutableCopy]; 247 | // [passwordSalt appendData:salt]; 248 | // 249 | // NSData *hash1 = [self hashForHash:nil passwordSalt:passwordSalt]; 250 | // NSData *hash2 = [self hashForHash:hash1 passwordSalt:passwordSalt]; 251 | // NSData *hash3 = [self hashForHash:hash2 passwordSalt:passwordSalt]; 252 | // NSData *hash4 = [self hashForHash:hash3 passwordSalt:passwordSalt]; 253 | // 254 | // NSMutableData *IV = [hash3 mutableCopy]; 255 | // [IV appendData:hash4]; 256 | // 257 | // return IV; 258 | // 259 | // 260 | //// // IV = MD5(Key + password + salt) 261 | //// unsigned char md[CC_MD5_DIGEST_LENGTH]; 262 | //// NSMutableData *IVMaterial = [NSMutableData dataWithData:key]; 263 | //// [IVMaterial appendData:[password dataUsingEncoding:NSUTF8StringEncoding]]; 264 | //// [IVMaterial appendData:salt]; 265 | //// CC_MD5([IVMaterial bytes], [IVMaterial length], md); 266 | //// NSData *IV = [NSData dataWithBytes:md length:sizeof(md)]; 267 | //// return IV; 268 | //} 269 | // 270 | //- (BOOL)decryptFromStream:(NSInputStream *)fromStream toStream:(NSOutputStream *)toStream password:(NSString *)password error:(NSError **)error 271 | //{ 272 | // NSData *salted; 273 | // NSData *encryptionKeySalt; 274 | // 275 | // [fromStream open]; 276 | // 277 | // if (![fromStream _RNGetData:&salted maxLength:[kSaltedString length] error:error] || 278 | // ![fromStream _RNGetData:&encryptionKeySalt maxLength:kSaltSize error:error]) { 279 | // return NO; 280 | // } 281 | // 282 | // if (![[[NSString alloc] initWithData:salted encoding:NSUTF8StringEncoding] isEqualToString:kSaltedString]) { 283 | // if (error) { 284 | // *error = [NSError errorWithDomain:kRNCryptorErrorDomain code:kRNCryptorUnknownHeader 285 | // userInfo:[NSDictionary dictionaryWithObject:NSLocalizedString(@"Could not find salt", @"Could not find salt") forKey:NSLocalizedDescriptionKey]]; 286 | // } 287 | // return NO; 288 | // } 289 | // 290 | // NSData *encryptionKey = [self keyForPassword:password salt:encryptionKeySalt]; 291 | // NSData *IV = [self IVForKey:encryptionKey password:password salt:encryptionKeySalt]; 292 | // 293 | // RNCryptor *cryptor = [[RNCryptor alloc] initWithSettings:kRNCryptorOpenSSLSettings]; 294 | // 295 | // return [cryptor performOperation:kCCDecrypt fromStream:fromStream readCallback:nil toStream:toStream writeCallback:nil encryptionKey:encryptionKey IV:IV footerSize:0 footer:nil error:error]; 296 | //} 297 | // 298 | // 299 | //- (BOOL)encryptFromStream:(NSInputStream *)fromStream toStream:(NSOutputStream *)toStream password:(NSString *)password error:(NSError **)error 300 | //{ 301 | // NSData *encryptionKeySalt = [RNCryptor randomDataOfLength:kSaltSize]; 302 | // NSData *encryptionKey = [self keyForPassword:password salt:encryptionKeySalt]; 303 | // NSData *IV = [self IVForKey:encryptionKey password:password salt:encryptionKeySalt]; 304 | // 305 | // [toStream open]; 306 | // NSData *headerData = [kSaltedString dataUsingEncoding:NSUTF8StringEncoding]; 307 | // if (![toStream _RNWriteData:headerData error:error] || 308 | // ![toStream _RNWriteData:encryptionKeySalt error:error] 309 | // ) { 310 | // return NO; 311 | // } 312 | // 313 | // RNCryptor *cryptor = [[RNCryptor alloc] initWithSettings:kRNCryptorOpenSSLSettings]; 314 | // return [cryptor performOperation:kCCEncrypt fromStream:fromStream readCallback:nil toStream:toStream writeCallback:nil encryptionKey:encryptionKey IV:IV footerSize:0 footer:nil error:error]; 315 | //} 316 | //@end 317 | -------------------------------------------------------------------------------- /RNOpenSSLDecryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNOpenSSLDecryptor 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 28 | #import "RNDecryptor.h" 29 | 30 | @interface RNOpenSSLDecryptor : RNDecryptor 31 | - (RNDecryptor *)initWithSettings:(RNCryptorSettings)settings 32 | password:(NSString *)password 33 | handler:(RNCryptorHandler)handler; 34 | 35 | - (RNDecryptor *)initWithSettings:(RNCryptorSettings)theSettings 36 | encryptionKey:(NSData *)anEncryptionKey 37 | IV:(NSData *)anIV 38 | handler:(RNCryptorHandler)aHandler; 39 | 40 | + (NSData *)decryptData:(NSData *)data withSettings:(RNCryptorSettings)settings password:(NSString *)password error:(NSError **)error; 41 | + (NSData *)decryptData:(NSData *)data withSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey IV:(NSData *)IV error:(NSError **)error; 42 | 43 | @end -------------------------------------------------------------------------------- /RNOpenSSLDecryptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNOpenSSLDecryptor 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 | 28 | #import "RNOpenSSLDecryptor.h" 29 | #import "RNCryptor+Private.h" 30 | #import "RNCryptorEngine.h" 31 | #import "RNOpenSSLCryptor.h" 32 | 33 | @interface RNDecryptor (Private) 34 | @property (nonatomic, readwrite, strong) NSMutableData *inData; 35 | @property (nonatomic, readwrite, copy) NSData *encryptionKey; 36 | @property (nonatomic, readwrite, copy) NSData *HMACKey; 37 | @property (nonatomic, readwrite, copy) NSString *password; 38 | @end 39 | 40 | @interface RNOpenSSLDecryptor () 41 | @property (nonatomic, readwrite, assign) RNCryptorSettings settings; 42 | @property (nonatomic, readwrite, copy) NSString *password; 43 | @end 44 | 45 | @implementation RNOpenSSLDecryptor 46 | @synthesize password = _password; 47 | @synthesize settings = _settings; 48 | 49 | + (NSData *)decryptData:(NSData *)data withSettings:(RNCryptorSettings)settings password:(NSString *)password error:(NSError **)error 50 | { 51 | RNDecryptor *cryptor = [[self alloc] initWithSettings:settings password:password handler:^(RNCryptor *c, NSData *d) {}]; 52 | return [self synchronousResultForCryptor:cryptor data:data error:error]; 53 | } 54 | 55 | + (NSData *)decryptData:(NSData *)data withSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey IV:(NSData *)IV error:(NSError **)error 56 | { 57 | RNDecryptor *cryptor = [[self alloc] initWithSettings:settings encryptionKey:encryptionKey IV:IV handler:^(RNCryptor *c, NSData *d) {}]; 58 | return [self synchronousResultForCryptor:cryptor data:data error:error]; 59 | } 60 | 61 | - (RNDecryptor *)initWithSettings:(RNCryptorSettings)theSettings encryptionKey:(NSData *)anEncryptionKey IV:(NSData *)anIV handler:(RNCryptorHandler)aHandler 62 | { 63 | NSParameterAssert(anEncryptionKey != nil); 64 | 65 | self = [super initWithHandler:aHandler]; 66 | if (self) { 67 | NSError *error = nil; 68 | self.engine = [[RNCryptorEngine alloc] initWithOperation:kCCDecrypt 69 | settings:theSettings 70 | key:anEncryptionKey 71 | IV:anIV 72 | error:&error]; 73 | if (!self.engine) { 74 | [self cleanupAndNotifyWithError:error]; 75 | self = nil; 76 | return nil; 77 | } 78 | self.HMACLength = 0; 79 | self.settings = theSettings; 80 | } 81 | 82 | return self; 83 | } 84 | 85 | - (RNDecryptor *)initWithSettings:(RNCryptorSettings)theSettings password:(NSString *)aPassword handler:(RNCryptorHandler)aHandler 86 | { 87 | NSParameterAssert(aPassword != nil); 88 | 89 | self = [super initWithHandler:aHandler]; 90 | if (self) { 91 | self.HMACLength = 0; 92 | self.password = aPassword; 93 | self.settings = theSettings; 94 | } 95 | 96 | return self; 97 | } 98 | 99 | - (RNDecryptor *)initWithEncryptionKey:(NSData *)encryptionKey 100 | HMACKey:(NSData *)HMACKey 101 | handler:(RNCryptorHandler)handler 102 | { 103 | NSAssert(NO, @"%s -- Cannot be used in OpenSSL mode. An IV or password is required", __func__); 104 | return nil; 105 | } 106 | 107 | - (RNDecryptor *)initWithPassword:(NSString *)password 108 | handler:(RNCryptorHandler)handler 109 | { 110 | NSAssert(NO, @"%s -- Cannot be used in OpenSSL mode. Settings are required", __func__); 111 | return nil; 112 | } 113 | 114 | + (NSData *)decryptData:(NSData *)data withPassword:(NSString *)password error:(NSError **)error 115 | { 116 | NSAssert(NO, @"%s -- Cannot be used in OpenSSL mode. Settings are required", __func__); 117 | return nil; 118 | 119 | } 120 | 121 | + (NSData *)decryptData:(NSData *)data withEncryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey error:(NSError **)error 122 | { 123 | NSAssert(NO, @"%s -- Cannot be used in OpenSSL mode. Settings are required", __func__); 124 | return nil; 125 | } 126 | 127 | - (void)consumeHeaderFromData:(NSMutableData *)data 128 | { 129 | RNCryptorSettings settings = self.settings; 130 | if (data.length < [kRNCryptorOpenSSLSaltedString length] + settings.keySettings.saltSize) { 131 | return; 132 | } 133 | 134 | NSString *saltedPrefix = [[NSString alloc] initWithData:[data _RNConsumeToIndex:[kRNCryptorOpenSSLSaltedString length]] encoding:NSUTF8StringEncoding]; 135 | if (![kRNCryptorOpenSSLSaltedString isEqualToString:saltedPrefix]) { 136 | [self cleanupAndNotifyWithError:[NSError errorWithDomain:kRNCryptorErrorDomain 137 | code:kRNCryptorUnknownHeader 138 | userInfo:[NSDictionary dictionaryWithObject:@"Unknown header" /* DNL */ 139 | forKey:NSLocalizedDescriptionKey]]]; 140 | return; 141 | } 142 | 143 | NSData *salt = [data _RNConsumeToIndex:settings.keySettings.saltSize]; 144 | NSData *key = RNOpenSSLCryptorGetKey(self.password, salt, settings.keySettings); 145 | NSData *IV = RNOpenSSLCryptorGetIV(key, self.password, salt, settings.keySettings); 146 | NSError *error = nil; 147 | 148 | self.engine = [[RNCryptorEngine alloc] initWithOperation:kCCDecrypt 149 | settings:settings 150 | key:key 151 | IV:IV 152 | error:&error]; 153 | if (!self.engine) { 154 | [self cleanupAndNotifyWithError:error]; 155 | return; 156 | } 157 | } 158 | 159 | @end -------------------------------------------------------------------------------- /RNOpenSSLEncryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNOpenSSLEncryptor 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 28 | #import "RNEncryptor.h" 29 | 30 | @interface RNOpenSSLEncryptor : RNEncryptor 31 | + (NSData *)encryptData:(NSData *)data withSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey IV:(NSData *)IV error:(NSError **)error; 32 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)theSettings encryptionKey:(NSData *)anEncryptionKey IV:(NSData *)anIV handler:(RNCryptorHandler)aHandler; 33 | @end -------------------------------------------------------------------------------- /RNOpenSSLEncryptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNOpenSSLEncryptor 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 | // For aes-128: 28 | // 29 | // key = MD5(password + salt) 30 | // IV = MD5(Key + password + salt) 31 | 32 | // 33 | // For aes-256: 34 | // 35 | // Hash0 = '' 36 | // Hash1 = MD5(Hash0 + Password + Salt) 37 | // Hash2 = MD5(Hash1 + Password + Salt) 38 | // Hash3 = MD5(Hash2 + Password + Salt) 39 | // Hash4 = MD5(Hash3 + Password + Salt) 40 | // 41 | // Key = Hash1 + Hash2 42 | // IV = Hash3 + Hash4 43 | // 44 | 45 | // File Format: 46 | // 47 | // |Salted___||| 48 | 49 | #import "RNOpenSSLEncryptor.h" 50 | #import "RNCryptor+Private.h" 51 | #import "RNCryptorEngine.h" 52 | #import "RNOpenSSLCryptor.h" 53 | 54 | @interface RNOpenSSLEncryptor () 55 | @property (nonatomic, readwrite, strong) NSData *encryptionSalt; 56 | @end 57 | 58 | @implementation RNOpenSSLEncryptor 59 | @synthesize encryptionSalt = _encryptionSalt; 60 | 61 | + (NSData *)encryptData:(NSData *)data withSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey IV:(NSData *)IV error:(NSError **)error 62 | { 63 | RNEncryptor *cryptor = [[self alloc] initWithSettings:settings encryptionKey:encryptionKey IV:IV handler:^(RNCryptor *c, NSData *d) {}]; 64 | return [self synchronousResultForCryptor:cryptor data:data error:error]; 65 | } 66 | 67 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey handler:(RNCryptorHandler)handler 68 | { 69 | NSAssert(NO, @"%s -- Cannot be used in OpenSSL mode. An IV or password is required", __func__); 70 | return nil; 71 | } 72 | 73 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)theSettings encryptionKey:(NSData *)anEncryptionKey IV:(NSData *)anIV handler:(RNCryptorHandler)aHandler 74 | { 75 | self = [super initWithHandler:aHandler]; 76 | if (self) { 77 | NSError *error = nil; 78 | self.engine = [[RNCryptorEngine alloc] initWithOperation:kCCEncrypt 79 | settings:theSettings 80 | key:anEncryptionKey 81 | IV:anIV 82 | error:&error]; 83 | if (!self.engine) { 84 | [self cleanupAndNotifyWithError:error]; 85 | self = nil; 86 | return nil; 87 | } 88 | self.HMACLength = 0; 89 | } 90 | 91 | return self; 92 | } 93 | 94 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)theSettings password:(NSString *)aPassword handler:(RNCryptorHandler)aHandler 95 | { 96 | NSParameterAssert(aPassword != nil); 97 | 98 | NSData *encryptionSalt = [[self class] randomDataOfLength:theSettings.keySettings.saltSize]; 99 | NSData *encryptionKey = RNOpenSSLCryptorGetKey(aPassword, encryptionSalt, theSettings.keySettings); 100 | NSData *IV = RNOpenSSLCryptorGetIV(encryptionKey, aPassword, encryptionSalt, theSettings.keySettings); 101 | self = [self initWithSettings:theSettings 102 | encryptionKey:encryptionKey 103 | IV:IV 104 | handler:aHandler]; 105 | if (self) { 106 | self.options |= kRNCryptorOptionHasPassword; 107 | self.encryptionSalt = encryptionSalt; 108 | } 109 | return self; 110 | } 111 | 112 | - (NSData *)header 113 | { 114 | NSMutableData *headerData = [NSMutableData data]; 115 | if (kRNCryptorOptionHasPassword == (self.options & kRNCryptorOptionHasPassword)) { 116 | [headerData appendData:[kRNCryptorOpenSSLSaltedString dataUsingEncoding:NSUTF8StringEncoding]]; 117 | [headerData appendData:self.encryptionSalt]; 118 | } 119 | return headerData; 120 | } 121 | 122 | @end -------------------------------------------------------------------------------- /RNOpenSSLTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNOpenSSLTests.m 3 | // RNCryptor 4 | // 5 | // Created by Rob Napier on 12/12/13. 6 | // Copyright (c) 2013 Rob Napier. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RNCryptor.h" 11 | #import "RNOpenSSLCryptor.h" 12 | #import "RNCryptorTestHelpers.h" 13 | 14 | @interface RNOpenSSLTests : XCTestCase 15 | 16 | @end 17 | 18 | @implementation RNOpenSSLTests 19 | 20 | - (void)setUp 21 | { 22 | [super setUp]; 23 | // Put setup code here; it will be run once, before the first test case. 24 | } 25 | 26 | - (void)tearDown 27 | { 28 | // Put teardown code here; it will be run once, after the last test case. 29 | [super tearDown]; 30 | } 31 | 32 | // echo Test data | openssl enc -aes-256-cbc -out test.enc -k Passw0rd 33 | 34 | static NSString *const kOpenSSLString = @"Test data\n"; 35 | static NSString *const kOpenSSLPath = @"openssl.enc"; 36 | static NSString *const kOpenSSLPassword = @"Passw0rd"; 37 | 38 | - (void)testOpenSSLEncrypt 39 | { 40 | NSError *error = nil; 41 | 42 | NSData *encryptedData = [RNOpenSSLEncryptor encryptData:[kOpenSSLString dataUsingEncoding:NSUTF8StringEncoding] 43 | withSettings:kRNCryptorAES256Settings 44 | password:kOpenSSLPassword 45 | error:&error]; 46 | XCTAssertNotNil(encryptedData, @"Did not encrypt"); 47 | XCTAssertNil(error, @"Error:%@", error); 48 | 49 | NSString *encryptedFile = CreateTemporaryFilePath(); 50 | NSString *decryptedFile = CreateTemporaryFilePath(); 51 | [encryptedData writeToFile:encryptedFile atomically:NO]; 52 | 53 | NSString *cmd = [NSString stringWithFormat:@"/usr/bin/openssl enc -d -aes-256-cbc -k %@ -in %@ -out %@", kOpenSSLPassword, encryptedFile, decryptedFile]; 54 | XCTAssertEqual(system([cmd UTF8String]), 0, @"System call failed"); 55 | 56 | NSString *decryptedString = [NSString stringWithContentsOfFile:decryptedFile encoding:NSUTF8StringEncoding error:&error]; 57 | XCTAssertEqualObjects(decryptedString, kOpenSSLString, @"Decryption doesn't match: %@", error); 58 | } 59 | 60 | - (void)testOpenSSLDecrypt 61 | { 62 | NSData *encryptedData = [NSData dataWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:kOpenSSLPath ofType:nil]]; 63 | 64 | NSError *error = nil; 65 | NSData *decryptedData = [RNOpenSSLDecryptor decryptData:encryptedData 66 | withSettings:kRNCryptorAES256Settings 67 | password:kOpenSSLPassword 68 | error:&error]; 69 | XCTAssertNotNil(decryptedData, @"Did not decrypt"); 70 | XCTAssertNil(error, @"Error:%@", error); 71 | 72 | NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding]; 73 | XCTAssertEqualObjects(decryptedString, kOpenSSLString, @"Decrypted data does not match"); 74 | } 75 | 76 | - (void)testOpenSSLDecryptStream { 77 | NSString *filePath = [[NSBundle bundleForClass:[self class]] pathForResource:kOpenSSLPath ofType:nil]; 78 | 79 | NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:filePath]; 80 | [inputStream open]; 81 | 82 | __block NSOutputStream *outputStream = [[NSOutputStream alloc] initToMemory]; 83 | __block NSError *decryptionError = nil; 84 | [outputStream open]; 85 | 86 | __block dispatch_semaphore_t sem = dispatch_semaphore_create(0); 87 | 88 | size_t blockSize = 1024; 89 | 90 | __block RNDecryptor *decryptor; 91 | __block NSMutableData *buffer = [NSMutableData dataWithLength:blockSize]; 92 | 93 | 94 | dispatch_block_t readStreamBlock = ^{ 95 | [buffer setLength:blockSize]; 96 | NSInteger bytesRead = [inputStream read:[buffer mutableBytes] maxLength:blockSize]; 97 | if (bytesRead < 0) { 98 | XCTFail(@"Error reading block:%@", inputStream.streamError); 99 | [inputStream close]; 100 | dispatch_semaphore_signal(sem); 101 | } 102 | else if (bytesRead == 0) { 103 | [inputStream close]; 104 | [decryptor finish]; 105 | } 106 | else { 107 | [buffer setLength:bytesRead]; 108 | [decryptor addData:buffer]; 109 | NSLog(@"Sent %ld bytes to decryptor", (unsigned long)bytesRead); 110 | } 111 | }; 112 | 113 | decryptor = [[RNOpenSSLDecryptor alloc] initWithSettings:kRNCryptorAES256Settings 114 | password:kOpenSSLPassword 115 | handler:^(RNCryptor *cryptor, NSData *data) { 116 | NSLog(@"Received %d bytes", data.length); 117 | if (data.length > 0) { 118 | [outputStream write:data.bytes maxLength:data.length]; 119 | } 120 | if (cryptor.isFinished) { 121 | [outputStream close]; 122 | dispatch_semaphore_signal(sem); 123 | } 124 | else { 125 | readStreamBlock(); 126 | } 127 | }]; 128 | 129 | readStreamBlock(); 130 | 131 | long timedout = dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)); 132 | 133 | XCTAssertFalse(timedout, @"Test timed out."); 134 | XCTAssertNil(decryptionError, @"Decrypt error: %@", decryptionError); 135 | 136 | //Retrieve the decrypted data 137 | NSData *decryptedData = [outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; 138 | XCTAssertTrue([decryptedData length] > 0, @"Failed to decrypt."); 139 | 140 | NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding]; 141 | XCTAssertEqualObjects(decryptedString, kOpenSSLString, @"Decrypted data does not match"); 142 | } 143 | 144 | @end 145 | --------------------------------------------------------------------------------