├── .swift-version ├── RNCryptor ├── include │ ├── RNDecryptor.h │ ├── RNEncryptor.h │ ├── RNCryptor Package.h │ └── RNCryptor.h ├── RNCryptor-Prefix.pch ├── RNCryptorEngine.h ├── RNDecryptor.h ├── RNCryptor+Private.h ├── RNEncryptor.h ├── RNCryptor.h ├── RNCryptorEngine.m ├── RNEncryptor.m ├── RNDecryptor.m └── RNCryptor.m ├── RNCryptorV1.enc ├── RNCryptor OS X ├── en.lproj │ └── InfoPlist.strings ├── RNCryptor OS X-Prefix.pch └── RNCryptor OS X-Info.plist ├── RNCryptorTests ├── en.lproj │ └── InfoPlist.strings ├── openssl.enc ├── README.md ├── RNCryptorTestHelpers.h ├── RNCryptorTests-Info.plist ├── XCTestCase+RNCryptorVectorTests.h ├── RNCryptorTestHelpers.m ├── GenVectorTests ├── XCTestCase+RNCryptorVectorTests.m ├── Generated │ └── RNCryptorGeneratedVectorTests.m └── RNCryptorTests.m ├── .gitmodules ├── .gitignore ├── RNCryptor iOS ├── module.modulemap ├── RNCryptor iOS.h └── Info.plist ├── rncrypt ├── rncrypt-Prefix.pch ├── rncrypt │ ├── rncrypt-Prefix.pch │ ├── main.m │ └── rncrypt.1 ├── main.m └── rncrypt.xcodeproj │ └── project.pbxproj ├── RNCryptor.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ ├── cmk.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ └── rnapier.xcuserdatad │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── RNCryptor.xccheckout └── xcshareddata │ └── xcschemes │ ├── RNCryptor.xcscheme │ ├── RNCryptor OS X.xcscheme │ ├── rncrypt.xcscheme │ └── RNCryptor iOS.xcscheme ├── Package.swift ├── .travis.yml ├── AppledocSettings.plist ├── RNCryptor tvOS ├── RNCryptor tvOS.h └── Info.plist ├── RNCryptor watchOS ├── RNCryptor watchOS.h └── Info.plist ├── Changelog.md ├── RNCryptor-objc.podspec └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /RNCryptor/include/RNDecryptor.h: -------------------------------------------------------------------------------- 1 | ../RNDecryptor.h -------------------------------------------------------------------------------- /RNCryptor/include/RNEncryptor.h: -------------------------------------------------------------------------------- 1 | ../RNEncryptor.h -------------------------------------------------------------------------------- /RNCryptor/include/RNCryptor Package.h: -------------------------------------------------------------------------------- 1 | ../RNCryptor.h -------------------------------------------------------------------------------- /RNCryptorV1.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RNCryptor/RNCryptor-objc/HEAD/RNCryptorV1.enc -------------------------------------------------------------------------------- /RNCryptor OS X/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /RNCryptorTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Spec"] 2 | path = Spec 3 | url = https://github.com/RNCryptor/RNCryptor-Spec.git 4 | -------------------------------------------------------------------------------- /RNCryptorTests/openssl.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RNCryptor/RNCryptor-objc/HEAD/RNCryptorTests/openssl.enc -------------------------------------------------------------------------------- /RNCryptorTests/README.md: -------------------------------------------------------------------------------- 1 | openssl.enc: 2 | echo Test data | openssl enc -aes-256-cbc -out test.enc -k Passw0rd 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcuserdatad 2 | .idea 3 | 4 | # Eclipse files 5 | .buildpath 6 | .project 7 | .settings/ 8 | 9 | # MACOS files 10 | .DS_Store 11 | 12 | -------------------------------------------------------------------------------- /RNCryptor iOS/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module RNCryptor { 2 | umbrella header "RNCryptor iOS.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /rncrypt/rncrypt-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'rncrypt' target in the 'rncrypt' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /RNCryptor/RNCryptor-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'RNCrypt' target in the 'RNCrypt' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /rncrypt/rncrypt/rncrypt-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'rncrypt' target in the 'rncrypt' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/project.xcworkspace/xcuserdata/cmk.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RNCryptor/RNCryptor-objc/HEAD/RNCryptor.xcodeproj/project.xcworkspace/xcuserdata/cmk.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /RNCryptorTests/RNCryptorTestHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptorTestHelpers.h 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 | 11 | NSString * CreateTemporaryFilePath(void); 12 | -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RNCryptor OS X/RNCryptor OS X-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'RNCryptor OS X' target in the 'RNCryptor OS X' project 3 | // 4 | 5 | // For testing 6 | // #define RNCRYPTOR_ALLOW_V1_BAD_HMAC 7 | 8 | #ifdef __OBJC__ 9 | #endif 10 | 11 | #if ! __has_feature(objc_arc) 12 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 13 | #endif 14 | -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/project.xcworkspace/xcuserdata/rnapier.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "RNCryptor-objc", 7 | products: [ 8 | .library( 9 | name: "RNCryptor", 10 | targets: ["RNCryptor"] 11 | ), 12 | ], 13 | targets: [ 14 | .target( 15 | name: "RNCryptor", 16 | dependencies: [], 17 | path: "RNCryptor" 18 | ) 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | before_install: 4 | - git submodule update --init --recursive 5 | - brew update 6 | - brew upgrade xctool 7 | 8 | xcode_project: RNCryptor.xcodeproj 9 | xcode_scheme: RNCryptor 10 | xcode_sdk: iphonesimulator7.1 11 | 12 | matrix: 13 | include: 14 | - script: xctool -project RNCryptor.xcodeproj -scheme RNCryptor -sdk iphonesimulator7.0 build 15 | - script: xctool -project RNCryptor.xcodeproj -scheme "RNCryptor OS X" -sdk macosx10.9 build 16 | - script: xctool -project RNCryptor.xcodeproj -scheme "RNCryptor OS X" -sdk macosx10.10 build 17 | -------------------------------------------------------------------------------- /AppledocSettings.plist: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | --ignore 7 | 8 | RNCryptorTests 9 | 10 | --project-name 11 | RNCryptor 12 | --project-company 13 | Rob Napier 14 | --company-id 15 | net.robnapier 16 | --create-docset 17 | 18 | --install-docset 19 | 20 | --output 21 | doc 22 | 23 | 24 | -------------------------------------------------------------------------------- /RNCryptor iOS/RNCryptor iOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptor iOS.h 3 | // RNCryptor iOS 4 | // 5 | // Created by Daniel Brooks on 5/25/15. 6 | // Copyright (c) 2015 Rob Napier. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for RNCryptor iOS. 12 | FOUNDATION_EXPORT double RNCryptor_iOSVersionNumber; 13 | 14 | //! Project version string for RNCryptor iOS. 15 | FOUNDATION_EXPORT const unsigned char RNCryptor_iOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | 23 | -------------------------------------------------------------------------------- /RNCryptor tvOS/RNCryptor tvOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptor tvOS.h 3 | // RNCryptor tvOS 4 | // 5 | // Created by Jeff Kelley on 11/15/16. 6 | // Copyright © 2016 Rob Napier. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for RNCryptor tvOS. 12 | FOUNDATION_EXPORT double RNCryptor_tvOSVersionNumber; 13 | 14 | //! Project version string for RNCryptor tvOS. 15 | FOUNDATION_EXPORT const unsigned char RNCryptor_tvOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | 23 | -------------------------------------------------------------------------------- /RNCryptor watchOS/RNCryptor watchOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptor watchOS.h 3 | // RNCryptor watchOS 4 | // 5 | // Created by Jeff Kelley on 11/15/16. 6 | // Copyright © 2016 Rob Napier. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for RNCryptor watchOS. 12 | FOUNDATION_EXPORT double RNCryptor_watchOSVersionNumber; 13 | 14 | //! Project version string for RNCryptor watchOS. 15 | FOUNDATION_EXPORT const unsigned char RNCryptor_watchOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | 23 | -------------------------------------------------------------------------------- /RNCryptorTests/RNCryptorTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RNCryptor tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /RNCryptor watchOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /RNCryptorTests/XCTestCase+RNCryptorVectorTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptorVectorTests.h 3 | // RNCryptor 4 | // 5 | // Created by Rob Napier on 1/9/14. 6 | // Copyright (c) 2014 Rob Napier. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "RNEncryptor.h" 12 | #import "RNDecryptor.h" 13 | 14 | // Entry points for auto-generated test cases from GenVectorTests 15 | // Are in the form verify_{filename} 16 | 17 | @interface XCTestCase (RNCryptorVectorTests) 18 | - (void)verify_v3_kdf:(NSDictionary *)vector; 19 | - (void)verify_v3_password:(NSDictionary *)vector; 20 | - (void)verify_v3_key:(NSDictionary *)vector; 21 | 22 | - (void)verify_v2_kdf:(NSDictionary *)vector; 23 | - (void)verify_v2_password:(NSDictionary *)vector; 24 | 25 | - (void)verify_v1_kdf:(NSDictionary *)vector; 26 | - (void)verify_v1_password:(NSDictionary *)vector; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /RNCryptor iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /RNCryptorTests/RNCryptorTestHelpers.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptorTestHelpers.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 "RNCryptorTestHelpers.h" 10 | 11 | NSString * CreateTemporaryFilePath() 12 | { 13 | // Thanks to Matt Gallagher 14 | NSString *tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"RNCryptorTest.XXXXXX"]; 15 | const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation]; 16 | char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1); 17 | strcpy(tempFileNameCString, tempFileTemplateCString); 18 | int fileDescriptor = mkstemp(tempFileNameCString); 19 | 20 | NSCAssert(fileDescriptor >= 0, @"Failed to create temporary file"); 21 | 22 | NSString *tempFileName = 23 | [[NSFileManager defaultManager] 24 | stringWithFileSystemRepresentation:tempFileNameCString 25 | length:strlen(tempFileNameCString)]; 26 | 27 | free(tempFileNameCString); 28 | return tempFileName; 29 | } -------------------------------------------------------------------------------- /RNCryptor OS X/RNCryptor OS X-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2012 Rob Napier. All rights reserved. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /RNCryptor/include/RNCryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptor.h 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 Package.h" 28 | #import "RNDecryptor.h" 29 | #import "RNEncryptor.h" 30 | -------------------------------------------------------------------------------- /rncrypt/rncrypt/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // rncrypt 4 | // 5 | // Created by Rob Napier on 1/29/13. 6 | // Copyright (c) 2013 Rob Napier. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RNDecryptor.h" 11 | 12 | NSData *GetDataForHex(NSString *hex) { 13 | NSMutableData *data = [NSMutableData new]; 14 | unsigned char whole_byte; 15 | char byte_chars[3] = {'\0','\0','\0'}; 16 | int i; 17 | for (i=0; i < [hex length]/2; i++) { 18 | byte_chars[0] = [hex characterAtIndex:i*2]; 19 | byte_chars[1] = [hex characterAtIndex:i*2+1]; 20 | whole_byte = strtol(byte_chars, NULL, 16); 21 | [data appendBytes:&whole_byte length:1]; 22 | } 23 | return data; 24 | } 25 | 26 | int main(int argc, const char * argv[]) 27 | { 28 | @autoreleasepool { 29 | 30 | NSString *password = @"P@ssw0rd!"; 31 | NSString *messageString = @"02013F194AA9969CF70C8ACB76824DE4CB6CDCF78B7449A87C679FB8EDB6A0109C513481DE877F3A855A184C4947F2B3E8FEF7E916E4739F9F889A717FCAF277402866341008A09FD3EBAC7FA26C969DD7EE72CFB695547C971A75D8BF1CC5980E0C727BD9F97F6B7489F687813BEB94DEB61031260C246B9B0A78C2A52017AA8C92"; 32 | 33 | NSData *message = GetDataForHex(messageString); 34 | 35 | NSError *error; 36 | NSData *decrypted = [RNDecryptor decryptData:message withPassword:password error:&error]; 37 | 38 | NSLog(@"Result=%@", [[NSString alloc] initWithData:decrypted encoding:NSUTF8StringEncoding]); 39 | 40 | } 41 | return 0; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Version 3.0.6 2 | 3 | * Clean up self-retain warnings 4 | 5 | # Version 3.0.5 6 | 7 | * Clean up warnings for CocoaPods 8 | 9 | # Version 3.0.4 10 | 11 | * Remove non-modular header from framework header #11 12 | * Add watchOS and tvOS support. #8 13 | 14 | # Version 3.0.3 15 | 16 | * Upgrade to build on Xcode 9b1 17 | 18 | # Versin 3.0.2 19 | 20 | * Extract to its own repository 21 | 22 | # Version 3.0 23 | 24 | * Remove OpenSSL support. This has been moved to RNOpenSSLCryptor. 25 | * Remove warnings on OS X 10.8 26 | 27 | # Version 2.2 28 | 29 | Version 2.2 is a fairly large release. It's been almost a year since 2.1 came out, and there are many small and large bug fixes. 30 | 31 | V2.2 updates the file format from 2 to 3. It will read format 2 files, but will only write format 3. These are not readable by RNCryptor v2.1. See Issue #77 for details. The PHP, Python, and Ruby implementations also write format 3 and read format 2 or 3. 32 | 33 | ## Security Issues 34 | 35 | * Issue #91: Use constant time HMAC comparisons to avoid timing attacks 36 | * Issue #77: KeyForPassword() broken for multi-byte passwords (UTF-8) 37 | 38 | ## Other Noteable Changes 39 | 40 | * Improved PHP, Python, and Ruby implementations 41 | * Improved test cases, with test vectors 42 | * Issue #76: Support OSX in podspec 43 | * Resolved static analyzer warnings 44 | * Ensure compatibility with iOS 4.2 45 | * Accept settings to RNDecryptor (Issue #65) 46 | * Copy password rather than retain it (Issue #64) 47 | * Crash when reading v1 header 48 | -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/project.xcworkspace/xcshareddata/RNCryptor.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 3ABD3F64-A1E8-41CD-8789-63CBB720A99A 9 | IDESourceControlProjectName 10 | RNCryptor 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | DEDD58F0BAF28034A74179391A23AC2E0219CDC3 14 | github.com:RNCryptor/RNCryptor.git 15 | 16 | IDESourceControlProjectPath 17 | RNCryptor.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | DEDD58F0BAF28034A74179391A23AC2E0219CDC3 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | github.com:RNCryptor/RNCryptor.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | DEDD58F0BAF28034A74179391A23AC2E0219CDC3 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | DEDD58F0BAF28034A74179391A23AC2E0219CDC3 36 | IDESourceControlWCCName 37 | RNCryptor 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /RNCryptor/RNCryptorEngine.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptorEngine 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 29 | #import "RNCryptor.h" 30 | 31 | @interface RNCryptorEngine : NSObject 32 | - (RNCryptorEngine *)initWithOperation:(CCOperation)operation settings:(RNCryptorSettings)settings key:(NSData *)key IV:(NSData *)IV error:(NSError **)error; 33 | - (NSData *)addData:(NSData *)data error:(NSError **)error; 34 | - (NSData *)finishWithError:(NSError **)error; 35 | @end 36 | -------------------------------------------------------------------------------- /RNCryptor-objc.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'RNCryptor-objc' 3 | s.version = '3.0.6' 4 | s.summary = 'Encryptor/Decryptor for iOS.' 5 | s.authors = {'Rob Napier' => 'robnapier@gmail.com'} 6 | s.license = { 7 | :type => 'MIT', 8 | :text => <<-LIC 9 | This code is licensed under the MIT License: 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | LIC 17 | } 18 | s.source = { :git => 'https://github.com/RNCryptor/RNCryptor-objc.git', :tag => "#{s.version.to_s}" } 19 | s.description = 'Provides an easy-to-use, Objective-C interface to the AES functionality of CommonCrypto. Simplifies correct handling of password stretching (PBKDF2), salting, and IV.' 20 | s.homepage = 'https://github.com/rnapier/RNCryptor' 21 | s.source_files = 'RNCryptor/*.{h,m}' 22 | s.public_header_files = 'RNCryptor/*.h' 23 | s.private_header_files = "RNCryptor/RNCryptorEngine.h", "RNCryptor/RNCryptor+Private.h" 24 | s.requires_arc = true 25 | s.frameworks = 'Security' 26 | s.ios.deployment_target = '5.0' 27 | s.osx.deployment_target = '10.7' 28 | s.watchos.deployment_target = '2.0' 29 | s.tvos.deployment_target = '9.0' 30 | end 31 | 32 | -------------------------------------------------------------------------------- /RNCryptor/RNDecryptor.h: -------------------------------------------------------------------------------- 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 | 27 | #import 28 | #import "RNCryptor.h" 29 | 30 | 31 | @interface RNDecryptor : RNCryptor 32 | 33 | - (RNDecryptor *)initWithEncryptionKey:(NSData *)encryptionKey 34 | HMACKey:(NSData *)HMACKey 35 | handler:(RNCryptorHandler)handler; 36 | 37 | - (RNDecryptor *)initWithPassword:(NSString *)password 38 | handler:(RNCryptorHandler)handler; 39 | 40 | + (NSData *)decryptData:(NSData *)theCipherText withSettings:(RNCryptorSettings)settings password:(NSString *)aPassword error:(NSError **)anError; 41 | + (NSData *)decryptData:(NSData *)theCipherText withSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey error:(NSError **)anError; 42 | 43 | + (NSData *)decryptData:(NSData *)data withPassword:(NSString *)password error:(NSError **)error; 44 | + (NSData *)decryptData:(NSData *)data withEncryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey error:(NSError **)error; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /RNCryptorTests/GenVectorTests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'optparse' 4 | require File.join(File.dirname(__FILE__), '../Spec/vectors', 'vectorparser') 5 | 6 | @test_files = ["v3/kdf", "v3/password", "v3/key", 7 | "v2/kdf", "v2/password", 8 | "v1/kdf", "v1/password", 9 | ] 10 | 11 | @options = {} 12 | 13 | # Returns the text for an NSDictionary assignment from a hash 14 | def NSDictionaryForHash(hash) 15 | "@{\n" + hash.collect { |key, value| %Q( @"#{key}": @"#{value}") }.join(",\n") + "}" 16 | end 17 | 18 | # Output the file header to output stream 19 | def outputHeader(output) 20 | output << <<-HEADER 21 | // Automatically Generated by GenVectorTests 22 | #import "XCTestCase+RNCryptorVectorTests.h" 23 | @interface RNCryptorGeneratedVectorTests : XCTestCase 24 | @end 25 | @implementation RNCryptorGeneratedVectorTests 26 | HEADER 27 | end 28 | 29 | # Output the tests for a given filename to the output stream 30 | def outputTestsForFile(output, name) 31 | name_identifier = name.gsub('/', '_') 32 | VectorParser.new(@options[:vector_directory] + '/' + name).vectors.each do |vector| 33 | output << <<-TEST_CASE 34 | 35 | - (void)test_#{name_identifier}_#{vector["title"].gsub(/[ ()-]+/, '_')} { 36 | [self verify_#{name_identifier}:#{NSDictionaryForHash(vector)}]; 37 | } 38 | 39 | TEST_CASE 40 | 41 | end 42 | end 43 | 44 | # Output the footer to the output stream 45 | def outputFooter(output) 46 | output<< <<-FOOTER 47 | @end 48 | FOOTER 49 | end 50 | 51 | ################### 52 | ### MAIN 53 | ################### 54 | opt_parser = OptionParser.new do |opt| 55 | opt.banner = "Usage: GenVectorTest -o VectorTests.m " 56 | opt.separator "" 57 | opt.on("-o","--output PATH","path to output code") do |output_path| 58 | @options[:output_path] = output_path 59 | end 60 | opt.on("-3", "-3 PATH", "path to v3 directory") do |v3_directory| 61 | @options[:v3_directory] = v3_directory 62 | end 63 | end 64 | 65 | opt_parser.parse! 66 | 67 | raise OptionParser::MissingArgument if @options[:output_path].nil? 68 | raise OptionParser::MissingArgument if ARGV.length != 1 69 | 70 | @options[:vector_directory] = ARGV[0] 71 | 72 | File.open(@options[:output_path], "w") do |output| 73 | outputHeader(output) 74 | @test_files.each do |file| 75 | outputTestsForFile(output, file) 76 | end 77 | outputFooter(output) 78 | end 79 | -------------------------------------------------------------------------------- /RNCryptor/RNCryptor+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptor(Private) 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 "RNCryptor.h" 29 | 30 | @class RNCryptorEngine; 31 | 32 | @interface RNCryptor () 33 | @property (nonatomic, readwrite, strong) RNCryptorEngine *engine; 34 | #if OS_OBJECT_USE_OBJC 35 | @property (nonatomic, readwrite, strong) dispatch_queue_t queue; 36 | #else 37 | @property (nonatomic, readwrite, assign) dispatch_queue_t queue; 38 | #endif 39 | @property (nonatomic, readonly) NSMutableData *outData; 40 | @property (nonatomic, readwrite, copy) RNCryptorHandler handler; 41 | @property (nonatomic, readwrite, assign) NSUInteger HMACLength; 42 | @property (nonatomic, readwrite, strong) NSError *error; 43 | @property (nonatomic, readwrite, assign, getter=isFinished) BOOL finished; 44 | @property (nonatomic, readwrite, assign) RNCryptorOptions options; 45 | 46 | - (id)initWithHandler:(RNCryptorHandler)handler; 47 | + (NSData *)synchronousResultForCryptor:(RNCryptor *)cryptor data:(NSData *)inData error:(NSError **)anError; 48 | - (void)cleanupAndNotifyWithError:(NSError *)error; 49 | - (BOOL)hasHMAC; 50 | @end 51 | 52 | @interface NSMutableData (RNCryptor) 53 | - (NSData *)_RNConsumeToIndex:(NSUInteger)index; 54 | @end 55 | -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/xcshareddata/xcschemes/RNCryptor.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /rncrypt/rncrypt/rncrypt.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 1/29/13 \" DATE 7 | .Dt rncrypt 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm rncrypt, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /rncrypt/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // rncrypt 4 | // 5 | // Created by Rob Napier on 3/17/13. 6 | // Copyright (c) 2013 Rob Napier. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RNEncryptor.h" 11 | #import "RNDecryptor.h" 12 | #import 13 | 14 | NSData *GetDataForHex(NSString *hex) 15 | { 16 | NSString *hexNoSpaces = [[[hex stringByReplacingOccurrencesOfString:@" " withString:@""] 17 | stringByReplacingOccurrencesOfString:@"<" withString:@""] 18 | stringByReplacingOccurrencesOfString:@">" withString:@""]; 19 | 20 | NSMutableData *data = [[NSMutableData alloc] init]; 21 | unsigned char whole_byte = 0; 22 | char byte_chars[3] = {'\0','\0','\0'}; 23 | int i; 24 | for (i=0; i < [hexNoSpaces length] / 2; i++) { 25 | byte_chars[0] = [hexNoSpaces characterAtIndex:i*2]; 26 | byte_chars[1] = [hexNoSpaces characterAtIndex:i*2+1]; 27 | whole_byte = strtol(byte_chars, NULL, 16); 28 | [data appendBytes:&whole_byte length:1]; 29 | } 30 | return data; 31 | } 32 | 33 | void usage() { 34 | printf("Not like that\n"); 35 | exit(2); 36 | } 37 | 38 | void OutputData(NSData *data) 39 | { 40 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 41 | if (string) { 42 | printf("%s\n", [string UTF8String]); 43 | } 44 | else { 45 | printf("%s\n", [[data base64EncodedStringWithOptions:0] UTF8String]); 46 | } 47 | } 48 | 49 | int main(int argc, char * const argv[]) 50 | { 51 | @autoreleasepool { 52 | 53 | int decrypt_flag; 54 | NSString *password = nil; 55 | NSString *message = nil; 56 | 57 | char ch; 58 | 59 | /* options descriptor */ 60 | struct option longopts[] = { 61 | { "decrypt", no_argument, &decrypt_flag, 1 }, 62 | { "password", required_argument, NULL, 'p' }, 63 | { NULL, 0, NULL, 0 } 64 | }; 65 | 66 | while ((ch = getopt_long(argc, argv, "dp:P", longopts, NULL)) != -1) 67 | switch (ch) { 68 | case 'd': 69 | decrypt_flag = 1; 70 | break; 71 | case 'p': 72 | password = [NSString stringWithUTF8String:optarg]; 73 | break; 74 | default: 75 | usage(); 76 | } 77 | argc -= optind; 78 | argv += optind; 79 | 80 | if (argc != 1) { 81 | usage(); 82 | } 83 | 84 | message = [NSString stringWithUTF8String:argv[0]]; 85 | 86 | NSError *error; 87 | NSData *data; 88 | if (decrypt_flag) { 89 | data = [RNDecryptor decryptData:[[NSData alloc] initWithBase64EncodedString:message 90 | options:NSDataBase64DecodingIgnoreUnknownCharacters] 91 | withPassword:password 92 | error:&error]; 93 | } 94 | else { 95 | data = [RNEncryptor encryptData:[message dataUsingEncoding:NSUTF8StringEncoding] 96 | withSettings:kRNCryptorAES256Settings 97 | password:password 98 | error:&error]; 99 | } 100 | if (error) { 101 | NSLog(@"Failed: %@", error); 102 | exit(1); 103 | } 104 | else { 105 | OutputData(data); 106 | } 107 | } 108 | return 0; 109 | } -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/xcshareddata/xcschemes/RNCryptor OS X.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 63 | 64 | 65 | 66 | 72 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/xcshareddata/xcschemes/rncrypt.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /RNCryptor/RNEncryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNEncryptor 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 "RNCryptor.h" 29 | 30 | @interface RNEncryptor : RNCryptor 31 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)settings 32 | encryptionKey:(NSData *)encryptionKey 33 | HMACKey:(NSData *)HMACKey 34 | handler:(RNCryptorHandler)handler; 35 | 36 | 37 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)settings 38 | password:(NSString *)password 39 | handler:(RNCryptorHandler)handler; 40 | 41 | // This form with manual IV is generally only used for testing 42 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)theSettings 43 | encryptionKey:(NSData *)anEncryptionKey 44 | HMACKey:(NSData *)anHMACKey 45 | IV:(NSData *)anIV 46 | handler:(RNCryptorHandler)aHandler; 47 | 48 | // This form with manual IV and salts is generally only used for testing 49 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)settings 50 | password:(NSString *)password 51 | IV:(NSData *)anIV 52 | encryptionSalt:(NSData *)anEncryptionSalt 53 | HMACSalt:(NSData *)anHMACSalt 54 | handler:(RNCryptorHandler)handler; 55 | 56 | 57 | + (NSData *)encryptData:(NSData *)data withSettings:(RNCryptorSettings)settings password:(NSString *)password error:(NSError **)error; 58 | + (NSData *)encryptData:(NSData *)data withSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey error:(NSError **)error; 59 | 60 | // This form with manual IV is generally only used for testing 61 | + (NSData *)encryptData:(NSData *)thePlaintext 62 | withSettings:(RNCryptorSettings)theSettings 63 | encryptionKey:(NSData *)anEncryptionKey 64 | HMACKey:(NSData *)anHMACKey 65 | IV:(NSData *)anIV 66 | error:(NSError **)anError; 67 | 68 | // This form with manual IV and salts is generally only used for testing 69 | + (NSData *)encryptData:(NSData *)data 70 | withSettings:(RNCryptorSettings)settings 71 | password:(NSString *)password 72 | IV:(NSData *)anIV 73 | encryptionSalt:(NSData *)anEncryptionSalt 74 | HMACSalt:(NSData *)anHMACSalt 75 | error:(NSError **)error; 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /RNCryptor/RNCryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptor.h 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 29 | 30 | // NOTE: No CommonCrypto types may be used in this file. Swift can't handle them. 31 | 32 | extern NSString *const kRNCryptorErrorDomain; 33 | extern const uint8_t kRNCryptorFileVersion; 34 | 35 | typedef struct _RNCryptorKeyDerivationSettings 36 | { 37 | size_t keySize; 38 | size_t saltSize; 39 | /* CCPBKDFAlgorithm */ uint32_t PBKDFAlgorithm; 40 | /* CCPseudoRandomAlgorithm */ uint32_t PRF; 41 | uint rounds; 42 | BOOL hasV2Password; // See Issue #77. V2 incorrectly handled multi-byte characters. 43 | } RNCryptorKeyDerivationSettings; 44 | 45 | typedef struct _RNCryptorSettings 46 | { 47 | /* CCAlgorithm */ uint32_t algorithm; 48 | size_t blockSize; 49 | size_t IVSize; 50 | /* CCOptions */ uint32_t options; 51 | /* CCHmacAlgorithm */ uint32_t HMACAlgorithm; 52 | size_t HMACLength; 53 | RNCryptorKeyDerivationSettings keySettings; 54 | RNCryptorKeyDerivationSettings HMACKeySettings; 55 | } RNCryptorSettings; 56 | 57 | extern const RNCryptorSettings kRNCryptorAES256Settings; 58 | 59 | enum _RNCryptorOptions 60 | { 61 | kRNCryptorOptionHasPassword = 1 << 0, 62 | }; 63 | typedef uint8_t RNCryptorOptions; 64 | 65 | enum 66 | { 67 | kRNCryptorHMACMismatch = 1, 68 | kRNCryptorUnknownHeader = 2, 69 | }; 70 | 71 | @class RNCryptor; 72 | 73 | typedef void (^RNCryptorHandler)(RNCryptor *cryptor, NSData *data); 74 | 75 | ///** Encryptor/Decryptor for iOS 76 | // 77 | // Provides an easy-to-use, Objective-C interface to the AES functionality of CommonCrypto. Simplifies correct handling of 78 | // password stretching (PBKDF2), salting, and IV. For more information on these terms, see "Properly encrypting with AES 79 | // with CommonCrypto," and iOS 5 Programming Pushing the Limits, Chapter 11. Also includes automatic HMAC handling to integrity-check messages. 80 | // 81 | // RNCryptor is abstract. Use RNEncryptor to encrypt or RNDecryptor to decrypt 82 | // */ 83 | // 84 | 85 | @interface RNCryptor : NSObject 86 | @property (nonatomic, readonly, strong) NSError *error; 87 | @property (nonatomic, readonly, getter=isFinished) BOOL finished; 88 | @property (nonatomic, readonly, copy) RNCryptorHandler handler; 89 | @property (nonatomic, readwrite) dispatch_queue_t responseQueue; 90 | 91 | - (void)addData:(NSData *)data; 92 | - (void)finish; 93 | 94 | /** Generate key given a password and salt using a PBKDF 95 | * 96 | * @param password Password to use for PBKDF 97 | * @param salt Salt for password 98 | * @param keySettings Settings for the derivation (RNCryptorKeyDerivationSettings) 99 | * @returns Key 100 | * @throws if settings are illegal 101 | */ 102 | + (NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt settings:(RNCryptorKeyDerivationSettings)keySettings; 103 | 104 | /** Generate random data 105 | * 106 | * @param length Length of data to generate 107 | * @returns random data 108 | */ 109 | + (NSData *)randomDataOfLength:(size_t)length; 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /RNCryptor/RNCryptorEngine.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptorEngine 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 "RNCryptorEngine.h" 29 | 30 | @interface RNCryptorEngine () 31 | @property (nonatomic, readonly) CCCryptorRef cryptor; 32 | @property (nonatomic, readonly) NSMutableData *buffer; 33 | @end 34 | 35 | @implementation RNCryptorEngine 36 | @synthesize cryptor = __cryptor; 37 | @synthesize buffer = __buffer; 38 | 39 | 40 | - (RNCryptorEngine *)initWithOperation:(CCOperation)operation settings:(RNCryptorSettings)settings key:(NSData *)key IV:(NSData *)IV error:(NSError **)error 41 | { 42 | self = [super init]; 43 | if (self) { 44 | CCCryptorStatus 45 | cryptorStatus = CCCryptorCreate(operation, 46 | settings.algorithm, 47 | settings.options, 48 | key.bytes, 49 | key.length, 50 | IV.bytes, 51 | &__cryptor); 52 | if (cryptorStatus != kCCSuccess || __cryptor == NULL) { 53 | if (error) { 54 | *error = [NSError errorWithDomain:kRNCryptorErrorDomain code:cryptorStatus userInfo:nil]; 55 | } 56 | self = nil; 57 | return nil; 58 | } 59 | 60 | __buffer = [NSMutableData data]; 61 | } 62 | return self; 63 | } 64 | 65 | - (void)dealloc 66 | { 67 | if (__cryptor) { 68 | CCCryptorRelease(__cryptor); 69 | } 70 | } 71 | 72 | - (NSData *)addData:(NSData *)data error:(NSError **)error 73 | { 74 | NSMutableData *buffer = self.buffer; 75 | [buffer setLength:CCCryptorGetOutputLength(self.cryptor, [data length], true)]; // We'll reuse the buffer in -finish 76 | 77 | size_t dataOutMoved; 78 | CCCryptorStatus 79 | cryptorStatus = CCCryptorUpdate(self.cryptor, // cryptor 80 | data.bytes, // dataIn 81 | data.length, // dataInLength (verified > 0 above) 82 | buffer.mutableBytes, // dataOut 83 | buffer.length, // dataOutAvailable 84 | &dataOutMoved); // dataOutMoved 85 | 86 | if (cryptorStatus != kCCSuccess) { 87 | if (error) { 88 | *error = [NSError errorWithDomain:kRNCryptorErrorDomain code:cryptorStatus userInfo:nil]; 89 | } 90 | return nil; 91 | } 92 | 93 | return [buffer subdataWithRange:NSMakeRange(0, dataOutMoved)]; 94 | } 95 | 96 | - (NSData *)finishWithError:(NSError **)error 97 | { 98 | NSMutableData *buffer = self.buffer; 99 | size_t dataOutMoved; 100 | CCCryptorStatus 101 | cryptorStatus = CCCryptorFinal(self.cryptor, // cryptor 102 | buffer.mutableBytes, // dataOut 103 | buffer.length, // dataOutAvailable 104 | &dataOutMoved); // dataOutMoved 105 | if (cryptorStatus != kCCSuccess) { 106 | if (error) { 107 | *error = [NSError errorWithDomain:kRNCryptorErrorDomain code:cryptorStatus userInfo:nil]; 108 | } 109 | return nil; 110 | } 111 | 112 | return [buffer subdataWithRange:NSMakeRange(0, dataOutMoved)]; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /RNCryptor.xcodeproj/xcshareddata/xcschemes/RNCryptor iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /RNCryptorTests/XCTestCase+RNCryptorVectorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptorVectorTests.m 3 | // RNCryptor 4 | // 5 | // Created by Rob Napier on 1/9/14. 6 | // Copyright (c) 2014 Rob Napier. All rights reserved. 7 | // 8 | 9 | #import "XCTestCase+RNCryptorVectorTests.h" 10 | 11 | NSData *GetDataForHex(NSString *hex) { 12 | NSString *hexNoSpaces = [[[hex stringByReplacingOccurrencesOfString:@" " withString:@""] 13 | stringByReplacingOccurrencesOfString:@"<" withString:@""] 14 | stringByReplacingOccurrencesOfString:@">" withString:@""]; 15 | 16 | NSMutableData *data = [[NSMutableData alloc] init]; 17 | unsigned char whole_byte = 0; 18 | char byte_chars[3] = {'\0','\0','\0'}; 19 | int i; 20 | for (i=0; i < [hexNoSpaces length] / 2; i++) { 21 | byte_chars[0] = (unsigned char)[hexNoSpaces characterAtIndex:i*2]; 22 | byte_chars[1] = (unsigned char)[hexNoSpaces characterAtIndex:i*2+1]; 23 | whole_byte = (unsigned char)strtol(byte_chars, NULL, 16); 24 | [data appendBytes:&whole_byte length:1]; 25 | } 26 | return data; 27 | } 28 | 29 | @implementation XCTestCase (RNCryptorVectorTests) 30 | 31 | - (void)verifyVector:(NSDictionary *)vector key:(NSString *)key equals:(NSData *)actual title:(NSString*)title { 32 | XCTAssertEqualObjects(actual, GetDataForHex(vector[key]), @"Failed %@ test (v%@): %@\n", title, vector[@"version"], vector[@"title"]); 33 | } 34 | 35 | - (void)_verifyKDF:(NSDictionary *)vector settings:(RNCryptorKeyDerivationSettings)keySettings name:(NSString *)name { 36 | NSCParameterAssert(vector[@"title"]); 37 | NSCParameterAssert(vector[@"version"]); 38 | NSCParameterAssert(vector[@"password"]); 39 | NSCParameterAssert(vector[@"salt_hex"]); 40 | NSCParameterAssert(vector[@"key_hex"]); 41 | 42 | NSData *key = [RNCryptor keyForPassword:vector[@"password"] 43 | salt:GetDataForHex(vector[@"salt_hex"]) 44 | settings:keySettings]; 45 | [self verifyVector:vector key:@"key_hex" equals:key title:name]; 46 | } 47 | 48 | - (void)verify_v3_kdf:(NSDictionary *)vector { 49 | RNCryptorKeyDerivationSettings settings = kRNCryptorAES256Settings.keySettings; 50 | [self _verifyKDF:vector settings:settings name:@"kdf"]; 51 | } 52 | 53 | - (void)verify_v2_kdf:(NSDictionary *)vector { 54 | RNCryptorKeyDerivationSettings settings = kRNCryptorAES256Settings.keySettings; 55 | settings.hasV2Password = YES; 56 | [self _verifyKDF:vector settings:settings name:@"kdf"]; 57 | } 58 | 59 | - (void)verify_v1_kdf:(NSDictionary *)vector { 60 | RNCryptorKeyDerivationSettings settings = kRNCryptorAES256Settings.keySettings; 61 | settings.hasV2Password = YES; 62 | [self _verifyKDF:vector settings:settings name:@"kdf"]; 63 | } 64 | 65 | - (void)_verifyPassword:(NSDictionary *)vector settings:(RNCryptorSettings)settings name:(NSString *)name { 66 | NSCParameterAssert(vector[@"title"]); 67 | NSCParameterAssert(vector[@"version"]); 68 | NSCParameterAssert(vector[@"password"]); 69 | NSCParameterAssert(vector[@"iv_hex"]); 70 | NSCParameterAssert(vector[@"enc_salt_hex"]); 71 | NSCParameterAssert(vector[@"hmac_salt_hex"]); 72 | NSCParameterAssert(vector[@"plaintext_hex"]); 73 | NSCParameterAssert(vector[@"ciphertext_hex"]); 74 | 75 | NSError *error; 76 | 77 | if ([vector[@"version"] intValue] == kRNCryptorFileVersion) { 78 | NSData *ciphertext = [RNEncryptor encryptData:GetDataForHex(vector[@"plaintext_hex"]) 79 | withSettings:settings 80 | password:vector[@"password"] 81 | IV:GetDataForHex(vector[@"iv_hex"]) 82 | encryptionSalt:GetDataForHex(vector[@"enc_salt_hex"]) 83 | HMACSalt:GetDataForHex(vector[@"hmac_salt_hex"]) 84 | error:&error]; 85 | [self verifyVector:vector key:@"ciphertext_hex" equals:ciphertext title:[name stringByAppendingString:@" encrypt"]]; 86 | } 87 | 88 | NSData *plaintext = [RNDecryptor decryptData:GetDataForHex(vector[@"ciphertext_hex"]) 89 | withPassword:vector[@"password"] 90 | error:&error]; 91 | [self verifyVector:vector key:@"plaintext_hex" equals:plaintext title:[name stringByAppendingString:@" encrypt"]]; 92 | } 93 | 94 | - (void)verify_v3_password:(NSDictionary *)vector { 95 | [self _verifyPassword:vector settings:kRNCryptorAES256Settings name:@"password"]; 96 | } 97 | 98 | - (void)verify_v2_password:(NSDictionary *)vector { 99 | [self _verifyPassword:vector settings:kRNCryptorAES256Settings name:@"password"]; 100 | } 101 | 102 | - (void)verify_v1_password:(NSDictionary *)vector { 103 | [self _verifyPassword:vector settings:kRNCryptorAES256Settings name:@"password"]; 104 | } 105 | 106 | - (void)verify_v3_key:(NSDictionary *)vector { 107 | NSCParameterAssert(vector[@"title"]); 108 | NSCParameterAssert(vector[@"version"]); 109 | NSCParameterAssert(vector[@"enc_key_hex"]); 110 | NSCParameterAssert(vector[@"hmac_key_hex"]); 111 | NSCParameterAssert(vector[@"iv_hex"]); 112 | NSCParameterAssert(vector[@"plaintext_hex"]); 113 | NSCParameterAssert(vector[@"ciphertext_hex"]); 114 | 115 | NSError *error; 116 | 117 | if ([vector[@"version"] intValue] == kRNCryptorFileVersion) { 118 | NSData *ciphertext = [RNEncryptor encryptData:GetDataForHex(vector[@"plaintext_hex"]) 119 | withSettings:kRNCryptorAES256Settings 120 | encryptionKey:GetDataForHex(vector[@"enc_key_hex"]) 121 | HMACKey:GetDataForHex(vector[@"hmac_key_hex"]) 122 | IV:GetDataForHex(vector[@"iv_hex"]) 123 | error:&error]; 124 | [self verifyVector:vector key:@"ciphertext_hex" equals:ciphertext title:@"key encrypt"]; 125 | } 126 | 127 | NSData *plaintext = [RNDecryptor decryptData:GetDataForHex(vector[@"ciphertext_hex"]) 128 | withEncryptionKey:GetDataForHex(vector[@"enc_key_hex"]) 129 | HMACKey:GetDataForHex(vector[@"hmac_key_hex"]) 130 | error:&error]; 131 | [self verifyVector:vector key:@"plaintext_hex" equals:plaintext title:@"key decrypt"]; 132 | } 133 | @end 134 | 135 | 136 | -------------------------------------------------------------------------------- /RNCryptor/RNEncryptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNEncryptor 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 "RNEncryptor.h" 28 | #import "RNCryptor+Private.h" 29 | #import "RNCryptorEngine.h" 30 | 31 | #import 32 | 33 | @interface RNEncryptor () 34 | @property (nonatomic, readwrite, strong) NSData *encryptionSalt; 35 | @property (nonatomic, readwrite, strong) NSData *HMACSalt; 36 | @property (nonatomic, readwrite, strong) NSData *IV; 37 | @property (nonatomic, readwrite, assign) BOOL haveWrittenHeader; 38 | @end 39 | 40 | @implementation RNEncryptor 41 | { 42 | CCHmacContext _HMACContext; 43 | } 44 | @synthesize encryptionSalt = _encryptionSalt; 45 | @synthesize HMACSalt = _HMACSalt; 46 | @synthesize IV = _IV; 47 | @synthesize haveWrittenHeader = _haveWrittenHeader; 48 | 49 | 50 | + (NSData *)encryptData:(NSData *)thePlaintext withSettings:(RNCryptorSettings)theSettings password:(NSString *)aPassword error:(NSError **)anError 51 | { 52 | RNEncryptor *cryptor = [[self alloc] initWithSettings:theSettings 53 | password:aPassword 54 | handler:^(RNCryptor *c, NSData *d) {}]; 55 | return [self synchronousResultForCryptor:cryptor data:thePlaintext error:anError]; 56 | } 57 | 58 | + (NSData *)encryptData:(NSData *)thePlaintext 59 | withSettings:(RNCryptorSettings)theSettings 60 | password:(NSString *)aPassword 61 | IV:(NSData *)anIV 62 | encryptionSalt:(NSData *)anEncryptionSalt 63 | HMACSalt:(NSData *)anHMACSalt 64 | error:(NSError **)anError 65 | { 66 | RNEncryptor *cryptor = [[self alloc] initWithSettings:theSettings 67 | password:aPassword 68 | IV:anIV 69 | encryptionSalt:anEncryptionSalt 70 | HMACSalt:anHMACSalt 71 | handler:^(RNCryptor *c, NSData *d) {}]; 72 | return [self synchronousResultForCryptor:cryptor data:thePlaintext error:anError]; 73 | } 74 | 75 | + (NSData *)encryptData:(NSData *)thePlaintext withSettings:(RNCryptorSettings)theSettings encryptionKey:(NSData *)anEncryptionKey HMACKey:(NSData *)anHMACKey error:(NSError **)anError { 76 | RNEncryptor *cryptor = [[self alloc] initWithSettings:theSettings 77 | encryptionKey:anEncryptionKey 78 | HMACKey:anHMACKey 79 | handler:^(RNCryptor *c, NSData *d) {}]; 80 | return [self synchronousResultForCryptor:cryptor data:thePlaintext error:anError]; 81 | } 82 | 83 | 84 | + (NSData *)encryptData:(NSData *)thePlaintext 85 | withSettings:(RNCryptorSettings)theSettings 86 | encryptionKey:(NSData *)anEncryptionKey 87 | HMACKey:(NSData *)anHMACKey 88 | IV:(NSData *)anIV 89 | error:(NSError **)anError 90 | { 91 | RNEncryptor *cryptor = [[self alloc] initWithSettings:theSettings 92 | encryptionKey:anEncryptionKey 93 | HMACKey:anHMACKey 94 | IV:anIV 95 | handler:^(RNCryptor *c, NSData *d) {}]; 96 | return [self synchronousResultForCryptor:cryptor data:thePlaintext error:anError]; 97 | } 98 | 99 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)theSettings 100 | encryptionKey:(NSData *)anEncryptionKey 101 | HMACKey:(NSData *)anHMACKey 102 | handler:(RNCryptorHandler)aHandler { 103 | return [self initWithSettings:theSettings 104 | encryptionKey:anEncryptionKey 105 | HMACKey:anHMACKey 106 | IV:[[self class] randomDataOfLength:theSettings.IVSize] 107 | handler:aHandler]; 108 | } 109 | 110 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)theSettings 111 | encryptionKey:(NSData *)anEncryptionKey 112 | HMACKey:(NSData *)anHMACKey 113 | IV:(NSData *)anIV 114 | handler:(RNCryptorHandler)aHandler 115 | { 116 | self = [super initWithHandler:aHandler]; 117 | if (self) { 118 | self.IV = anIV; 119 | 120 | if (anHMACKey) { 121 | CCHmacInit(&_HMACContext, theSettings.HMACAlgorithm, anHMACKey.bytes, anHMACKey.length); 122 | self.HMACLength = theSettings.HMACLength; 123 | } 124 | 125 | NSError *error = nil; 126 | self.engine = [[RNCryptorEngine alloc] initWithOperation:kCCEncrypt 127 | settings:theSettings 128 | key:anEncryptionKey 129 | IV:self.IV 130 | error:&error]; 131 | if (!self.engine) { 132 | [self cleanupAndNotifyWithError:error]; 133 | self = nil; 134 | return nil; 135 | } 136 | } 137 | 138 | return self; 139 | } 140 | 141 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)theSettings password:(NSString *)aPassword handler:(RNCryptorHandler)aHandler { 142 | return [self initWithSettings:theSettings 143 | password:aPassword 144 | IV:[[self class] randomDataOfLength:theSettings.IVSize] 145 | encryptionSalt:[[self class] randomDataOfLength:theSettings.keySettings.saltSize] 146 | HMACSalt:[[self class] randomDataOfLength:theSettings.HMACKeySettings.saltSize] 147 | handler:aHandler]; 148 | } 149 | 150 | 151 | - (RNEncryptor *)initWithSettings:(RNCryptorSettings)theSettings 152 | password:(NSString *)aPassword 153 | IV:(NSData *)anIV 154 | encryptionSalt:(NSData *)anEncryptionSalt 155 | HMACSalt:(NSData *)anHMACSalt 156 | handler:(RNCryptorHandler)aHandler; 157 | { 158 | NSParameterAssert(aPassword.length > 0); // We'll go forward, but this is undefined behavior for RNCryptor 159 | NSParameterAssert(anIV); 160 | NSParameterAssert(anEncryptionSalt); 161 | NSParameterAssert(anHMACSalt); 162 | 163 | NSData *encryptionKey = [[self class] keyForPassword:aPassword salt:anEncryptionSalt settings:theSettings.keySettings]; 164 | NSData *HMACKey = [[self class] keyForPassword:aPassword salt:anHMACSalt settings:theSettings.HMACKeySettings]; 165 | 166 | self = [self initWithSettings:theSettings 167 | encryptionKey:encryptionKey 168 | HMACKey:HMACKey 169 | IV:anIV 170 | handler:aHandler]; 171 | if (self) { 172 | self.options |= kRNCryptorOptionHasPassword; 173 | self.encryptionSalt = anEncryptionSalt; 174 | self.HMACSalt = anHMACSalt; 175 | } 176 | return self; 177 | } 178 | 179 | - (NSData *)header 180 | { 181 | uint8_t header[2] = {kRNCryptorFileVersion, self.options}; 182 | NSMutableData *headerData = [NSMutableData dataWithBytes:header length:sizeof(header)]; 183 | if (self.options & kRNCryptorOptionHasPassword) { 184 | [headerData appendData:self.encryptionSalt]; 185 | [headerData appendData:self.HMACSalt]; 186 | } 187 | [headerData appendData:self.IV]; 188 | return headerData; 189 | } 190 | 191 | - (void)addData:(NSData *)data 192 | { 193 | if (self.isFinished) { 194 | return; 195 | } 196 | 197 | dispatch_async(self.queue, ^{ 198 | if (!self.haveWrittenHeader) { 199 | NSData *header = [self header]; 200 | [self.outData setData:header]; 201 | if (self.hasHMAC) { 202 | CCHmacUpdate(&self->_HMACContext, [header bytes], [header length]); 203 | } 204 | self.haveWrittenHeader = YES; 205 | } 206 | 207 | NSError *error = nil; 208 | NSData *encryptedData = [self.engine addData:data error:&error]; 209 | if (!encryptedData) { 210 | [self cleanupAndNotifyWithError:error]; 211 | } 212 | if (self.hasHMAC) { 213 | CCHmacUpdate(&self->_HMACContext, encryptedData.bytes, encryptedData.length); 214 | } 215 | 216 | [self.outData appendData:encryptedData]; 217 | 218 | dispatch_sync(self.responseQueue, ^{ 219 | self.handler(self, self.outData); 220 | }); 221 | [self.outData setLength:0]; 222 | }); 223 | } 224 | 225 | - (void)finish 226 | { 227 | if (self.isFinished) { 228 | return; 229 | } 230 | 231 | dispatch_async(self.queue, ^{ 232 | NSError *error = nil; 233 | NSData *encryptedData = [self.engine finishWithError:&error]; 234 | [self.outData appendData:encryptedData]; 235 | if (self.hasHMAC) { 236 | CCHmacUpdate(&self->_HMACContext, encryptedData.bytes, encryptedData.length); 237 | NSMutableData *HMACData = [NSMutableData dataWithLength:self.HMACLength]; 238 | CCHmacFinal(&self->_HMACContext, [HMACData mutableBytes]); 239 | [self.outData appendData:HMACData]; 240 | } 241 | [self cleanupAndNotifyWithError:error]; 242 | }); 243 | } 244 | 245 | @end 246 | -------------------------------------------------------------------------------- /rncrypt/rncrypt.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FBB08CB216B8ABB600E7E968 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBB08CB116B8ABB600E7E968 /* Foundation.framework */; }; 11 | FBB08CB516B8ABB600E7E968 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB08CB416B8ABB600E7E968 /* main.m */; }; 12 | FBB08CB916B8ABB600E7E968 /* rncrypt.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FBB08CB816B8ABB600E7E968 /* rncrypt.1 */; }; 13 | FBB08CC816B8ABE600E7E968 /* RNCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB08CC016B8ABE600E7E968 /* RNCryptor.m */; }; 14 | FBB08CC916B8ABE600E7E968 /* RNCryptorEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB08CC316B8ABE600E7E968 /* RNCryptorEngine.m */; }; 15 | FBB08CCA16B8ABE600E7E968 /* RNDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB08CC516B8ABE600E7E968 /* RNDecryptor.m */; }; 16 | FBB08CCB16B8ABE600E7E968 /* RNEncryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB08CC716B8ABE600E7E968 /* RNEncryptor.m */; }; 17 | FBB08CCD16B8ACEB00E7E968 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBB08CCC16B8ACEB00E7E968 /* Security.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | FBB08CAC16B8ABB600E7E968 /* CopyFiles */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = /usr/share/man/man1/; 25 | dstSubfolderSpec = 0; 26 | files = ( 27 | FBB08CB916B8ABB600E7E968 /* rncrypt.1 in CopyFiles */, 28 | ); 29 | runOnlyForDeploymentPostprocessing = 1; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | FBB08CAE16B8ABB600E7E968 /* rncrypt */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rncrypt; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | FBB08CB116B8ABB600E7E968 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 36 | FBB08CB416B8ABB600E7E968 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 37 | FBB08CB716B8ABB600E7E968 /* rncrypt-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "rncrypt-Prefix.pch"; sourceTree = ""; }; 38 | FBB08CB816B8ABB600E7E968 /* rncrypt.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rncrypt.1; sourceTree = ""; }; 39 | FBB08CBF16B8ABE600E7E968 /* RNCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNCryptor.h; path = ../../RNCryptor/RNCryptor.h; sourceTree = ""; }; 40 | FBB08CC016B8ABE600E7E968 /* RNCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNCryptor.m; path = ../../RNCryptor/RNCryptor.m; sourceTree = ""; }; 41 | FBB08CC116B8ABE600E7E968 /* RNCryptor+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "RNCryptor+Private.h"; path = "../../RNCryptor/RNCryptor+Private.h"; sourceTree = ""; }; 42 | FBB08CC216B8ABE600E7E968 /* RNCryptorEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNCryptorEngine.h; path = ../../RNCryptor/RNCryptorEngine.h; sourceTree = ""; }; 43 | FBB08CC316B8ABE600E7E968 /* RNCryptorEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNCryptorEngine.m; path = ../../RNCryptor/RNCryptorEngine.m; sourceTree = ""; }; 44 | FBB08CC416B8ABE600E7E968 /* RNDecryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNDecryptor.h; path = ../../RNCryptor/RNDecryptor.h; sourceTree = ""; }; 45 | FBB08CC516B8ABE600E7E968 /* RNDecryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNDecryptor.m; path = ../../RNCryptor/RNDecryptor.m; sourceTree = ""; }; 46 | FBB08CC616B8ABE600E7E968 /* RNEncryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNEncryptor.h; path = ../../RNCryptor/RNEncryptor.h; sourceTree = ""; }; 47 | FBB08CC716B8ABE600E7E968 /* RNEncryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNEncryptor.m; path = ../../RNCryptor/RNEncryptor.m; sourceTree = ""; }; 48 | FBB08CCC16B8ACEB00E7E968 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | FBB08CAB16B8ABB600E7E968 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | FBB08CCD16B8ACEB00E7E968 /* Security.framework in Frameworks */, 57 | FBB08CB216B8ABB600E7E968 /* Foundation.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | FBB08CA516B8ABB600E7E968 = { 65 | isa = PBXGroup; 66 | children = ( 67 | FBB08CCC16B8ACEB00E7E968 /* Security.framework */, 68 | FBB08CB316B8ABB600E7E968 /* rncrypt */, 69 | FBB08CB016B8ABB600E7E968 /* Frameworks */, 70 | FBB08CAF16B8ABB600E7E968 /* Products */, 71 | ); 72 | sourceTree = ""; 73 | }; 74 | FBB08CAF16B8ABB600E7E968 /* Products */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | FBB08CAE16B8ABB600E7E968 /* rncrypt */, 78 | ); 79 | name = Products; 80 | sourceTree = ""; 81 | }; 82 | FBB08CB016B8ABB600E7E968 /* Frameworks */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | FBB08CB116B8ABB600E7E968 /* Foundation.framework */, 86 | ); 87 | name = Frameworks; 88 | sourceTree = ""; 89 | }; 90 | FBB08CB316B8ABB600E7E968 /* rncrypt */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | FBB08CB416B8ABB600E7E968 /* main.m */, 94 | FBB08CBF16B8ABE600E7E968 /* RNCryptor.h */, 95 | FBB08CC016B8ABE600E7E968 /* RNCryptor.m */, 96 | FBB08CC116B8ABE600E7E968 /* RNCryptor+Private.h */, 97 | FBB08CC216B8ABE600E7E968 /* RNCryptorEngine.h */, 98 | FBB08CC316B8ABE600E7E968 /* RNCryptorEngine.m */, 99 | FBB08CC416B8ABE600E7E968 /* RNDecryptor.h */, 100 | FBB08CC516B8ABE600E7E968 /* RNDecryptor.m */, 101 | FBB08CC616B8ABE600E7E968 /* RNEncryptor.h */, 102 | FBB08CC716B8ABE600E7E968 /* RNEncryptor.m */, 103 | FBB08CB816B8ABB600E7E968 /* rncrypt.1 */, 104 | FBB08CB616B8ABB600E7E968 /* Supporting Files */, 105 | ); 106 | path = rncrypt; 107 | sourceTree = ""; 108 | }; 109 | FBB08CB616B8ABB600E7E968 /* Supporting Files */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | FBB08CB716B8ABB600E7E968 /* rncrypt-Prefix.pch */, 113 | ); 114 | name = "Supporting Files"; 115 | sourceTree = ""; 116 | }; 117 | /* End PBXGroup section */ 118 | 119 | /* Begin PBXNativeTarget section */ 120 | FBB08CAD16B8ABB600E7E968 /* rncrypt */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = FBB08CBC16B8ABB600E7E968 /* Build configuration list for PBXNativeTarget "rncrypt" */; 123 | buildPhases = ( 124 | FBB08CAA16B8ABB600E7E968 /* Sources */, 125 | FBB08CAB16B8ABB600E7E968 /* Frameworks */, 126 | FBB08CAC16B8ABB600E7E968 /* CopyFiles */, 127 | ); 128 | buildRules = ( 129 | ); 130 | dependencies = ( 131 | ); 132 | name = rncrypt; 133 | productName = rncrypt; 134 | productReference = FBB08CAE16B8ABB600E7E968 /* rncrypt */; 135 | productType = "com.apple.product-type.tool"; 136 | }; 137 | /* End PBXNativeTarget section */ 138 | 139 | /* Begin PBXProject section */ 140 | FBB08CA616B8ABB600E7E968 /* Project object */ = { 141 | isa = PBXProject; 142 | attributes = { 143 | LastUpgradeCheck = 0460; 144 | ORGANIZATIONNAME = "Rob Napier"; 145 | }; 146 | buildConfigurationList = FBB08CA916B8ABB600E7E968 /* Build configuration list for PBXProject "rncrypt" */; 147 | compatibilityVersion = "Xcode 3.2"; 148 | developmentRegion = English; 149 | hasScannedForEncodings = 0; 150 | knownRegions = ( 151 | en, 152 | ); 153 | mainGroup = FBB08CA516B8ABB600E7E968; 154 | productRefGroup = FBB08CAF16B8ABB600E7E968 /* Products */; 155 | projectDirPath = ""; 156 | projectRoot = ""; 157 | targets = ( 158 | FBB08CAD16B8ABB600E7E968 /* rncrypt */, 159 | ); 160 | }; 161 | /* End PBXProject section */ 162 | 163 | /* Begin PBXSourcesBuildPhase section */ 164 | FBB08CAA16B8ABB600E7E968 /* Sources */ = { 165 | isa = PBXSourcesBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | FBB08CB516B8ABB600E7E968 /* main.m in Sources */, 169 | FBB08CC816B8ABE600E7E968 /* RNCryptor.m in Sources */, 170 | FBB08CC916B8ABE600E7E968 /* RNCryptorEngine.m in Sources */, 171 | FBB08CCA16B8ABE600E7E968 /* RNDecryptor.m in Sources */, 172 | FBB08CCB16B8ABE600E7E968 /* RNEncryptor.m in Sources */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXSourcesBuildPhase section */ 177 | 178 | /* Begin XCBuildConfiguration section */ 179 | FBB08CBA16B8ABB600E7E968 /* Debug */ = { 180 | isa = XCBuildConfiguration; 181 | buildSettings = { 182 | ALWAYS_SEARCH_USER_PATHS = NO; 183 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 185 | CLANG_CXX_LIBRARY = "libc++"; 186 | CLANG_ENABLE_OBJC_ARC = YES; 187 | CLANG_WARN_CONSTANT_CONVERSION = YES; 188 | CLANG_WARN_EMPTY_BODY = YES; 189 | CLANG_WARN_ENUM_CONVERSION = YES; 190 | CLANG_WARN_INT_CONVERSION = YES; 191 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 192 | COPY_PHASE_STRIP = NO; 193 | GCC_C_LANGUAGE_STANDARD = gnu99; 194 | GCC_DYNAMIC_NO_PIC = NO; 195 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 196 | GCC_OPTIMIZATION_LEVEL = 0; 197 | GCC_PREPROCESSOR_DEFINITIONS = ( 198 | "DEBUG=1", 199 | "$(inherited)", 200 | ); 201 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 202 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 203 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 204 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 205 | GCC_WARN_UNUSED_VARIABLE = YES; 206 | MACOSX_DEPLOYMENT_TARGET = 10.8; 207 | ONLY_ACTIVE_ARCH = YES; 208 | SDKROOT = macosx; 209 | }; 210 | name = Debug; 211 | }; 212 | FBB08CBB16B8ABB600E7E968 /* Release */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | ALWAYS_SEARCH_USER_PATHS = NO; 216 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 217 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 218 | CLANG_CXX_LIBRARY = "libc++"; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_WARN_CONSTANT_CONVERSION = YES; 221 | CLANG_WARN_EMPTY_BODY = YES; 222 | CLANG_WARN_ENUM_CONVERSION = YES; 223 | CLANG_WARN_INT_CONVERSION = YES; 224 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 225 | COPY_PHASE_STRIP = YES; 226 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 227 | GCC_C_LANGUAGE_STANDARD = gnu99; 228 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 231 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | MACOSX_DEPLOYMENT_TARGET = 10.8; 234 | SDKROOT = macosx; 235 | }; 236 | name = Release; 237 | }; 238 | FBB08CBD16B8ABB600E7E968 /* Debug */ = { 239 | isa = XCBuildConfiguration; 240 | buildSettings = { 241 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 242 | GCC_PREFIX_HEADER = "rncrypt/rncrypt-Prefix.pch"; 243 | PRODUCT_NAME = "$(TARGET_NAME)"; 244 | }; 245 | name = Debug; 246 | }; 247 | FBB08CBE16B8ABB600E7E968 /* Release */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 251 | GCC_PREFIX_HEADER = "rncrypt/rncrypt-Prefix.pch"; 252 | PRODUCT_NAME = "$(TARGET_NAME)"; 253 | }; 254 | name = Release; 255 | }; 256 | /* End XCBuildConfiguration section */ 257 | 258 | /* Begin XCConfigurationList section */ 259 | FBB08CA916B8ABB600E7E968 /* Build configuration list for PBXProject "rncrypt" */ = { 260 | isa = XCConfigurationList; 261 | buildConfigurations = ( 262 | FBB08CBA16B8ABB600E7E968 /* Debug */, 263 | FBB08CBB16B8ABB600E7E968 /* Release */, 264 | ); 265 | defaultConfigurationIsVisible = 0; 266 | defaultConfigurationName = Release; 267 | }; 268 | FBB08CBC16B8ABB600E7E968 /* Build configuration list for PBXNativeTarget "rncrypt" */ = { 269 | isa = XCConfigurationList; 270 | buildConfigurations = ( 271 | FBB08CBD16B8ABB600E7E968 /* Debug */, 272 | FBB08CBE16B8ABB600E7E968 /* Release */, 273 | ); 274 | defaultConfigurationIsVisible = 0; 275 | }; 276 | /* End XCConfigurationList section */ 277 | }; 278 | rootObject = FBB08CA616B8ABB600E7E968 /* Project object */; 279 | } 280 | -------------------------------------------------------------------------------- /RNCryptor/RNDecryptor.m: -------------------------------------------------------------------------------- 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 | 27 | #import "RNDecryptor.h" 28 | #import "RNCryptor+Private.h" 29 | #import "RNCryptorEngine.h" 30 | 31 | #import 32 | 33 | static const NSUInteger kPreambleSize = 2; 34 | 35 | @interface NSData (RNCryptor_ConsistentCompare) 36 | 37 | /** Compare two NSData in time proportional to the compared data (otherData) 38 | * 39 | * isEqual:-based comparisons stop comparing at the first difference. This can be used by attackers, in some situations, 40 | * to determine a secret value by considering the time required to compare the values. 41 | * 42 | * It is slightly better to call this as [secret rnc_isEqualInConsistentTime:attackersData] rather than the reverse, 43 | * but it is not a major issue either way. In the first case, the time required is proportional to the attacker's data, 44 | * which provides the attacker no information about the length of secret. In the second case, the time is proportional 45 | * to the length of secret, which leaks a small amount of informaiont, but in a way that does not varry in proportion to 46 | * the attacker's data. 47 | * 48 | * @param otherData data to compare 49 | * @returns YES if values are equal 50 | */ 51 | - (BOOL)rnc_isEqualInConsistentTime:(NSData *)otherData; 52 | 53 | @end 54 | 55 | @implementation NSData (RNCryptor_ConstantCompare) 56 | 57 | - (BOOL)rnc_isEqualInConsistentTime:(NSData *)otherData { 58 | // The point of this routine is XOR the bytes of each data and accumulate the results with OR. 59 | // If any bytes are different, then the OR will accumulate some non-0 value. 60 | 61 | const uint8_t *myBytes = [self bytes]; 62 | const NSUInteger myLength = [self length]; 63 | const uint8_t *otherBytes = [otherData bytes]; 64 | const NSUInteger otherLength = [otherData length]; 65 | 66 | uint8_t result = otherLength != myLength; // Start with 0 (equal) only if our lengths are equal 67 | 68 | for (NSUInteger i = 0; i < otherLength; ++i) { 69 | // Use mod to wrap around ourselves if they are longer than we are. 70 | // Remember, we already broke equality if our lengths are different. 71 | result |= myBytes[i % myLength] ^ otherBytes[i]; 72 | } 73 | 74 | return result == 0; 75 | } 76 | 77 | @end 78 | 79 | 80 | @interface RNDecryptor () 81 | @property (nonatomic, readonly, strong) NSMutableData *inData; 82 | @property (nonatomic, readwrite, copy) NSData *encryptionKey; 83 | @property (nonatomic, readwrite, copy) NSData *HMACKey; 84 | @property (nonatomic, readwrite, copy) NSString *password; 85 | @property (nonatomic, readwrite, assign) BOOL hasV1HMAC; 86 | 87 | @property (nonatomic, readwrite, assign) RNCryptorSettings settings; 88 | 89 | @end 90 | 91 | @implementation RNDecryptor 92 | { 93 | CCHmacContext _HMACContext; 94 | NSMutableData *__inData; 95 | } 96 | @synthesize encryptionKey = _encryptionKey; 97 | @synthesize HMACKey = _HMACKey; 98 | @synthesize password = _password; 99 | @synthesize settings = _settings; 100 | 101 | + (NSData *)decryptData:(NSData *)theCipherText withSettings:(RNCryptorSettings)settings password:(NSString *)aPassword error:(NSError **)anError 102 | { 103 | RNDecryptor *cryptor = [[self alloc] initWithPassword:aPassword 104 | handler:^(RNCryptor *c, NSData *d) {}]; 105 | cryptor.settings = settings; 106 | return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError]; 107 | } 108 | 109 | + (NSData *)decryptData:(NSData *)theCipherText withSettings:(RNCryptorSettings)settings encryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey error:(NSError **)anError 110 | { 111 | RNDecryptor *cryptor = [[self alloc] initWithEncryptionKey:encryptionKey 112 | HMACKey:HMACKey 113 | handler:^(RNCryptor *c, NSData *d) {}]; 114 | cryptor.settings = settings; 115 | return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError]; 116 | } 117 | 118 | + (NSData *)decryptData:(NSData *)theCipherText withPassword:(NSString *)aPassword error:(NSError **)anError 119 | { 120 | RNDecryptor *cryptor = [[self alloc] initWithPassword:aPassword 121 | handler:^(RNCryptor *c, NSData *d) {}]; 122 | return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError]; 123 | } 124 | 125 | + (NSData *)decryptData:(NSData *)theCipherText withEncryptionKey:(NSData *)encryptionKey HMACKey:(NSData *)HMACKey error:(NSError **)anError; 126 | { 127 | RNDecryptor *cryptor = [[self alloc] initWithEncryptionKey:encryptionKey 128 | HMACKey:HMACKey 129 | handler:^(RNCryptor *c, NSData *d) {}]; 130 | return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError]; 131 | } 132 | 133 | - (RNDecryptor *)initWithEncryptionKey:(NSData *)anEncryptionKey HMACKey:(NSData *)anHMACKey handler:(RNCryptorHandler)aHandler 134 | { 135 | self = [super initWithHandler:aHandler]; 136 | if (self) { 137 | _encryptionKey = [anEncryptionKey copy]; 138 | _HMACKey = [anHMACKey copy]; 139 | _settings = kRNCryptorAES256Settings; 140 | } 141 | 142 | return self; 143 | } 144 | 145 | - (RNDecryptor *)initWithPassword:(NSString *)aPassword handler:(RNCryptorHandler)aHandler 146 | { 147 | NSParameterAssert(aPassword != nil); 148 | 149 | self = [self initWithEncryptionKey:nil HMACKey:nil handler:aHandler]; 150 | if (self) { 151 | _password = [aPassword copy]; 152 | _settings = kRNCryptorAES256Settings; 153 | } 154 | return self; 155 | } 156 | 157 | - (NSMutableData *)inData 158 | { 159 | if (!__inData) { 160 | __inData = [NSMutableData data]; 161 | } 162 | return __inData; 163 | } 164 | 165 | - (void)decryptData:(NSData *)data 166 | { 167 | dispatch_async(self.queue, ^{ 168 | if (self.hasHMAC) { 169 | CCHmacUpdate(&self->_HMACContext, data.bytes, data.length); 170 | } 171 | 172 | NSError *error = nil; 173 | NSData *decryptedData = [self.engine addData:data error:&error]; 174 | 175 | if (!decryptedData) { 176 | [self cleanupAndNotifyWithError:error]; 177 | return; 178 | } 179 | 180 | [self.outData appendData:decryptedData]; 181 | 182 | dispatch_sync(self.responseQueue, ^{ 183 | self.handler(self, self.outData); 184 | }); 185 | [self.outData setLength:0]; 186 | }); 187 | } 188 | 189 | - (void)addData:(NSData *)theData 190 | { 191 | if (self.isFinished) { 192 | return; 193 | } 194 | 195 | [self.inData appendData:theData]; 196 | if (!self.engine) { 197 | [self consumeHeaderFromData:self.inData]; 198 | } 199 | if (self.engine) { 200 | NSUInteger HMACLength = self.HMACLength; 201 | if (self.inData.length > HMACLength) { 202 | NSData *data = [self.inData _RNConsumeToIndex:self.inData.length - HMACLength]; 203 | [self decryptData:data]; 204 | } 205 | } 206 | } 207 | 208 | - (BOOL)updateOptionsForPreamble:(NSData *)preamble 209 | { 210 | const uint8_t *bytes = [preamble bytes]; 211 | 212 | // See http://robnapier.net/blog/rncryptor-hmac-vulnerability-827 for information on the v1 bad HMAC 213 | #ifdef RNCRYPTOR_ALLOW_V1_BAD_HMAC 214 | if (bytes[0] == 1) { 215 | self.options = bytes[1]; 216 | self.hasV1HMAC = YES; 217 | return YES; 218 | } 219 | #endif 220 | 221 | if (bytes[0] == 2) { 222 | self.options = bytes[1]; 223 | 224 | RNCryptorSettings settings = self.settings; 225 | settings.keySettings.hasV2Password = YES; 226 | settings.HMACKeySettings.hasV2Password = YES; 227 | self.settings = settings; 228 | return YES; 229 | } 230 | 231 | if (bytes[0] == kRNCryptorFileVersion) { 232 | self.options = bytes[1]; 233 | return YES; 234 | } 235 | 236 | return NO; 237 | } 238 | 239 | - (void)consumeHeaderFromData:(NSMutableData *)data 240 | { 241 | if (data.length < kPreambleSize) { 242 | return; 243 | } 244 | 245 | if (![self updateOptionsForPreamble:[data subdataWithRange:NSMakeRange(0, kPreambleSize)]]) { 246 | [self cleanupAndNotifyWithError:[NSError errorWithDomain:kRNCryptorErrorDomain 247 | code:kRNCryptorUnknownHeader 248 | userInfo:[NSDictionary dictionaryWithObject:@"Unknown header" /* DNL */ 249 | forKey:NSLocalizedDescriptionKey]]]; 250 | return; 251 | } 252 | 253 | NSUInteger headerSize = kPreambleSize + self.settings.IVSize; 254 | if (self.options & kRNCryptorOptionHasPassword) { 255 | headerSize += self.settings.keySettings.saltSize + self.settings.HMACKeySettings.saltSize; 256 | } 257 | 258 | if (data.length < headerSize) { 259 | return; 260 | } 261 | 262 | NSData *header = [data subdataWithRange:NSMakeRange(0, headerSize)]; // We'll need this for the HMAC later 263 | 264 | [[data _RNConsumeToIndex:kPreambleSize] mutableCopy]; // Throw away the preamble 265 | 266 | NSError *error = nil; 267 | 268 | // If we were called with a password and the format thinks there's a password 269 | // (The format might think there's a password because it's corrupt, and we don't want to assert in that case.) 270 | if ((self.options & kRNCryptorOptionHasPassword) && self.password) { 271 | NSAssert(!self.encryptionKey && !self.HMACKey, @"Both password and the key (%d) or HMACKey (%d) are set.", self.encryptionKey != nil, self.HMACKey != nil); 272 | 273 | NSData *encryptionKeySalt = [data _RNConsumeToIndex:self.settings.keySettings.saltSize]; 274 | NSData *HMACKeySalt = [data _RNConsumeToIndex:self.settings.HMACKeySettings.saltSize]; 275 | self.encryptionKey = [[self class] keyForPassword:self.password salt:encryptionKeySalt settings:self.settings.keySettings]; 276 | self.HMACKey = [[self class] keyForPassword:self.password salt:HMACKeySalt settings:self.settings.HMACKeySettings]; 277 | 278 | self.password = nil; // Don't need this anymore. 279 | } 280 | 281 | NSData *IV = [data _RNConsumeToIndex:self.settings.IVSize]; 282 | 283 | self.engine = [[RNCryptorEngine alloc] initWithOperation:kCCDecrypt settings:self.settings key:self.encryptionKey IV:IV error:&error]; 284 | self.encryptionKey = nil; // Don't need this anymore 285 | if (!self.engine) { 286 | [self cleanupAndNotifyWithError:error]; 287 | return; 288 | } 289 | 290 | if (self.HMACKey) { 291 | CCHmacInit(&_HMACContext, self.settings.HMACAlgorithm, self.HMACKey.bytes, self.HMACKey.length); 292 | self.HMACLength = self.settings.HMACLength; 293 | self.HMACKey = nil; // Don't need this anymore 294 | 295 | if (! self.hasV1HMAC) { 296 | CCHmacUpdate(&_HMACContext, [header bytes], [header length]); 297 | } 298 | } 299 | } 300 | 301 | - (void)finish 302 | { 303 | if (self.isFinished) { 304 | return; 305 | } 306 | 307 | dispatch_async(self.queue, ^{ 308 | NSError *error = nil; 309 | 310 | if (self.hasHMAC) { 311 | NSMutableData *HMACData = [NSMutableData dataWithLength:self.HMACLength]; 312 | CCHmacFinal(&self->_HMACContext, [HMACData mutableBytes]); 313 | 314 | if (![HMACData rnc_isEqualInConsistentTime:self.inData]) { 315 | [self cleanupAndNotifyWithError:[NSError errorWithDomain:kRNCryptorErrorDomain 316 | code:kRNCryptorHMACMismatch 317 | userInfo:[NSDictionary dictionaryWithObject:@"HMAC Mismatch" /* DNL */ 318 | forKey:NSLocalizedDescriptionKey]]]; 319 | return; 320 | } 321 | } 322 | 323 | NSData *decryptedData = [self.engine finishWithError:&error]; 324 | 325 | if (!decryptedData) { 326 | [self cleanupAndNotifyWithError:error]; 327 | return; 328 | } 329 | [self.outData appendData:decryptedData]; 330 | 331 | [self cleanupAndNotifyWithError:nil]; 332 | }); 333 | } 334 | 335 | @end 336 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RNCryptor 2 | 3 | Cross-language AES Encryptor/Decryptor [data 4 | format](https://github.com/RNCryptor/RNCryptor-Spec/blob/master/RNCryptor-Spec-v3.md). 5 | 6 | The primary target is Objective-C, but implementations are available in 7 | [C++](https://github.com/RNCryptor/RNCryptor-cpp), 8 | [C#](https://github.com/RNCryptor/RNCryptor-cs), 9 | [Java](https://github.com/RNCryptor/JNCryptor), 10 | [PHP](https://github.com/RNCryptor/RNCryptor-php), 11 | [Python](https://github.com/RNCryptor/RNCryptor-python), 12 | [Javascript](https://github.com/RNCryptor/rncryptor-js), 13 | and [Ruby](https://github.com/RNCryptor/ruby_rncryptor). 14 | 15 | The data format includes all the metadata required to securely implement AES 16 | encryption, as described in ["Properly encrypting with AES with 17 | CommonCrypto,"](http://robnapier.net/aes-commoncrypto) and [*iOS 6 18 | Programming Pushing the Limits*](http://iosptl.com), Chapter 15. Specifically, 19 | it includes: 20 | 21 | * AES-256 encryption 22 | * CBC mode 23 | * Password stretching with PBKDF2 24 | * Password salting 25 | * Random IV 26 | * Encrypt-then-hash HMAC 27 | 28 | ## Basic Objective-C Usage 29 | 30 | The most common in-memory use case is as follows: 31 | 32 | ``` objc 33 | NSData *data = [@"Data" dataUsingEncoding:NSUTF8StringEncoding]; 34 | NSError *error; 35 | NSData *encryptedData = [RNEncryptor encryptData:data 36 | withSettings:kRNCryptorAES256Settings 37 | password:aPassword 38 | error:&error]; 39 | ``` 40 | 41 | This generates an `NSData` including a header, encryption salt, HMAC salt, IV, 42 | ciphertext, and HMAC. To decrypt this bundle: 43 | 44 | ``` objc 45 | NSData *decryptedData = [RNDecryptor decryptData:encryptedData 46 | withPassword:aPassword 47 | error:&error]; 48 | ``` 49 | 50 | Note that `RNDecryptor` does not require settings. These are read from the 51 | header. 52 | 53 | ## Asynchronous use 54 | 55 | `RNCryptor suports asynchronous use, specifically designed to work with 56 | `NSURLConnection. This is also useful for cases where the encrypted or decrypted 57 | `data will not comfortably fit in memory. If the data will comfortably fit in 58 | `memory, asynchronous operation is best acheived using dispatch_async(). 59 | 60 | To operate in asynchronous mode, you create an `RNEncryptor` or `RNDecryptor`, 61 | providing it a handler. This handler will be called as data is encrypted or 62 | decrypted. As data becomes available, call `addData:`. When you reach the end of 63 | the data call `finish`. 64 | 65 | ``` objc 66 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 67 | [self.encryptedData addData:[self.cryptor addData:data]]; 68 | } 69 | 70 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection { 71 | [self.cryptor finish]; 72 | } 73 | 74 | // Other connection delegates 75 | 76 | - (void)decryptionDidFinish { 77 | if (self.cryptor.error) { 78 | // An error occurred. You cannot trust encryptedData at this point 79 | } 80 | else { 81 | // self.encryptedData is complete. Use it as you like 82 | } 83 | self.encryptedData = nil; 84 | self.cryptor = nil; 85 | self.connection = nil; 86 | } 87 | 88 | - (void)decryptRequest:(NSURLRequest *)request { 89 | self.encryptedData = [NSMutableData data]; 90 | self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 91 | self.cryptor = [[RNDecryptor alloc] initWithPassword:self.password 92 | handler:^(RNCryptor *cryptor, NSData *data) { 93 | [self.decryptedData appendData:data]; 94 | if (cryptor.isFinished) { 95 | [self decryptionDidFinish]; 96 | } 97 | }]; 98 | } 99 | ``` 100 | 101 | ## Async and Streams 102 | 103 | When performing async operations on streams, the data can come very quickly 104 | (particularly if you're reading from a local file). If you use RNCryptor in a 105 | naïve way, you'll queue a work blocks faster than the engine can process them 106 | and your memory usage will spike. This is particularly true if there's only one 107 | core, such as on an iPad 1. The solution is to only dispatch new work blocks as 108 | the previous work blocks complete. 109 | 110 | ``` objc 111 | // Make sure that this number is larger than the header + 1 block. 112 | // 33+16 bytes = 49 bytes. So it shouldn't be a problem. 113 | int blockSize = 32 * 1024; 114 | 115 | NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:@"C++ Spec.pdf"]; 116 | NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:@"/tmp/C++.crypt" append:NO]; 117 | 118 | [cryptedStream open]; 119 | [decryptedStream open]; 120 | 121 | // We don't need to keep making new NSData objects. We can just use one repeatedly. 122 | __block NSMutableData *data = [NSMutableData dataWithLength:blockSize]; 123 | __block RNEncryptor *decryptor = nil; 124 | 125 | dispatch_block_t readStreamBlock = ^{ 126 | [data setLength:blockSize]; 127 | NSInteger bytesRead = [cryptedStream read:[data mutableBytes] maxLength:blockSize]; 128 | if (bytesRead < 0) { 129 | // Throw an error 130 | } 131 | else if (bytesRead == 0) { 132 | [decryptor finish]; 133 | } 134 | else { 135 | [data setLength:bytesRead]; 136 | [decryptor addData:data]; 137 | NSLog(@"Sent %ld bytes to decryptor", (unsigned long)bytesRead); 138 | } 139 | }; 140 | 141 | decryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings 142 | password:@"blah" 143 | handler:^(RNCryptor *cryptor, NSData *data) { 144 | NSLog(@"Decryptor recevied %ld bytes", (unsigned long)data.length); 145 | [decryptedStream write:data.bytes maxLength:data.length]; 146 | if (cryptor.isFinished) { 147 | [decryptedStream close]; 148 | // call my delegate that I'm finished with decrypting 149 | } 150 | else { 151 | // Might want to put this in a dispatch_async(), but I don't think you need it. 152 | readStreamBlock(); 153 | } 154 | }]; 155 | 156 | // Read the first block to kick things off 157 | readStreamBlock(); 158 | ``` 159 | 160 | I'll eventually get this into the API to simplify things. See [Cyrille's SO 161 | question](http://stackoverflow.com/a/14586231/97337) for more discussion. Pull 162 | requests welcome. 163 | 164 | ## Building 165 | 166 | Comes packaged as a static library, but the source files can be dropped into any 167 | project. The OpenSSL files are not required. 168 | 169 | Requires `Security.framework`. 170 | 171 | Supports 10.6+ and iOS 4+. 172 | 173 | The current file format is v3. To read v1 files (see Issue #44), you need to set the compile-time macro `RNCRYPTOR_ALLOW_V1_BAD_HMAC`. It is not possible to write v1 files anymore. 174 | 175 | ## Design considerations 176 | 177 | `RNCryptor` has several design goals, in order of importance: 178 | 179 | ### Easy to use correctly for most common use cases 180 | 181 | The most critical concern is that it be easy for non-experts to use `RNCryptor` correctly. A framework that is more secure, but requires a steep learning curve on the developer will either be not used, or used incorrectly. Whenever possible, a single line of code should "do the right thing" for the most common cases. 182 | 183 | This also requires that it fail correctly and provide good errors. 184 | 185 | ### Reliance on CommonCryptor functionality 186 | 187 | `RNCryptor` has very little "security" code. It relies as much as possible on the OS-provided CommonCryptor. If a feature does not exist in CommonCryptor, then it generally will not be provided in `RNCryptor`. 188 | 189 | ### Best practice security 190 | 191 | Wherever possible within the above constraints, the best available algorithms 192 | are applied. This means AES-256, HMAC+SHA1, and PBKDF2: 193 | 194 | * AES-256. While Bruce Schneier has made some interesting recommendations 195 | regarding moving to AES-128 due to certain attacks on AES-256, my current 196 | thinking is in line with [Colin 197 | Percival](http://www.daemonology.net/blog/2009-07-31-thoughts-on-AES.html). 198 | PBKDF2 output is effectively random, which should negate related-keys attacks 199 | against the kinds of use cases we're interested in. 200 | 201 | * AES-CBC mode. This was a somewhat complex decision, but the ubiquity of CBC 202 | outweighs other considerations here. There are no major problems with CBC mode, 203 | and nonce-based modes like CTR have other trade-offs. See ["Mode changes for 204 | RNCryptor"](http://robnapier.net/blog/mode-rncryptor) for more details on this 205 | decision. 206 | 207 | * Encrypt-then-MAC. If there were a good authenticated AES mode on iOS (GCM for 208 | instance), I would probably use that for its simplicity. Colin Percival makes 209 | [good arguments for hand-coding an encrypt-than- 210 | MAC](http://www.daemonology.net/blog/2009-06-24-encrypt-then-mac.html) rather 211 | than using an authenticated AES mode, but in RNCryptor mananging the HMAC 212 | actually adds quite a bit of complexity. I'd rather the complexity at a more 213 | broadly peer-reviewed layer like CommonCryptor than at the RNCryptor layer. But 214 | this isn't an option, so I fall back to my own Encrypt-than-MAC. 215 | 216 | * HMAC+SHA256. No surprises here. 217 | 218 | * PBKDF2. While bcrypt and scrypt may be more secure than PBKDF2, CommonCryptor 219 | only supports PBKDF2. [NIST also continues to recommend 220 | PBKDF2](http://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage). We use 10k rounds of PBKDF2 221 | which represents about 80ms on an iPhone 4. 222 | 223 | ### Code simplicity 224 | 225 | `RNCryptor endeavors to be implemented as simply as possible, avoiding tricky 226 | `code. It is designed to be easy to read and code review. 227 | 228 | ### Performance 229 | 230 | Performance is a goal, but not the most important goal. The code must be secure 231 | and easy to use. Within that, it is as fast and memory-efficient as possible. 232 | 233 | ### Portability 234 | 235 | Without sacrificing other goals, it is preferable to read the output format of 236 | `RNCryptor` on other platforms. 237 | 238 | ## Version History 239 | 240 | * v2.2 Switches to file format v3 to deal with Issue #77. 241 | * v2.1 Switches to file format v2 to deal with Issue #44. 242 | * v2.0 adds asynchronous modes. 243 | * v2.1 backports `RNCryptor` to older versions of Mac OS X (and possibly iOS). 244 | 245 | 246 | ## LICENSE 247 | 248 | Except where otherwise indicated in the source code, this code is licensed under 249 | the MIT License: 250 | 251 | ``` 252 | Permission is hereby granted, free of charge, to any person obtaining a 253 | copy of this software and associated documentation files (the "Software"), 254 | to deal in the Software without restriction, including without limitation 255 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 256 | and/or sell copies of the Software, and to permit persons to whom the 257 | Software is furnished to do so, subject to the following conditions: 258 | 259 | The above copyright notice and this permission notice shall be included in 260 | all copies or substantial portions of the Software. 261 | 262 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 263 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 264 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 265 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 266 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 267 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 268 | DEALINGS IN THE SOFTWARE. 269 | ``` 270 | 271 | Portions of this code, indicated in the source, are licensed under the following 272 | license: 273 | 274 | ``` 275 | /*- 276 | * Copyright (c) 2008 Damien Bergamini 277 | * 278 | * Permission to use, copy, modify, and distribute this software for any 279 | * purpose with or without fee is hereby granted, provided that the above 280 | * copyright notice and this permission notice appear in all copies. 281 | * 282 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 283 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 284 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 285 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 286 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 287 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 288 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 289 | */ 290 | ``` 291 | 292 | Portions of this code, indicated in the source, are licensed under the APSL 293 | license: 294 | 295 | ``` 296 | /* 297 | * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved. 298 | * 299 | * @APPLE_LICENSE_HEADER_START@ 300 | * 301 | * This file contains Original Code and/or Modifications of Original Code 302 | * as defined in and that are subject to the Apple Public Source License 303 | * Version 2.0 (the 'License'). You may not use this file except in 304 | * compliance with the License. Please obtain a copy of the License at 305 | * http://www.opensource.apple.com/apsl/ and read it before using this 306 | * file. 307 | * 308 | * The Original Code and all software distributed under the License are 309 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 310 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 311 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 312 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 313 | * Please see the License for the specific language governing rights and 314 | * limitations under the License. 315 | * 316 | * @APPLE_LICENSE_HEADER_END@ 317 | */ 318 | ``` 319 | -------------------------------------------------------------------------------- /RNCryptorTests/Generated/RNCryptorGeneratedVectorTests.m: -------------------------------------------------------------------------------- 1 | // Automatically Generated by GenVectorTests 2 | #import "XCTestCase+RNCryptorVectorTests.h" 3 | @interface RNCryptorGeneratedVectorTests : XCTestCase 4 | @end 5 | @implementation RNCryptorGeneratedVectorTests 6 | 7 | - (void)test_v3_kdf_One_byte { 8 | [self verify_v3_kdf:@{ 9 | @"title": @"One byte", 10 | @"version": @"3", 11 | @"password": @"a", 12 | @"salt_hex": @"0102030405060708", 13 | @"key_hex": @"fc632b0c a6b23eff 9a9dc3e0 e585167f 5a328916 ed19f835 58be3ba9 828797cd"}]; 14 | } 15 | 16 | 17 | - (void)test_v3_kdf_Short_password { 18 | [self verify_v3_kdf:@{ 19 | @"title": @"Short password", 20 | @"version": @"3", 21 | @"password": @"thepassword", 22 | @"salt_hex": @"0203040506070801", 23 | @"key_hex": @"0ea84f52 52310dc3 e3a7607c 33bfd1eb 580805fb 68293005 da21037c cf499626"}]; 24 | } 25 | 26 | 27 | - (void)test_v3_kdf_Passphrase { 28 | [self verify_v3_kdf:@{ 29 | @"title": @"Passphrase", 30 | @"version": @"3", 31 | @"password": @"this is a bit longer password", 32 | @"salt_hex": @"0304050607080102", 33 | @"key_hex": @"71343acb 1e9675b0 16ac65dc fe5ddac2 e57ed9c3 5565fdbb 2dd6d2ce fe263d5b"}]; 34 | } 35 | 36 | 37 | - (void)test_v3_kdf_Long_passphrase { 38 | [self verify_v3_kdf:@{ 39 | @"title": @"Long passphrase", 40 | @"version": @"3", 41 | @"password": @"$$$it was the epoch of belief, it was the epoch of incredulity; it was the season of Light, it was the season of Darkness; it was the spring of hope, it was the winter of despair; we had everything before us, we had nothing before us; we were all going directly to Heaven, we were all going the other way.", 42 | @"salt_hex": @"0405060708010203", 43 | @"key_hex": @"11b52c50 cbf45be6 a636a314 2b8c30b8 5a624481 4a7d43e3 7457f38d e46c6735"}]; 44 | } 45 | 46 | 47 | - (void)test_v3_kdf_Multibyte { 48 | [self verify_v3_kdf:@{ 49 | @"title": @"Multibyte", 50 | @"version": @"3", 51 | @"password": @"中文密码", 52 | @"salt_hex": @"0506070801020304", 53 | @"key_hex": @"d2fc3237 d4a69668 ca83d969 c2cda1ac 6c368479 2b6644b1 a90b2052 007215dd"}]; 54 | } 55 | 56 | 57 | - (void)test_v3_kdf_Mixed_language { 58 | [self verify_v3_kdf:@{ 59 | @"title": @"Mixed language", 60 | @"version": @"3", 61 | @"password": @"中文密码 with a little English, too.", 62 | @"salt_hex": @"0607080102030405", 63 | @"key_hex": @"46bda5f4 65982a47 40c728bc 14c5de5c c7fc4eea f0aa41bb 9b9e8495 452dafff"}]; 64 | } 65 | 66 | 67 | - (void)test_v3_password_All_fields_empty_or_zero_with_one_byte_password_ { 68 | [self verify_v3_password:@{ 69 | @"title": @"All fields empty or zero (with one-byte password)", 70 | @"version": @"3", 71 | @"password": @"a", 72 | @"enc_salt_hex": @"0000000000000000", 73 | @"hmac_salt_hex": @"0000000000000000", 74 | @"iv_hex": @"00000000000000000000000000000000", 75 | @"plaintext_hex": @"", 76 | @"ciphertext_hex": @"03010000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000b303 9be31cd7 ece5e754 f5c8da17 00366631 3ae8a89d dcf8e3cb 41fdc130 b2329dbe 07d6f4d3 2c34e050 c8bd7e93 3b12"}]; 77 | } 78 | 79 | 80 | - (void)test_v3_password_One_byte { 81 | [self verify_v3_password:@{ 82 | @"title": @"One byte", 83 | @"version": @"3", 84 | @"password": @"thepassword", 85 | @"enc_salt_hex": @"0001020304050607", 86 | @"hmac_salt_hex": @"0102030405060708", 87 | @"iv_hex": @"02030405060708090a0b0c0d0e0f0001", 88 | @"plaintext_hex": @"01", 89 | @"ciphertext_hex": @"03010001 02030405 06070102 03040506 07080203 04050607 08090a0b 0c0d0e0f 0001a1f8 730e0bf4 80eb7b70 f690abf2 1e029514 164ad3c4 74a51b30 c7eaa1ca 545b7de3 de5b010a cbad0a9a 13857df6 96a8"}]; 90 | } 91 | 92 | 93 | - (void)test_v3_password_Exactly_one_block { 94 | [self verify_v3_password:@{ 95 | @"title": @"Exactly one block", 96 | @"version": @"3", 97 | @"password": @"thepassword", 98 | @"enc_salt_hex": @"0102030405060700", 99 | @"hmac_salt_hex": @"0203040506070801", 100 | @"iv_hex": @"030405060708090a0b0c0d0e0f000102", 101 | @"plaintext_hex": @"0123456789abcdef", 102 | @"ciphertext_hex": @"03010102 03040506 07000203 04050607 08010304 05060708 090a0b0c 0d0e0f00 01020e43 7fe80930 9c03fd53 a475131e 9a1978b8 eaef576f 60adb8ce 2320849b a32d7429 00438ba8 97d22210 c76c35c8 49df"}]; 103 | } 104 | 105 | 106 | - (void)test_v3_password_More_than_one_block { 107 | [self verify_v3_password:@{ 108 | @"title": @"More than one block", 109 | @"version": @"3", 110 | @"password": @"thepassword", 111 | @"enc_salt_hex": @"0203040506070001", 112 | @"hmac_salt_hex": @"0304050607080102", 113 | @"iv_hex": @"0405060708090a0b0c0d0e0f00010203", 114 | @"plaintext_hex": @"0123456789abcdef 01234567", 115 | @"ciphertext_hex": @"03010203 04050607 00010304 05060708 01020405 06070809 0a0b0c0d 0e0f0001 0203e01b bda5df2c a8adace3 8f6c588d 291e03f9 51b78d34 17bc2816 581dc6b7 67f1a2e5 7597512b 18e1638f 21235fa5 928c"}]; 116 | } 117 | 118 | 119 | - (void)test_v3_password_Multibyte_password { 120 | [self verify_v3_password:@{ 121 | @"title": @"Multibyte password", 122 | @"version": @"3", 123 | @"password": @"中文密码", 124 | @"enc_salt_hex": @"0304050607000102", 125 | @"hmac_salt_hex": @"0405060708010203", 126 | @"iv_hex": @"05060708090a0b0c0d0e0f0001020304", 127 | @"plaintext_hex": @"23456789abcdef 0123456701", 128 | @"ciphertext_hex": @"03010304 05060700 01020405 06070801 02030506 0708090a 0b0c0d0e 0f000102 03048a9e 08bdec1c 4bfe13e8 1fb85f00 9ab3ddb9 1387e809 c4ad86d9 e8a60145 57716657 bd317d4b b6a76446 15b3de40 2341"}]; 129 | } 130 | 131 | 132 | - (void)test_v3_password_Longer_text_and_password { 133 | [self verify_v3_password:@{ 134 | @"title": @"Longer text and password", 135 | @"version": @"3", 136 | @"password": @"It was the best of times, it was the worst of times; it was the age of wisdom, it was the age of foolishness;", 137 | @"enc_salt_hex": @"0405060700010203", 138 | @"hmac_salt_hex": @"0506070801020304", 139 | @"iv_hex": @"060708090a0b0c0d0e0f000102030405", 140 | @"plaintext_hex": @"69 74 20 77 61 73 20 74 68 65 20 65 70 6f 63 68 20 6f 66 20 62 65 6c 69 65 66 2c 20 69 74 20 77 61 73 20 74 68 65 20 65 70 6f 63 68 20 6f 66 20 69 6e 63 72 65 64 75 6c 69 74 79 3b 20 69 74 20 77 61 73 20 74 68 65 20 73 65 61 73 6f 6e 20 6f 66 20 4c 69 67 68 74 2c 20 69 74 20 77 61 73 20 74 68 65 20 73 65 61 73 6f 6e 20 6f 66 20 44 61 72 6b 6e 65 73 73 3b 20 69 74 20 77 61 73 20 74 68 65 20 73 70 72 69 6e 67 20 6f 66 20 68 6f 70 65 2c 20 69 74 20 77 61 73 20 74 68 65 20 77 69 6e 74 65 72 20 6f 66 20 64 65 73 70 61 69 72 3b 20 77 65 20 68 61 64 20 65 76 65 72 79 74 68 69 6e 67 20 62 65 66 6f 72 65 20 75 73 2c 20 77 65 20 68 61 64 20 6e 6f 74 68 69 6e 67 20 62 65 66 6f 72 65 20 75 73 3b 20 77 65 20 77 65 72 65 20 61 6c 6c 20 67 6f 69 6e 67 20 64 69 72 65 63 74 6c 79 20 74 6f 20 48 65 61 76 65 6e 2c 20 77 65 20 77 65 72 65 20 61 6c 6c 20 67 6f 69 6e 67 20 74 68 65 20 6f 74 68 65 72 20 77 61 79 2e 0a 0a", 141 | @"ciphertext_hex": @"03010405 06070001 02030506 07080102 03040607 08090a0b 0c0d0e0f 00010203 0405d564 c7a99da9 21a6e7c4 078a8264 1d954795 51283167 a2c81f31 ab80c9d7 d8beb770 111decd3 e3d29bbd f7ebbfc5 f10ac87e 7e55bfb5 a7f487bc d3983570 5e83b9c0 49c6d695 2be011f8 ddb1a14f c0c92573 8de017e6 2b1d621c cdb75f29 37d0a1a7 0e44d843 b9c61037 dee2998b 2bbd740b 910232ee a7196116 8838f699 5b996417 3b34c0bc d311a2c8 7e271630 928bae30 1a8f4703 ac2ae469 9f3c285a bf1c55ac 324b073a 958ae52e e8c3bd68 f919c09e b1cd2814 2a1996a9 e6cbff5f 4f4e1dba 07d29ff6 6860db98 95a48233 140ca249 419d6304 6448db1b 0f4252a6 e4edb947 fd0071d1 e52bc156 00622fa5 48a67739 63618150 797a8a80 e592446d f5926d0b fd32b544 b796f335 9567394f 77e7b171 b2f9bc5f 2caf7a0f ac0da7d0 4d6a8674 4d6e06d0 2fbe15d0 f580a1d5 bd16ad91 34800361 1358dcb4 ac999095 5f6cbbbf b185941d 4b4b71ce 7f9ba6ef c1270b78 08838b6c 7b7ef17e 8db919b3 4fac"}]; 142 | } 143 | 144 | 145 | - (void)test_v3_key_All_fields_empty_or_zero { 146 | [self verify_v3_key:@{ 147 | @"title": @"All fields empty or zero", 148 | @"version": @"3", 149 | @"enc_key_hex": @"0000000000000000000000000000000000000000000000000000000000000000", 150 | @"hmac_key_hex": @"0000000000000000000000000000000000000000000000000000000000000000", 151 | @"iv_hex": @"00000000000000000000000000000000", 152 | @"plaintext_hex": @"", 153 | @"ciphertext_hex": @"03000000 00000000 00000000 00000000 00001f78 8fe6d86c 31754969 7fbf0c07 fa436384 ac0ef35b 860b2ddb 2aba2fff 816b1fb3 a9c180f7 b43650ae c0d2b5f8 8e33"}]; 154 | } 155 | 156 | 157 | - (void)test_v3_key_One_byte { 158 | [self verify_v3_key:@{ 159 | @"title": @"One byte", 160 | @"version": @"3", 161 | @"enc_key_hex": @"000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", 162 | @"hmac_key_hex": @"0102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f00", 163 | @"iv_hex": @"02030405060708090a0b0c0d0e0f0001", 164 | @"plaintext_hex": @"01", 165 | @"ciphertext_hex": @"03000203 04050607 08090a0b 0c0d0e0f 0001981b 22e7a644 8118d695 bd654f72 e9d6ed75 ec14ae2a a067eed2 a98a56e0 993dfe22 ab5887b3 f6e3cdd4 0767f519 5eb5"}]; 166 | } 167 | 168 | 169 | - (void)test_v3_key_Exactly_one_block { 170 | [self verify_v3_key:@{ 171 | @"title": @"Exactly one block", 172 | @"version": @"3", 173 | @"enc_key_hex": @"0102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f00", 174 | @"hmac_key_hex": @"02030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f0001", 175 | @"iv_hex": @"030405060708090a0b0c0d0e0f000102", 176 | @"plaintext_hex": @"000102030405060708090a0b0c0d0e0f", 177 | @"ciphertext_hex": @"03000304 05060708 090a0b0c 0d0e0f00 0102d2b1 77d61878 1829f564 53f739a2 d4f729f9 2b1a9c6c 50837864 74e16a22 c60f92b0 73454f79 76cdda04 3e09b117 66de05ff e05bc1dc a9522ea6 6e64ad25 bbbc"}]; 178 | } 179 | 180 | 181 | - (void)test_v3_key_More_than_one_block { 182 | [self verify_v3_key:@{ 183 | @"title": @"More than one block", 184 | @"version": @"3", 185 | @"enc_key_hex": @"02030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f0001", 186 | @"hmac_key_hex": @"030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102", 187 | @"iv_hex": @"0405060708090a0b0c0d0e0f00010203", 188 | @"plaintext_hex": @"000102030405060708090a0b0c0d0e0f 000102030405060708", 189 | @"ciphertext_hex": @"03000405 06070809 0a0b0c0d 0e0f0001 02034c9b 98b425f1 d732644c b311278d 858e3d18 2a0789b8 6af7f741 34b6a27e 9d938617 741c0fb8 aaf094b3 b5b26f50 5da7bf19 13f6c17e 70273977 ae51323b 6f09"}]; 190 | } 191 | 192 | 193 | - (void)test_v2_kdf_One_byte { 194 | [self verify_v2_kdf:@{ 195 | @"title": @"One byte", 196 | @"version": @"2", 197 | @"password": @"a", 198 | @"salt_hex": @"0102030405060708", 199 | @"key_hex": @"fc632b0c a6b23eff 9a9dc3e0 e585167f 5a328916 ed19f835 58be3ba9 828797cd"}]; 200 | } 201 | 202 | 203 | - (void)test_v2_kdf_Short_password { 204 | [self verify_v2_kdf:@{ 205 | @"title": @"Short password", 206 | @"version": @"2", 207 | @"password": @"thepassword", 208 | @"salt_hex": @"0203040506070801", 209 | @"key_hex": @"0ea84f52 52310dc3 e3a7607c 33bfd1eb 580805fb 68293005 da21037c cf499626"}]; 210 | } 211 | 212 | 213 | - (void)test_v2_kdf_Passphrase { 214 | [self verify_v2_kdf:@{ 215 | @"title": @"Passphrase", 216 | @"version": @"2", 217 | @"password": @"this is a bit longer password", 218 | @"salt_hex": @"0304050607080102", 219 | @"key_hex": @"71343acb 1e9675b0 16ac65dc fe5ddac2 e57ed9c3 5565fdbb 2dd6d2ce fe263d5b"}]; 220 | } 221 | 222 | 223 | - (void)test_v2_kdf_Long_passphrase { 224 | [self verify_v2_kdf:@{ 225 | @"title": @"Long passphrase", 226 | @"version": @"2", 227 | @"password": @"$$$it was the epoch of belief, it was the epoch of incredulity; it was the season of Light, it was the season of Darkness; it was the spring of hope, it was the winter of despair; we had everything before us, we had nothing before us; we were all going directly to Heaven, we were all going the other way.", 228 | @"salt_hex": @"0405060708010203", 229 | @"key_hex": @"11b52c50 cbf45be6 a636a314 2b8c30b8 5a624481 4a7d43e3 7457f38d e46c6735"}]; 230 | } 231 | 232 | 233 | - (void)test_v2_password_Multi_block { 234 | [self verify_v2_password:@{ 235 | @"title": @"Multi-block", 236 | @"version": @"2", 237 | @"password": @"password", 238 | @"enc_salt_hex": @"9707 6dc6 61b6 e0ce", 239 | @"hmac_salt_hex": @"9da3 bb43 d95b cd45", 240 | @"iv_hex": @"ee39 6d39 e342 ffdb 679b 270d cd9c 557c", 241 | @"plaintext_hex": @"546869732069732061206c6f6e676572207465737420766563746f722069 6e74656e64656420746f206265206c6f6e676572207468616e206f6e6520 626c6f636b2e", 242 | @"ciphertext_hex": @"020197076dc661b6e0ce9da3bb43d95bcd45ee396d39e342ffdb679b270d cd9c557c37055fffcc1b663b1e6b8c5694dbb96d97a3ac0fa3f355db6668 c5a8a2a06f10056ce92384a618a35bf0fa9eb612b0b4fa72f749f76e2f72 8c16574dc2f15b7cec1786d291c2135f932ddc5a34d9eafd6b45f99491ac 23c34299af0be68a43e6e8113bb748fbc19bcad638ea79b07309"}]; 243 | } 244 | 245 | 246 | - (void)test_v1_kdf_One_byte { 247 | [self verify_v1_kdf:@{ 248 | @"title": @"One byte", 249 | @"version": @"1", 250 | @"password": @"a", 251 | @"salt_hex": @"0102030405060708", 252 | @"key_hex": @"fc632b0c a6b23eff 9a9dc3e0 e585167f 5a328916 ed19f835 58be3ba9 828797cd"}]; 253 | } 254 | 255 | 256 | - (void)test_v1_kdf_Short_password { 257 | [self verify_v1_kdf:@{ 258 | @"title": @"Short password", 259 | @"version": @"1", 260 | @"password": @"thepassword", 261 | @"salt_hex": @"0203040506070801", 262 | @"key_hex": @"0ea84f52 52310dc3 e3a7607c 33bfd1eb 580805fb 68293005 da21037c cf499626"}]; 263 | } 264 | 265 | 266 | - (void)test_v1_kdf_Passphrase { 267 | [self verify_v1_kdf:@{ 268 | @"title": @"Passphrase", 269 | @"version": @"1", 270 | @"password": @"this is a bit longer password", 271 | @"salt_hex": @"0304050607080102", 272 | @"key_hex": @"71343acb 1e9675b0 16ac65dc fe5ddac2 e57ed9c3 5565fdbb 2dd6d2ce fe263d5b"}]; 273 | } 274 | 275 | 276 | - (void)test_v1_kdf_Long_passphrase { 277 | [self verify_v1_kdf:@{ 278 | @"title": @"Long passphrase", 279 | @"version": @"1", 280 | @"password": @"$$$it was the epoch of belief, it was the epoch of incredulity; it was the season of Light, it was the season of Darkness; it was the spring of hope, it was the winter of despair; we had everything before us, we had nothing before us; we were all going directly to Heaven, we were all going the other way.", 281 | @"salt_hex": @"0405060708010203", 282 | @"key_hex": @"11b52c50 cbf45be6 a636a314 2b8c30b8 5a624481 4a7d43e3 7457f38d e46c6735"}]; 283 | } 284 | 285 | 286 | - (void)test_v1_password_Multi_block { 287 | [self verify_v1_password:@{ 288 | @"title": @"Multi-block", 289 | @"version": @"1", 290 | @"password": @"password", 291 | @"enc_salt_hex": @"9707 6dc6 61b6 e0ce", 292 | @"hmac_salt_hex": @"9da3 bb43 d95b cd45", 293 | @"iv_hex": @"ee39 6d39 e342 ffdb 679b 270d cd9c 557c", 294 | @"plaintext_hex": @"546869732069732061206c6f6e676572207465737420766563746f722069 6e74656e64656420746f206265206c6f6e676572207468616e206f6e6520 626c6f636b2e", 295 | @"ciphertext_hex": @"0101d5e9b0eeefa3336d7578c1f9babe4b5de9ecf9598c104ae1e080ec71 e0cabad22204d8d0dc5bf77203fa7e46465d09136cd4a194aadf2b5593d5 f9122aa13b27dc7afaca7ca1548e046a92298ee884c014b5b4a55503be28 852ba1a2750208fd5f0e410e7c1eb969c3990c621b3b73a65715de4d9ff5 6e159ee7625e852517b135dbccef0b9460350a04cd6e3d10e925"}]; 296 | } 297 | 298 | @end 299 | -------------------------------------------------------------------------------- /RNCryptor/RNCryptor.m: -------------------------------------------------------------------------------- 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 | 28 | #import "RNCryptor.h" 29 | #import "RNCryptor+Private.h" 30 | 31 | #import 32 | #import 33 | #import 34 | #import 35 | 36 | const RNCryptorSettings kRNCryptorAES256Settings = { 37 | .algorithm = kCCAlgorithmAES128, 38 | .blockSize = kCCBlockSizeAES128, 39 | .IVSize = kCCBlockSizeAES128, 40 | .options = kCCOptionPKCS7Padding, 41 | .HMACAlgorithm = kCCHmacAlgSHA256, 42 | .HMACLength = CC_SHA256_DIGEST_LENGTH, 43 | 44 | .keySettings = { 45 | .keySize = kCCKeySizeAES256, 46 | .saltSize = 8, 47 | .PBKDFAlgorithm = kCCPBKDF2, 48 | .PRF = kCCPRFHmacAlgSHA1, 49 | .rounds = 10000 50 | }, 51 | 52 | .HMACKeySettings = { 53 | .keySize = kCCKeySizeAES256, 54 | .saltSize = 8, 55 | .PBKDFAlgorithm = kCCPBKDF2, 56 | .PRF = kCCPRFHmacAlgSHA1, 57 | .rounds = 10000 58 | } 59 | }; 60 | 61 | // Provide internal symbols for 10.6. These were made available in 10.7. 62 | #ifdef __MAC_OS_X_VERSION_MAX_ALLOWED 63 | #if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060 64 | extern int SecRandomCopyBytes(SecRandomRef rnd, size_t count, uint8_t *bytes) __attribute__((weak_import)); 65 | extern int 66 | CCKeyDerivationPBKDF( CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen, 67 | const uint8_t *salt, size_t saltLen, 68 | CCPseudoRandomAlgorithm prf, uint rounds, 69 | uint8_t *derivedKey, size_t derivedKeyLen) __attribute__((weak_import)); 70 | #endif 71 | #endif 72 | 73 | NSString *const kRNCryptorErrorDomain = @"net.robnapier.RNCryptManager"; 74 | const uint8_t kRNCryptorFileVersion = 3; 75 | 76 | // TODO: This is a slightly expensive solution, but it's convenient. May want to create a "walkable" data object 77 | @implementation NSMutableData (RNCryptor) 78 | - (NSData *)_RNConsumeToIndex:(NSUInteger)index 79 | { 80 | NSData *removed = [self subdataWithRange:NSMakeRange(0, index)]; 81 | [self replaceBytesInRange:NSMakeRange(0, self.length - index) withBytes:([self mutableBytes] + index)]; 82 | [self setLength:self.length - index]; 83 | return removed; 84 | } 85 | @end 86 | 87 | 88 | @implementation RNCryptor 89 | @synthesize responseQueue = _responseQueue; 90 | @synthesize engine = _engine; 91 | @synthesize outData = __outData; 92 | @synthesize queue = _queue; 93 | @synthesize HMACLength = __HMACLength; 94 | @synthesize error = _error; 95 | @synthesize finished = _finished; 96 | @synthesize options = _options; 97 | @synthesize handler = _handler; 98 | 99 | + (NSData *)synchronousResultForCryptor:(RNCryptor *)cryptor data:(NSData *)inData error:(NSError **)anError 100 | { 101 | dispatch_semaphore_t sem = dispatch_semaphore_create(0); 102 | 103 | NSMutableData *data = [NSMutableData data]; 104 | __block NSError *returnedError = nil; 105 | 106 | RNCryptorHandler handler = ^(RNCryptor *c, NSData *d) { 107 | [data appendData:d]; 108 | if (c.isFinished) { 109 | returnedError = c.error; 110 | dispatch_semaphore_signal(sem); 111 | } 112 | }; 113 | 114 | cryptor.handler = handler; 115 | 116 | dispatch_queue_t queue = dispatch_queue_create("net.robnapier.RNEncryptor.response", DISPATCH_QUEUE_SERIAL); 117 | cryptor.responseQueue = queue; 118 | [cryptor addData:inData]; 119 | [cryptor finish]; 120 | 121 | 122 | dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 123 | 124 | #if !OS_OBJECT_USE_OBJC 125 | dispatch_release(sem); 126 | if (queue) { 127 | dispatch_release(queue); 128 | } 129 | #endif 130 | 131 | if (returnedError) { 132 | if (anError) { 133 | *anError = returnedError; 134 | } 135 | return nil; 136 | } 137 | else { 138 | return data; 139 | } 140 | } 141 | 142 | // For use with OS X 10.6 143 | // Based on http://opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/API/CommonKeyDerivation.c 144 | /*- 145 | * Copyright (c) 2008 Damien Bergamini 146 | * 147 | * Permission to use, copy, modify, and distribute this software for any 148 | * purpose with or without fee is hereby granted, provided that the above 149 | * copyright notice and this permission notice appear in all copies. 150 | * 151 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 152 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 153 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 154 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 155 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 156 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 157 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158 | */ 159 | #define CC_MAX_PRF_WORKSPACE 128+4 160 | #define kCCPRFHmacAlgSHA1hlen CC_SHA1_DIGEST_LENGTH 161 | #define kCCPRFHmacAlgSHA224hlen CC_SHA224_DIGEST_LENGTH 162 | #define kCCPRFHmacAlgSHA256hlen CC_SHA256_DIGEST_LENGTH 163 | #define kCCPRFHmacAlgSHA384hlen CC_SHA384_DIGEST_LENGTH 164 | #define kCCPRFHmacAlgSHA512hlen CC_SHA512_DIGEST_LENGTH 165 | 166 | static size_t 167 | getPRFhlen(CCPseudoRandomAlgorithm prf) 168 | { 169 | switch(prf) { 170 | case kCCPRFHmacAlgSHA1: return kCCPRFHmacAlgSHA1hlen; 171 | case kCCPRFHmacAlgSHA224: return kCCPRFHmacAlgSHA224hlen; 172 | case kCCPRFHmacAlgSHA256: return kCCPRFHmacAlgSHA256hlen; 173 | case kCCPRFHmacAlgSHA384: return kCCPRFHmacAlgSHA384hlen; 174 | case kCCPRFHmacAlgSHA512: return kCCPRFHmacAlgSHA512hlen; 175 | default: 176 | NSCAssert(NO, @"Unknown prf: %d", prf); 177 | return 1; 178 | } 179 | } 180 | 181 | static void 182 | PRF(CCPseudoRandomAlgorithm prf, const char *password, size_t passwordLen, u_int8_t *salt, size_t saltLen, u_int8_t *output) 183 | { 184 | switch(prf) { 185 | case kCCPRFHmacAlgSHA1: 186 | CCHmac(kCCHmacAlgSHA1, password, passwordLen, salt, saltLen, output); 187 | break; 188 | case kCCPRFHmacAlgSHA224: 189 | CCHmac(kCCHmacAlgSHA224, password, passwordLen, salt, saltLen, output); 190 | break; 191 | case kCCPRFHmacAlgSHA256: 192 | CCHmac(kCCHmacAlgSHA256, password, passwordLen, salt, saltLen, output); 193 | break; 194 | case kCCPRFHmacAlgSHA384: 195 | CCHmac(kCCHmacAlgSHA384, password, passwordLen, salt, saltLen, output); 196 | break; 197 | case kCCPRFHmacAlgSHA512: 198 | CCHmac(kCCHmacAlgSHA512, password, passwordLen, salt, saltLen, output); 199 | break; 200 | } 201 | } 202 | 203 | static int 204 | RN_CCKeyDerivationPBKDF( CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen, 205 | const uint8_t *salt, size_t saltLen, 206 | CCPseudoRandomAlgorithm prf, uint rounds, 207 | uint8_t *derivedKey, size_t derivedKeyLen) 208 | { 209 | u_int8_t oldbuffer[CC_MAX_PRF_WORKSPACE], newbuffer[CC_MAX_PRF_WORKSPACE], 210 | saltCopy[CC_MAX_PRF_WORKSPACE+4], collector[CC_MAX_PRF_WORKSPACE]; 211 | int rawblock, i, j; 212 | size_t r, nblocks; 213 | size_t hlen, offset; 214 | 215 | if(algorithm != kCCPBKDF2) return -1; 216 | 217 | /* 218 | * Check initial parameters 219 | */ 220 | 221 | if (rounds < 1 || derivedKeyLen == 0) 222 | return -1; // bad parameters 223 | if (saltLen == 0 || saltLen > CC_MAX_PRF_WORKSPACE) 224 | return -1; // out of bounds parameters 225 | 226 | hlen = getPRFhlen(prf); 227 | 228 | /* 229 | * FromSpec: Let l be the number of hLen-octet blocks in the derived key, rounding up, 230 | * and let r be the number of octets in the last block: 231 | */ 232 | 233 | nblocks = (derivedKeyLen+hlen-1)/hlen; // in the spec nblocks is referred to as l 234 | r = derivedKeyLen % hlen; 235 | r = (r) ? r: hlen; 236 | 237 | /* 238 | * Make a copy of the salt buffer so we can concatenate the 239 | * block counter for each series of rounds. 240 | */ 241 | 242 | memcpy(saltCopy, salt, saltLen); 243 | bzero(derivedKey, derivedKeyLen); 244 | 245 | /* 246 | * FromSpec: 247 | * 248 | * For each block of the derived key apply the function F defined below to the password P, 249 | * the salt S, the iteration count c, and the block index to compute the block: 250 | * 251 | * F(P,S,c,i)=U1 \xorU2 \xor⋅⋅⋅\xorUc 252 | * 253 | * where 254 | * U1 =PRF(P,S||INT (i)), 255 | * U2 =PRF(P,U1), 256 | * ... 257 | * Uc = PRF (P, Uc-1) . 258 | */ 259 | 260 | for(rawblock = 0; rawblock < nblocks; rawblock++) { 261 | int block = rawblock+1; 262 | size_t copyLen; 263 | 264 | offset = rawblock * hlen; 265 | copyLen = (block != nblocks) ? hlen: r; 266 | 267 | /* 268 | * FromSpec: Here, INT (i) is a four-octet encoding of the integer i, most significant octet first. 269 | */ 270 | 271 | for(i=0; i<4; i++) saltCopy[saltLen+i] = (block >> 8*(3-i)) & 0xff; 272 | 273 | PRF(prf, password, passwordLen, saltCopy, saltLen+4, oldbuffer); // Initial PRF with the modified salt 274 | 275 | memcpy(collector, oldbuffer, hlen); // Initial value for this block of the derived key. 276 | 277 | for(i = 1; i < rounds; i++) { 278 | PRF(prf, password, passwordLen, oldbuffer, hlen, newbuffer); // Subsequent PRF with the previous result as the salt 279 | memcpy(oldbuffer, newbuffer, hlen); 280 | for(j = 0; j < hlen; j++) collector[j] ^= newbuffer[j]; // Xoring the round into the collector 281 | } 282 | memcpy(derivedKey+offset, collector, copyLen); 283 | } 284 | 285 | /* 286 | * Clear temp buffers. 287 | */ 288 | 289 | bzero(oldbuffer, CC_MAX_PRF_WORKSPACE); 290 | bzero(newbuffer, CC_MAX_PRF_WORKSPACE); 291 | bzero(collector, CC_MAX_PRF_WORKSPACE); 292 | bzero(saltCopy, CC_MAX_PRF_WORKSPACE+4); 293 | 294 | return 0; 295 | } 296 | 297 | /* End code derived from CommonKeyDerivation.c */ 298 | 299 | 300 | + (NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt settings:(RNCryptorKeyDerivationSettings)keySettings 301 | { 302 | NSMutableData *derivedKey = [NSMutableData dataWithLength:keySettings.keySize]; 303 | 304 | // See Issue #77. V2 incorrectly calculated key for multi-byte characters. 305 | NSData *passwordData; 306 | if (keySettings.hasV2Password) { 307 | passwordData = [NSData dataWithBytes:[password UTF8String] length:[password length]]; 308 | } 309 | else { 310 | passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; 311 | } 312 | 313 | // Use the built-in PBKDF2 if it's available. Otherwise, we have our own. Hello crazy function pointer. 314 | int result; 315 | int (*PBKDF)(CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen, 316 | const uint8_t *salt, size_t saltLen, 317 | CCPseudoRandomAlgorithm prf, uint rounds, 318 | uint8_t *derivedKey, size_t derivedKeyLen); 319 | 320 | #pragma clang diagnostic push 321 | #pragma clang diagnostic ignored "-Wunreachable-code" 322 | PBKDF = CCKeyDerivationPBKDF ?: RN_CCKeyDerivationPBKDF; 323 | #pragma clang diagnostic pop 324 | 325 | result = PBKDF(keySettings.PBKDFAlgorithm, // algorithm 326 | passwordData.bytes, // password 327 | passwordData.length, // passwordLength 328 | salt.bytes, // salt 329 | salt.length, // saltLen 330 | keySettings.PRF, // PRF 331 | keySettings.rounds, // rounds 332 | derivedKey.mutableBytes, // derivedKey 333 | derivedKey.length); // derivedKeyLen 334 | 335 | // Do not log password here 336 | NSAssert(result == kCCSuccess, @"Unable to create AES key for password: %d", result); 337 | 338 | return derivedKey; 339 | } 340 | 341 | // For use on OS X 10.6 342 | // Based on http://www.opensource.apple.com/source/Security/Security-55179.1/sec/Security/SecFramework.c 343 | // Modified by Rob Napier April, 2013. 344 | /* 345 | * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved. 346 | * 347 | * @APPLE_LICENSE_HEADER_START@ 348 | * 349 | * This file contains Original Code and/or Modifications of Original Code 350 | * as defined in and that are subject to the Apple Public Source License 351 | * Version 2.0 (the 'License'). You may not use this file except in 352 | * compliance with the License. Please obtain a copy of the License at 353 | * http://www.opensource.apple.com/apsl/ and read it before using this 354 | * file. 355 | * 356 | * The Original Code and all software distributed under the License are 357 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 358 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 359 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 360 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 361 | * Please see the License for the specific language governing rights and 362 | * limitations under the License. 363 | * 364 | * @APPLE_LICENSE_HEADER_END@ 365 | */ 366 | static int RN_SecRandomCopyBytes(void *rnd, size_t count, uint8_t *bytes) { 367 | static int kSecRandomFD; 368 | static dispatch_once_t onceToken; 369 | dispatch_once(&onceToken, ^{ 370 | kSecRandomFD = open("/dev/random", O_RDONLY); 371 | }); 372 | 373 | if (kSecRandomFD < 0) 374 | return -1; 375 | while (count) { 376 | ssize_t bytes_read = read(kSecRandomFD, bytes, count); 377 | if (bytes_read == -1) { 378 | if (errno == EINTR) 379 | continue; 380 | return -1; 381 | } 382 | if (bytes_read == 0) { 383 | return -1; 384 | } 385 | bytes += bytes_read; 386 | count -= bytes_read; 387 | } 388 | 389 | return 0; 390 | } 391 | /* End code dervied from SecFramework.c */ 392 | 393 | + (NSData *)randomDataOfLength:(size_t)length 394 | { 395 | NSMutableData *data = [NSMutableData dataWithLength:length]; 396 | 397 | int result; 398 | if (&SecRandomCopyBytes != NULL) { 399 | result = SecRandomCopyBytes(NULL, length, data.mutableBytes); 400 | } 401 | else { 402 | #pragma clang diagnostic push 403 | #pragma clang diagnostic ignored "-Wunreachable-code" 404 | result = RN_SecRandomCopyBytes(NULL, length, data.mutableBytes); 405 | #pragma clang diagnostic pop 406 | } 407 | NSAssert(result == 0, @"Unable to generate random bytes: %d", errno); 408 | 409 | return data; 410 | } 411 | 412 | - (id)initWithHandler:(RNCryptorHandler)handler 413 | { 414 | NSParameterAssert(handler); 415 | self = [super init]; 416 | if (self) { 417 | NSString *responseQueueName = [@"net.robnapier.response." stringByAppendingString:NSStringFromClass([self class])]; 418 | _responseQueue = dispatch_queue_create([responseQueueName UTF8String], NULL); 419 | 420 | NSString *queueName = [@"net.robnapier." stringByAppendingString:NSStringFromClass([self class])]; 421 | _queue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL); 422 | __outData = [NSMutableData data]; 423 | 424 | _handler = [handler copy]; 425 | } 426 | return self; 427 | } 428 | 429 | - (void)dealloc 430 | { 431 | if (_responseQueue) { 432 | #if !OS_OBJECT_USE_OBJC 433 | dispatch_release(_responseQueue); 434 | #endif 435 | _responseQueue = NULL; 436 | } 437 | 438 | if (_queue) { 439 | #if !OS_OBJECT_USE_OBJC 440 | dispatch_release(_queue); 441 | #endif 442 | _queue = NULL; 443 | } 444 | } 445 | 446 | - (void)setResponseQueue:(dispatch_queue_t)aResponseQueue 447 | { 448 | if (aResponseQueue) { 449 | #if !OS_OBJECT_USE_OBJC 450 | dispatch_retain(aResponseQueue); 451 | #endif 452 | } 453 | 454 | if (_responseQueue) { 455 | #if !OS_OBJECT_USE_OBJC 456 | dispatch_release(_responseQueue); 457 | #endif 458 | } 459 | 460 | _responseQueue = aResponseQueue; 461 | } 462 | 463 | - (void)addData:(NSData *)data 464 | { 465 | 466 | } 467 | 468 | - (void)finish 469 | { 470 | 471 | } 472 | 473 | - (void)cleanupAndNotifyWithError:(NSError *)error 474 | { 475 | self.error = error; 476 | self.finished = YES; 477 | if (self.handler) { 478 | dispatch_sync(self.responseQueue, ^{ 479 | self.handler(self, self.outData); 480 | }); 481 | self.handler = nil; 482 | } 483 | } 484 | 485 | - (BOOL)hasHMAC 486 | { 487 | return self.HMACLength > 0; 488 | } 489 | 490 | 491 | @end 492 | -------------------------------------------------------------------------------- /RNCryptorTests/RNCryptorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNCryptTests.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 28 | 29 | #import "RNEncryptor.h" 30 | #import "RNDecryptor.h" 31 | 32 | NSString *const kGoodPassword = @"Passw0rd!"; 33 | NSString *const kBadPassword = @"NotThePassword"; 34 | 35 | @interface RNCryptorTests : XCTestCase 36 | @property (nonatomic, readwrite, assign) BOOL isTestRunning; 37 | @property (nonatomic, readwrite, strong) RNEncryptor *encryptor; 38 | @end 39 | 40 | @interface NSData (RNCryptor_ConsistentCompare) 41 | - (BOOL)rnc_isEqualInConsistentTime:(NSData *)otherData; 42 | @end 43 | 44 | @implementation RNCryptorTests 45 | 46 | - (void)setUp 47 | { 48 | [super setUp]; 49 | 50 | // Set-up code here. 51 | } 52 | 53 | - (void)tearDown 54 | { 55 | // Tear-down code here. 56 | 57 | [super tearDown]; 58 | } 59 | 60 | - (void) testAsyncDecrypt { 61 | size_t dataLength = 29808; 62 | 63 | NSData * plaintext = [RNCryptor randomDataOfLength:dataLength]; 64 | 65 | NSError *error = nil; 66 | NSData *encryptedData = [RNEncryptor encryptData:plaintext 67 | withSettings:kRNCryptorAES256Settings 68 | password:kGoodPassword 69 | error:&error]; 70 | 71 | XCTAssertNil(error, @"Encryption error:%@", error); 72 | XCTAssertNotNil(encryptedData, @"Data did not encrypt."); 73 | 74 | NSInputStream *inputStream = [NSInputStream inputStreamWithData:encryptedData]; 75 | [inputStream open]; 76 | 77 | __block NSOutputStream *outputStream = [[NSOutputStream alloc] initToMemory]; 78 | __block NSError *decryptionError = nil; 79 | [outputStream open]; 80 | 81 | __block dispatch_semaphore_t sem = dispatch_semaphore_create(0); 82 | 83 | int blockSize = 1024; 84 | 85 | __block RNDecryptor *decryptor; 86 | __block NSMutableData *buffer = [NSMutableData dataWithLength:blockSize]; 87 | 88 | dispatch_block_t readStreamBlock = ^{ 89 | @autoreleasepool { 90 | [buffer setLength:blockSize]; 91 | NSInteger bytesRead = [inputStream read:[buffer mutableBytes] maxLength:blockSize]; 92 | if (bytesRead < 0) { 93 | XCTFail(@"Error reading block:%@", inputStream.streamError); 94 | [inputStream close]; 95 | dispatch_semaphore_signal(sem); 96 | } 97 | else if (bytesRead == 0) { 98 | [inputStream close]; 99 | [decryptor finish]; 100 | } 101 | else { 102 | [buffer setLength:bytesRead]; 103 | [decryptor addData:buffer]; 104 | } 105 | } 106 | }; 107 | 108 | decryptor = [[RNDecryptor alloc] initWithPassword:kGoodPassword handler:^(RNCryptor *cryptor, NSData *data) { 109 | [outputStream write:data.bytes maxLength:data.length]; 110 | if (cryptor.isFinished) { 111 | [outputStream close]; 112 | dispatch_semaphore_signal(sem); 113 | } 114 | else { 115 | readStreamBlock(); 116 | } 117 | }]; 118 | 119 | readStreamBlock(); 120 | 121 | long timedout = dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)); 122 | 123 | XCTAssertFalse(timedout, @"Test timed out."); 124 | XCTAssertNil(decryptionError, @"Decrypt error: %@", decryptionError); 125 | 126 | //Retrieve the decrypted data 127 | NSData *decryptedData = [outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; 128 | XCTAssertTrue([decryptedData length] > 0, @"Failed to decrypt."); 129 | 130 | XCTAssertEqualObjects(decryptedData, plaintext, @"Incorrect decryption."); 131 | 132 | } 133 | 134 | - (void)testSimple 135 | { 136 | NSData *data = [RNCryptor randomDataOfLength:1024]; 137 | NSError *error = nil; 138 | 139 | NSData *encryptedData = [RNEncryptor encryptData:data 140 | withSettings:kRNCryptorAES256Settings 141 | password:kGoodPassword 142 | error:&error]; 143 | 144 | XCTAssertNil(error, @"Encryption error:%@", error); 145 | XCTAssertNotNil(encryptedData, @"Data did not encrypt."); 146 | 147 | NSError *decryptionError = nil; 148 | NSData *decryptedData = [RNDecryptor decryptData:encryptedData withPassword:kGoodPassword error:&decryptionError]; 149 | XCTAssertNil(decryptionError, @"Error decrypting:%@", decryptionError); 150 | XCTAssertEqualObjects(decryptedData, data, @"Incorrect decryption."); 151 | } 152 | 153 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 154 | { 155 | [self.encryptor addData:data]; 156 | } 157 | 158 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 159 | { 160 | self.isTestRunning = NO; 161 | } 162 | 163 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection 164 | { 165 | [self.encryptor finish]; 166 | } 167 | 168 | - (void)testAsync 169 | { 170 | NSURL *testURL = [NSURL URLWithString:@"http://robnapier.net/favicon.ico"]; 171 | NSError *downloadError = nil; 172 | NSData *plaintext = [NSData dataWithContentsOfURL:testURL options:0 error:&downloadError]; 173 | XCTAssertNotNil(plaintext, @"Couldn't download: %@", downloadError); 174 | 175 | NSURLRequest *request = [NSURLRequest requestWithURL:testURL]; 176 | NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 177 | [connection start]; 178 | 179 | self.isTestRunning = YES; 180 | __block NSMutableData *encryptedData = [NSMutableData data]; 181 | __block NSError *encryptionError = nil; 182 | self.encryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings 183 | password:kGoodPassword 184 | handler:^(RNCryptor *cryptor, NSData *data) { 185 | [encryptedData appendData:data]; 186 | if (cryptor.isFinished) { 187 | encryptionError = cryptor.error; 188 | self.isTestRunning = NO; 189 | } 190 | }]; 191 | 192 | NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:10]; 193 | do { 194 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 195 | beforeDate:timeout]; 196 | } while (self.isTestRunning); 197 | 198 | XCTAssertFalse(self.isTestRunning, @"Test timed out."); 199 | XCTAssertNil(encryptionError, @"Encrypt error: %@", encryptionError); 200 | XCTAssertTrue([encryptedData length] > 0, @"Failed to encrypt."); 201 | 202 | NSError *decryptionError = nil; 203 | NSData *decryptedData = [RNDecryptor decryptData:encryptedData withPassword:kGoodPassword error:&decryptionError]; 204 | XCTAssertNil(decryptionError, @"decryption error:%@", decryptionError); 205 | XCTAssertEqualObjects(plaintext, decryptedData, @"Bad decryption"); 206 | } 207 | 208 | - (void)testSimpleFail 209 | { 210 | NSData *data = [RNCryptor randomDataOfLength:1024]; 211 | NSError *error = nil; 212 | 213 | NSData *encryptedData = [RNEncryptor encryptData:data 214 | withSettings:kRNCryptorAES256Settings 215 | password:kGoodPassword 216 | error:&error]; 217 | 218 | XCTAssertNil(error, @"Encryption error:%@", error); 219 | XCTAssertNotNil(encryptedData, @"Data did not encrypt."); 220 | 221 | NSError *decryptionError = nil; 222 | NSData *decryptedData = [RNDecryptor decryptData:encryptedData withPassword:kBadPassword error:&decryptionError]; 223 | XCTAssertNotNil(decryptionError, @"Should have received error decrypting:%@", decryptionError); 224 | XCTAssertNil(decryptedData, @"Decryption should be nil: %@", decryptedData); 225 | } 226 | 227 | - (void)testCorruption 228 | { 229 | NSData *data = [RNCryptor randomDataOfLength:1024]; 230 | NSError *error = nil; 231 | 232 | NSData *encryptedData = [RNEncryptor encryptData:data 233 | withSettings:kRNCryptorAES256Settings 234 | password:kGoodPassword 235 | error:&error]; 236 | 237 | XCTAssertNil(error, @"Encryption error:%@", error); 238 | XCTAssertNotNil(encryptedData, @"Data did not encrypt."); 239 | 240 | NSMutableData *corruptData = [encryptedData mutableCopy]; 241 | [corruptData replaceBytesInRange:NSMakeRange(100, 100) withBytes:[[RNCryptor randomDataOfLength:100] bytes]]; 242 | 243 | NSError *decryptionError = nil; 244 | NSData *decryptedData = [RNDecryptor decryptData:corruptData withPassword:kGoodPassword error:&decryptionError]; 245 | XCTAssertNil(decryptedData, @"Decryption should be nil: %@", decryptedData); 246 | XCTAssertEqual([decryptionError code], (NSInteger)kRNCryptorHMACMismatch, @"Should have received kRNCryptorHMACMismatch"); 247 | } 248 | 249 | - (void)testBadHeader 250 | { 251 | NSData *data = [@"Data" dataUsingEncoding:NSUTF8StringEncoding]; 252 | NSError *error = nil; 253 | NSMutableData *encrypted = [[RNEncryptor encryptData:data withSettings:kRNCryptorAES256Settings password:kGoodPassword error:&error] mutableCopy]; 254 | 255 | uint8_t firstByte = 99; 256 | [encrypted replaceBytesInRange:NSMakeRange(0, 1) withBytes:&firstByte]; 257 | 258 | NSData *decrypted = [RNDecryptor decryptData:encrypted withPassword:kGoodPassword error:&error]; 259 | XCTAssertNil(decrypted, @"Decrypt should have failed"); 260 | XCTAssertEqual([error code], (NSInteger)kRNCryptorUnknownHeader, @"Wrong error code:%ld", (long)[error code]); 261 | } 262 | 263 | - (void)testActuallyEncrypting 264 | { 265 | NSData *data = [@"Data" dataUsingEncoding:NSUTF8StringEncoding]; 266 | NSError *error = nil; 267 | NSData *encrypted = [RNEncryptor encryptData:data withSettings:kRNCryptorAES256Settings password:kGoodPassword error:&error]; 268 | 269 | NSRange found = [encrypted rangeOfData:data options:0 range:NSMakeRange(0, encrypted.length)]; 270 | XCTAssertEqual(found.location, (NSUInteger)NSNotFound, @"Data is not encrypted"); 271 | } 272 | 273 | - (void)testBackground 274 | { 275 | NSData *data = [RNCryptor randomDataOfLength:1024]; 276 | 277 | __block NSError *error = nil; 278 | __block NSData *encryptedData = nil; 279 | 280 | dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 281 | encryptedData = [RNEncryptor encryptData:data 282 | withSettings:kRNCryptorAES256Settings 283 | password:kGoodPassword 284 | error:&error]; 285 | }); 286 | 287 | XCTAssertNil(error, @"Encryption error:%@", error); 288 | XCTAssertNotNil(encryptedData, @"Data did not encrypt."); 289 | 290 | NSError *decryptionError = nil; 291 | NSData *decryptedData = [RNDecryptor decryptData:encryptedData withPassword:kGoodPassword error:&decryptionError]; 292 | XCTAssertNil(decryptionError, @"Error decrypting:%@", decryptionError); 293 | XCTAssertEqualObjects(decryptedData, data, @"Incorrect decryption."); 294 | } 295 | 296 | - (void)testKey 297 | { 298 | NSData *data = [RNCryptor randomDataOfLength:1024]; 299 | NSData *encryptionKey = [RNCryptor randomDataOfLength:kRNCryptorAES256Settings.keySettings.keySize]; 300 | NSData *HMACKey = [RNCryptor randomDataOfLength:kRNCryptorAES256Settings.HMACKeySettings.keySize]; 301 | NSError *error = nil; 302 | 303 | NSData *encryptedData = [RNEncryptor encryptData:data 304 | withSettings:kRNCryptorAES256Settings 305 | encryptionKey:encryptionKey 306 | HMACKey:HMACKey 307 | error:&error]; 308 | 309 | XCTAssertNil(error, @"Encryption error:%@", error); 310 | XCTAssertNotNil(encryptedData, @"Data did not encrypt."); 311 | 312 | NSError *decryptionError = nil; 313 | NSData *decryptedData = [RNDecryptor decryptData:encryptedData withEncryptionKey:encryptionKey HMACKey:HMACKey error:&decryptionError]; 314 | XCTAssertNil(decryptionError, @"Error decrypting:%@", decryptionError); 315 | XCTAssertEqualObjects(decryptedData, data, @"Incorrect decryption."); 316 | } 317 | 318 | // Issue #77: KeyForPassword() broken for multi-byte passwords (UTF-8) 319 | - (void)testMultibytePasswordTruncation { 320 | NSData *data = [RNCryptor randomDataOfLength:1024]; 321 | NSString *password = @"中文密码"; // 4 characters, 8 bytes => uses first 2 characters 322 | NSString *truncatedPassword = @"中文xx"; // 4 characters, 6 bytes => uses first 2 characters 323 | 324 | NSError *error; 325 | NSData *encryptedData = [RNEncryptor encryptData:data withSettings:kRNCryptorAES256Settings password:password error:&error]; 326 | 327 | XCTAssertNil(error, @"Encryption error:%@", error); 328 | XCTAssertNotNil(encryptedData, @"Data did not encrypt."); 329 | 330 | NSError *decryptionError = nil; 331 | NSData *decryptedData = [RNDecryptor decryptData:encryptedData withPassword:truncatedPassword error:&decryptionError]; 332 | XCTAssertNil(decryptedData, @"Decryption should be nil: %@", decryptedData); 333 | XCTAssertEqual([decryptionError code], (NSInteger)kRNCryptorHMACMismatch, @"Should have received kRNCryptorHMACMismatch"); 334 | } 335 | 336 | // Issue #77: KeyForPassword() broken for multi-byte passwords (UTF-8) 337 | - (void)testReadVersion2PasswordTruncation { 338 | NSString *plaintext = @"Attack at dawn"; 339 | NSString *password = @"中文密码"; 340 | NSData *v2EncryptionWithPasswordTrunction = 341 | [[NSData alloc] initWithBase64EncodedString:@"AgHOKe1rygDPDDk5DBInKERD85Ezo1EAU5uj+PyEz22o2dtFAjPyaJhY3jW0BXD4W9L4GnAhiscr8PKZ+zOzWDbTFB3/6alv2LWQ1TCG4cpT0g==" options:0]; 342 | 343 | NSError *decryptionError = nil; 344 | NSData *decryptedData = [RNDecryptor decryptData:v2EncryptionWithPasswordTrunction withPassword:password error:&decryptionError]; 345 | XCTAssertNil(decryptionError, @"Error decrypting:%@", decryptionError); 346 | XCTAssertEqualObjects(decryptedData, [plaintext dataUsingEncoding:NSUTF8StringEncoding], @"Incorrect decryption."); 347 | } 348 | 349 | // Issue #24: Crash on 0-length input 350 | - (void)testZeroLengthInput { 351 | NSData *data = [NSData data]; 352 | 353 | NSError *encryptionError; 354 | NSData *encryptedData = [RNEncryptor encryptData:data withSettings:kRNCryptorAES256Settings password:kGoodPassword error:&encryptionError]; 355 | 356 | XCTAssertNil(encryptionError, @"Error while encrypting: %@", encryptionError); 357 | XCTAssertNotNil(encryptedData, @"Failed to encrypt: %@", encryptionError); 358 | 359 | NSError *decryptionError; 360 | NSData *decryptedData = [RNDecryptor decryptData:encryptedData withPassword:kGoodPassword error:&decryptionError]; 361 | 362 | XCTAssertNil(decryptionError, @"Error while decrypting: %@", decryptionError); 363 | XCTAssertEqualObjects(decryptedData, data, @"Failed to decrypt."); 364 | } 365 | 366 | - (void)testEqualInConsistentTime 367 | { 368 | NSData *data = [RNCryptor randomDataOfLength:101]; 369 | NSData *data2 = [RNCryptor randomDataOfLength:102]; 370 | 371 | XCTAssertFalse([data rnc_isEqualInConsistentTime:nil]); 372 | XCTAssertFalse([data rnc_isEqualInConsistentTime:[NSData data]]); 373 | XCTAssertFalse([data rnc_isEqualInConsistentTime:data2]); 374 | XCTAssertTrue([data rnc_isEqualInConsistentTime:data]); 375 | 376 | /* There was a bug where two datas whose length differed by a multiple of 256 could return YES */ 377 | NSData *data256 = [RNCryptor randomDataOfLength:256]; 378 | NSMutableData *data512 = [data256 mutableCopy]; 379 | [data512 appendData:data256]; 380 | XCTAssertFalse([data256 rnc_isEqualInConsistentTime:data512]); 381 | } 382 | 383 | // 384 | //- (void)_testDataOfLength:(NSUInteger)length encryptPassword:(NSString *)encryptPassword decryptPassword:(NSString *)decryptPassword 385 | //{ 386 | // RNCryptor *cryptor = [RNCryptor AES256Cryptor]; 387 | // 388 | // NSData *data = [[cryptor class] randomDataOfLength:length]; 389 | // 390 | // NSError *error; 391 | // 392 | // NSData *encryptedData = [cryptor encryptData:data password:encryptPassword error:&error]; 393 | // NSData *decryptedData = [cryptor decryptData:encryptedData password:decryptPassword error:&error]; 394 | // 395 | // if ([encryptPassword isEqualToString:decryptPassword]) { 396 | // STAssertTrue([data isEqualToData:decryptedData], @"Decrypted data does not match for length:%d", length); // Don't use STAssertEqualObjects(). Some data is quite large. 397 | // } 398 | // else { 399 | // STAssertFalse([data isEqualToData:decryptedData], @"Decrypt should have failed for length:%d", length); // Don't use STAssertEqualObjects(). Some data is quite large. 400 | // } 401 | //} 402 | // 403 | //- (void)_testDataOfLength:(NSUInteger)length 404 | //{ 405 | // [self _testDataOfLength:length encryptPassword:kGoodPassword decryptPassword:kBadPassword]; 406 | //} 407 | // 408 | //- (void)testData 409 | //{ 410 | // [self _testDataOfLength:1024]; 411 | //} 412 | 413 | // 414 | 415 | // 416 | //- (void)_testURLWithLength:(NSUInteger)length encryptPassword:(NSString *)encryptPassword decryptPassword:(NSString *)decryptPassword 417 | //{ 418 | // RNCryptor *cryptor = [RNCryptor AES256Cryptor]; 419 | // 420 | // NSData *data = [[cryptor class] randomDataOfLength:length]; 421 | // NSError *error; 422 | // 423 | // NSURL *plaintextURL = [NSURL fileURLWithPath:[self temporaryFilePath]]; 424 | // NSURL *ciphertextURL = [NSURL fileURLWithPath:[self temporaryFilePath]]; 425 | // NSURL *decryptedURL = [NSURL fileURLWithPath:[self temporaryFilePath]]; 426 | // 427 | // NSAssert([data writeToURL:plaintextURL options:0 error:&error], @"Couldn't write file:%@", error); 428 | // 429 | // STAssertTrue([cryptor encryptFromURL:plaintextURL toURL:ciphertextURL append:NO password:encryptPassword error:&error], @"Failed to encrypt:%@", error); 430 | // 431 | // BOOL result = [cryptor decryptFromURL:ciphertextURL toURL:decryptedURL append:NO password:decryptPassword error:&error]; 432 | // if ([encryptPassword isEqualToString:decryptPassword]) { 433 | // STAssertTrue(result, @"Failed to decrypt:%@", error); 434 | // NSData *decryptedData = [NSData dataWithContentsOfURL:decryptedURL]; 435 | // STAssertEqualObjects(data, decryptedData, @"Data doesn't match"); 436 | // 437 | // } 438 | // else { 439 | // STAssertFalse(result, @"Should have failed"); 440 | // } 441 | // 442 | // [[NSFileManager defaultManager] removeItemAtURL:plaintextURL error:&error]; 443 | // [[NSFileManager defaultManager] removeItemAtURL:ciphertextURL error:&error]; 444 | // [[NSFileManager defaultManager] removeItemAtURL:decryptedURL error:&error]; 445 | //} 446 | // 447 | //- (void)_testURLWithLength:(NSUInteger)length 448 | //{ 449 | // return [self _testURLWithLength:length encryptPassword:kGoodPassword decryptPassword:kGoodPassword]; 450 | //} 451 | // 452 | // 453 | //- (void)testURL 454 | //{ 455 | // [self _testURLWithLength:1024]; 456 | // 457 | //} 458 | // 459 | //- (void)testBigData 460 | //{ 461 | // [self _testDataOfLength:1024 * 1024]; 462 | //} 463 | // 464 | //- (void)testOddSizeData 465 | //{ 466 | // [self _testDataOfLength:1023]; 467 | // [self _testDataOfLength:1025]; 468 | //} 469 | // 470 | //- (void)testActuallyEncrypting 471 | //{ 472 | // NSData *data = [@"Data" dataUsingEncoding:NSUTF8StringEncoding]; 473 | // NSError *error; 474 | // NSData *encrypted = [[RNCryptor AES256Cryptor] encryptData:data password:kGoodPassword error:&error]; 475 | // 476 | // NSRange found = [encrypted rangeOfData:data options:0 range:NSMakeRange(0, encrypted.length)]; 477 | // STAssertEquals(found.location, (NSUInteger)NSNotFound, @"Data is not encrypted"); 478 | //} 479 | // 480 | //- (void)testSmall 481 | //{ 482 | // for (NSUInteger i = 1; i < 32; i++) { 483 | // [self _testDataOfLength:i]; 484 | // } 485 | //} 486 | // 487 | //- (void)testNearReadBlocksize 488 | //{ 489 | // for (NSUInteger i = 1024 - 10; i < 1024 + 10; i++) { 490 | // [self _testDataOfLength:i]; 491 | // } 492 | //} 493 | // 494 | //- (void)testNearDoubleReadBlocksize 495 | //{ 496 | // for (NSUInteger i = 2048 - 10; i < 2048 + 10; i++) { 497 | // [self _testDataOfLength:i]; 498 | // } 499 | //} 500 | // 501 | //- (void)testSmallBadPassword 502 | //{ 503 | // for (NSUInteger i = 1; i < 32; i++) { 504 | // [self _testDataOfLength:i encryptPassword:kGoodPassword decryptPassword:kBadPassword]; 505 | // } 506 | //} 507 | // 508 | //- (void)testNearReadBlocksizeBadPassword 509 | //{ 510 | // for (NSUInteger i = 1024 - 32; i < 1024 + 32; i++) { 511 | // [self _testDataOfLength:i encryptPassword:kGoodPassword decryptPassword:kBadPassword]; 512 | // } 513 | //} 514 | // 515 | //- (void)testNearDoubleReadBlocksizeBadPassword 516 | //{ 517 | // for (NSUInteger i = 2048 - 32; i < 2048 + 32; i++) { 518 | // [self _testDataOfLength:i encryptPassword:kGoodPassword decryptPassword:kBadPassword]; 519 | // } 520 | //} 521 | // 522 | //- (void)testNearTripleReadBlocksizeBadPassword 523 | //{ 524 | // for (NSUInteger i = 3072 - 32; i <= 3072 + 32; i++) { 525 | // [self _testDataOfLength:i encryptPassword:kGoodPassword decryptPassword:kBadPassword]; 526 | // } 527 | //} 528 | // 529 | //- (void)testURLBadPassword 530 | //{ 531 | // [self _testURLWithLength:1024 encryptPassword:kGoodPassword decryptPassword:kBadPassword]; 532 | //} 533 | // 534 | //- (void)testURLSmallBadPassword 535 | //{ 536 | // for (NSUInteger i = 1; i < 32; i++) { 537 | // [self _testURLWithLength:i encryptPassword:kGoodPassword decryptPassword:kBadPassword]; 538 | // } 539 | //} 540 | // 541 | //- (void)testURLNearReadBlocksize 542 | //{ 543 | // for (NSUInteger i = 1024 - 32; i < 1024 + 32; i++) { 544 | // [self _testURLWithLength:i]; 545 | // } 546 | //} 547 | // 548 | //- (void)testURLNearReadBlocksizeBadPassword 549 | //{ 550 | // for (NSUInteger i = 1024 - 32; i < 1024 + 32; i++) { 551 | // [self _testURLWithLength:i encryptPassword:kGoodPassword decryptPassword:kBadPassword]; 552 | // } 553 | //} 554 | // 555 | // 556 | 557 | // 558 | //- (void)testURLNegativeInput 559 | //{ 560 | // RNCryptor *cryptor = [RNCryptor AES256Cryptor]; 561 | // 562 | // NSError *error; 563 | // 564 | // NSURL *plaintextURL = [NSURL fileURLWithPath:@"DoesNotExist"]; 565 | // NSURL *ciphertextURL = [NSURL fileURLWithPath:[self temporaryFilePath]]; 566 | // NSURL *decryptedURL = [NSURL fileURLWithPath:[self temporaryFilePath]]; 567 | // 568 | // // Don't write the data 569 | // 570 | // STAssertFalse([cryptor encryptFromURL:plaintextURL toURL:ciphertextURL append:NO password:kGoodPassword error:&error], @"Should have failed."); 571 | // 572 | // [[NSFileManager defaultManager] removeItemAtURL:plaintextURL error:&error]; 573 | // [[NSFileManager defaultManager] removeItemAtURL:ciphertextURL error:&error]; 574 | // [[NSFileManager defaultManager] removeItemAtURL:decryptedURL error:&error]; 575 | //} 576 | 577 | @end 578 | --------------------------------------------------------------------------------