aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Source/SPDataAdditions.h1
-rw-r--r--Source/SPDataAdditions.m58
-rw-r--r--UnitTests/SPDataAdditionsTests.m95
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj27
4 files changed, 148 insertions, 33 deletions
diff --git a/Source/SPDataAdditions.h b/Source/SPDataAdditions.h
index 6c404b9e..5158c270 100644
--- a/Source/SPDataAdditions.h
+++ b/Source/SPDataAdditions.h
@@ -35,6 +35,7 @@
- (NSData *)dataEncryptedWithPassword:(NSString *)password;
- (NSData *)dataEncryptedWithKey:(NSData *)aesKey IV:(NSData *)iv;
- (NSData *)dataDecryptedWithPassword:(NSString *)password;
+- (NSData *)dataDecryptedWithKey:(NSData *)key;
- (NSData *)compress;
- (NSData *)decompress;
diff --git a/Source/SPDataAdditions.m b/Source/SPDataAdditions.m
index 4a07c437..8002595c 100644
--- a/Source/SPDataAdditions.m
+++ b/Source/SPDataAdditions.m
@@ -35,7 +35,6 @@
#import "SPDataAdditions.h"
#include <zlib.h>
-#include <openssl/aes.h>
#include <CommonCrypto/CommonCrypto.h>
#include <stdlib.h>
@@ -172,23 +171,50 @@ uint32_t LimitUInt32(NSUInteger i);
- (NSData *)dataDecryptedWithPassword:(NSString *)password
{
// Create the key from the password hash
- NSData *passwordDigest = [[password dataUsingEncoding:NSUTF8StringEncoding] sha1Hash];
+ NSData *passwordDigest = [[[password dataUsingEncoding:NSUTF8StringEncoding] sha1Hash] subdataWithRange:NSMakeRange(0, kCCKeySizeAES128)];
+
+ return [self dataDecryptedWithKey:passwordDigest];
- // AES-128-cbc decrypt the data
- AES_KEY aesKey;
- AES_set_decrypt_key([passwordDigest bytes], 128, &aesKey);
+}
+
+- (NSData *)dataDecryptedWithKey:(NSData *)aesKey
+{
+ if([aesKey length] != kCCKeySizeAES128)
+ @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Key length invalid. Must be kCCKeySizeAES128 bytes!" userInfo:nil];
+
+ if([self length] < (2*kCCBlockSizeAES128) || [self length] > UINT32_MAX)
+ @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Length of encrypted NSData must be in range 32 to 2^32!" userInfo:nil];
// Total length = encrypted length + IV
- NSInteger totalLength = [self length];
- NSInteger encryptedLength = totalLength - 16;
+ NSUInteger totalLength = [self length];
+ NSUInteger encryptedLength = totalLength - kCCBlockSizeAES128; // >=0 ensured above
// Take the IV from the first 128-bit block
- unsigned char iv[16];
- memcpy(iv, [self bytes], 16);
+ unsigned char iv[kCCBlockSizeAES128];
+ memcpy(iv, [self bytes], kCCBlockSizeAES128);
// Decrypt the data
- unsigned char *decryptedBytes = (unsigned char*)malloc(encryptedLength);
- AES_cbc_encrypt([self bytes] + 16, decryptedBytes, encryptedLength, &aesKey, iv, AES_DECRYPT);
+ unsigned char *decryptedBytes = calloc(1,encryptedLength);
+
+ CCCryptorStatus res = CCCrypt(
+ kCCDecrypt, // operation mode
+ kCCAlgorithmAES128, // algorithm
+ 0, // options. We use our own padding algorithm and CBC is the default
+ [aesKey bytes], // key bytes
+ kCCKeySizeAES128, // key length
+ iv, // iv bytes (length == block size)
+ ([self bytes] + kCCBlockSizeAES128), // raw data
+ encryptedLength, // length of raw data
+ decryptedBytes, // output buffer. overwriting input is OK
+ encryptedLength, // output buffer size
+ NULL // number of bytes written. not relevant here
+ );
+
+ if(res != kCCSuccess) {
+ @throw [NSException exceptionWithName:SPCommonCryptoExceptionName
+ reason:[NSString stringWithFormat:@"CCCrypt() failed! (CCCryptorStatus=%d)",res]
+ userInfo:@{@"cryptorStatus":@(res)}];
+ }
// If decryption was successful, these blocks will be zeroed
if ( *((UInt32*)decryptedBytes + ((encryptedLength / 4) - 4)) ||
@@ -200,8 +226,14 @@ uint32_t LimitUInt32(NSUInteger i);
}
// Get the size of the data from the last 32-bit chunk
- NSInteger bigIntDataLength = *((UInt32*)decryptedBytes + ((encryptedLength / 4) - 1));
- NSInteger dataLength = NSSwapBigIntToHost((unsigned int)bigIntDataLength);
+ uint32_t bigIntDataLength = *((UInt32*)decryptedBytes + ((encryptedLength / sizeof(UInt32)) - 1));
+ uint32_t dataLength = NSSwapBigIntToHost(bigIntDataLength);
+
+ if(dataLength >= (encryptedLength-sizeof(UInt32))) { //this way dataLength can still reach into padding, but we own that memory anyway.
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException
+ reason:[NSString stringWithFormat:@"dataLength=%u exceeds encryptedLength=%lu! Either the message is incomplete, decrypting resulted in invalid data, or this is a malicious message!",dataLength,encryptedLength]
+ userInfo:nil];
+ }
return [NSData dataWithBytesNoCopy:decryptedBytes length:dataLength];
}
diff --git a/UnitTests/SPDataAdditionsTests.m b/UnitTests/SPDataAdditionsTests.m
index 0ecad9e7..812eb45d 100644
--- a/UnitTests/SPDataAdditionsTests.m
+++ b/UnitTests/SPDataAdditionsTests.m
@@ -38,6 +38,8 @@
- (void)testSha1Hash;
- (void)testDataEncryptedWithPassword;
- (void)testDataEncryptedWithKeyIV;
+- (void)testDataDecryptedWithPassword;
+- (void)testDataDecryptedWithKey;
@end
@@ -186,4 +188,97 @@
}
}
+- (void)testDataDecryptedWithPassword
+{
+ //see test above
+ NSData *raw = [@" " dataUsingEncoding:NSASCIIStringEncoding];
+ unsigned char encrypted[] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41,
+ 0x42, 0x43, 0x44, 0x45, 0x46, 0xd3, 0x58, 0x30, 0x95, 0x6d, 0x7f,
+ 0xf5, 0x1e, 0x18, 0xb0, 0xbc, 0x1f, 0xb3, 0xe4, 0x52, 0xb1, 0x75,
+ 0x4c, 0xc3, 0x52, 0xd0, 0x93, 0xad, 0xff, 0x36, 0x4a, 0xae, 0xbe,
+ 0x60, 0x32, 0xdd, 0x71, 0xef, 0xce, 0x2e, 0x8b, 0x09, 0xcb, 0x9a,
+ 0x44, 0x32, 0xb3, 0xda, 0x42, 0x58, 0x29, 0x78, 0xc3
+ };
+ NSData *encData = [NSData dataWithBytesNoCopy:encrypted length:sizeof(encrypted) freeWhenDone:NO];
+
+ NSData *decrypted = [encData dataDecryptedWithPassword:@""];
+
+ STAssertEqualObjects(decrypted, raw, @"Decrypt simple data encrypted with empty password");
+}
+
+- (void)testDataDecryptedWithKey
+{
+ NSData *raw = [@" " dataUsingEncoding:NSASCIIStringEncoding];
+
+ unsigned char keyRaw[] = {0xda,0x39,0xa3,0xee,0x5e,0x6b,0x4b,0x0d,0x32,0x55,0xbf,0xef,0x95,0x60,0x18,0x90,0xaf,0xd8,0x07,0x09}; // sha1("")
+ NSData *key = [NSData dataWithBytes:keyRaw length:16];
+
+ unsigned char encrypted[] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41,
+ 0x42, 0x43, 0x44, 0x45, 0x46, 0xd3, 0x58, 0x30, 0x95, 0x6d, 0x7f,
+ 0xf5, 0x1e, 0x18, 0xb0, 0xbc, 0x1f, 0xb3, 0xe4, 0x52, 0xb1, 0x75,
+ 0x4c, 0xc3, 0x52, 0xd0, 0x93, 0xad, 0xff, 0x36, 0x4a, 0xae, 0xbe,
+ 0x60, 0x32, 0xdd, 0x71, 0xef, 0xce, 0x2e, 0x8b, 0x09, 0xcb, 0x9a,
+ 0x44, 0x32, 0xb3, 0xda, 0x42, 0x58, 0x29, 0x78, 0xc3
+ };
+ NSData *encData = [NSData dataWithBytesNoCopy:encrypted length:sizeof(encrypted) freeWhenDone:NO];
+
+ // invalid key length
+ {
+ @try {
+ [encData dataDecryptedWithKey:[NSData data]];
+ STFail(@"Invalid key length!");
+ }
+ @catch (NSException *exception) {
+ //expected
+ }
+ }
+ // data too short for encryption
+ {
+ @try {
+ [[@"Hello World!" dataUsingEncoding:NSASCIIStringEncoding] dataDecryptedWithKey:key];
+ STFail(@"Invalid data length!");
+ }
+ @catch (NSException *exception) {
+ //expected
+ }
+ }
+ // wrong data with valid length
+ {
+ NSData *inp = [@"12345678901234567890123456789012" dataUsingEncoding:NSASCIIStringEncoding];
+ STAssertNil([inp dataDecryptedWithKey:key], @"Trying to decrypt invalid data.");
+ }
+ // wrong data with invalid length
+ {
+ NSData *inp = [@"12345678901234567890123456789012345678901234567" dataUsingEncoding:NSASCIIStringEncoding];
+ STAssertNil([inp dataDecryptedWithKey:key], @"Trying to decrypt data with invalid length.");
+ }
+ // simple decryption test
+ {
+ NSData *decrypted = [encData dataDecryptedWithKey:key];
+ STAssertEqualObjects(decrypted, raw, @"Simple Decryption test");
+ }
+ // malicious message test
+ {
+ //this is an empty message with a length field set to UINT32_MAX
+ unsigned char _encrypted[] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41,
+ 0x42, 0x43, 0x44, 0x45, 0x46, 0x50, 0xc9, 0xca, 0x75, 0x14, 0xd3,
+ 0x6e, 0xec, 0x9e, 0xc6, 0x4c, 0x25, 0x02, 0x33, 0xdd, 0x86, 0x54,
+ 0xea, 0x1a, 0x0d, 0xe9, 0x88, 0xe3, 0xeb, 0xcb, 0xb7, 0x01, 0x52,
+ 0x42, 0x1c, 0xd8, 0xd5
+ };
+ NSData *_encData = [NSData dataWithBytesNoCopy:_encrypted length:sizeof(_encrypted) freeWhenDone:NO];
+
+ @try {
+ [_encData dataDecryptedWithKey:key];
+ STFail(@"Malicious message with invalid data length");
+ }
+ @catch (NSException *exception) {
+ //expected
+ }
+ }
+}
+
@end
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index c2c7e035..0958501d 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -181,7 +181,8 @@
501B1D181728A3DA0017C92E /* SPCharsetCollationHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 501B1D171728A3DA0017C92E /* SPCharsetCollationHelper.m */; };
502D21F61BA50710000D4CE7 /* SPDataAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 502D21F51BA50710000D4CE7 /* SPDataAdditionsTests.m */; };
502D21F81BA50966000D4CE7 /* SPDataAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2C16D30FEBEDF10003993B /* SPDataAdditions.m */; };
- 502D21FA1BA509AF000D4CE7 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 502D21F91BA509AF000D4CE7 /* libcrypto.dylib */; };
+ 502D22151BA62FA5000D4CE7 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5EAC0FC0EC87FF900CC579C /* Security.framework */; };
+ 502D22161BA62FF5000D4CE7 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5EAC0FC0EC87FF900CC579C /* Security.framework */; };
503B02CA1AE82C5E0060CAB1 /* SPTableFilterParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 503B02C91AE82C5E0060CAB1 /* SPTableFilterParser.m */; };
503B02CF1AE95C2C0060CAB1 /* SPTableFilterParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 503B02CE1AE95C2C0060CAB1 /* SPTableFilterParserTest.m */; };
503B02D11AE95DD40060CAB1 /* SPTableFilterParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 503B02C91AE82C5E0060CAB1 /* SPTableFilterParser.m */; };
@@ -409,7 +410,6 @@
BC09D7E312A786FB0030DB64 /* cancel.png in Resources */ = {isa = PBXBuildFile; fileRef = BC09D7DD12A786FB0030DB64 /* cancel.png */; };
BC0E1486120AAB5600E52E25 /* SPDataAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2C16D30FEBEDF10003993B /* SPDataAdditions.m */; };
BC0E1487120AAB5C00E52E25 /* SPStringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1789343B0F30C1DD0097539A /* SPStringAdditions.m */; };
- BC0E1493120AABE900E52E25 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 17B7B58F1016028F00F057DE /* libcrypto.dylib */; };
BC0E14A1120AAC2E00E52E25 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 179ECEC611F265EE009C6A40 /* libbz2.dylib */; };
BC0ED3DA12A9196C00088461 /* SPChooseMenuItemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0ED3D912A9196C00088461 /* SPChooseMenuItemDialog.m */; };
BC1847EA0FE6EC8400094BFB /* SPEditSheetTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC1847E90FE6EC8400094BFB /* SPEditSheetTextView.m */; };
@@ -758,7 +758,6 @@
17A7773711C52E61001E27B4 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Interfaces/English.lproj/IndexesView.xib; sourceTree = "<group>"; };
17AF787911FC41C00073D043 /* SPExportFilenameUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPExportFilenameUtilities.h; sourceTree = "<group>"; };
17AF787A11FC41C00073D043 /* SPExportFilenameUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPExportFilenameUtilities.m; sourceTree = "<group>"; };
- 17B7B58F1016028F00F057DE /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = /usr/lib/libcrypto.dylib; sourceTree = "<absolute>"; };
17B7B591101602AE00F057DE /* libssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.dylib; path = /usr/lib/libssl.dylib; sourceTree = "<absolute>"; };
17BA2A3015275D8600389803 /* SPExportInterfaceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPExportInterfaceController.h; sourceTree = "<group>"; };
17BA2A3115275D8600389803 /* SPExportInterfaceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPExportInterfaceController.m; sourceTree = "<group>"; };
@@ -895,7 +894,6 @@
501B1D161728A3DA0017C92E /* SPCharsetCollationHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPCharsetCollationHelper.h; sourceTree = "<group>"; };
501B1D171728A3DA0017C92E /* SPCharsetCollationHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPCharsetCollationHelper.m; sourceTree = "<group>"; };
502D21F51BA50710000D4CE7 /* SPDataAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDataAdditionsTests.m; sourceTree = "<group>"; };
- 502D21F91BA509AF000D4CE7 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
5037F79A1B00148000733564 /* SPNamedNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPNamedNode.h; sourceTree = "<group>"; };
503B02C81AE82C5E0060CAB1 /* SPTableFilterParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTableFilterParser.h; sourceTree = "<group>"; };
503B02C91AE82C5E0060CAB1 /* SPTableFilterParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableFilterParser.m; sourceTree = "<group>"; };
@@ -1277,7 +1275,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 502D21FA1BA509AF000D4CE7 /* libcrypto.dylib in Frameworks */,
+ 502D22151BA62FA5000D4CE7 /* Security.framework in Frameworks */,
1717F9DB1558114D0065C036 /* OCMock.framework in Frameworks */,
1717FA43155831600065C036 /* libicucore.dylib in Frameworks */,
50EA92671AB23EE1008D3C4F /* SPMySQL.framework in Frameworks */,
@@ -1288,8 +1286,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 502D22161BA62FF5000D4CE7 /* Security.framework in Frameworks */,
BC0E14A1120AAC2E00E52E25 /* libbz2.dylib in Frameworks */,
- BC0E1493120AABE900E52E25 /* libcrypto.dylib in Frameworks */,
584D87C415141A5D00F24774 /* libz.dylib in Frameworks */,
58475686120A065B0057631F /* CoreFoundation.framework in Frameworks */,
584756B5120A06740057631F /* CoreServices.framework in Frameworks */,
@@ -1364,7 +1362,6 @@
296DC8BE0F9091DF002A3258 /* libicucore.dylib */,
179ECEC611F265EE009C6A40 /* libbz2.dylib */,
17B7B591101602AE00F057DE /* libssl.dylib */,
- 17B7B58F1016028F00F057DE /* libcrypto.dylib */,
584D87BE15141A4A00F24774 /* libz.dylib */,
);
name = "Linked Frameworks";
@@ -2356,7 +2353,6 @@
2A37F4C3FDCFA73011CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
- 502D21F91BA509AF000D4CE7 /* libcrypto.dylib */,
1058C7A6FEA54F5311CA2CBB /* Linked Frameworks */,
);
name = Frameworks;
@@ -3937,10 +3933,7 @@
INSTALL_PATH = "$(HOME)/Applications";
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/Frameworks/zlib";
MACOSX_DEPLOYMENT_TARGET = 10.6;
- OTHER_LDFLAGS = (
- "$(SRCROOT)/Frameworks/zlib/libz.a",
- "-lcrypto",
- );
+ OTHER_LDFLAGS = "$(SRCROOT)/Frameworks/zlib/libz.a";
PRODUCT_NAME = "Sequel Pro";
SEPARATE_STRIP = NO;
};
@@ -4140,10 +4133,7 @@
INSTALL_PATH = "$(HOME)/Applications";
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/Frameworks/zlib";
MACOSX_DEPLOYMENT_TARGET = 10.6;
- OTHER_LDFLAGS = (
- "$(SRCROOT)/Frameworks/zlib/libz.a",
- "-lcrypto",
- );
+ OTHER_LDFLAGS = "$(SRCROOT)/Frameworks/zlib/libz.a";
PRODUCT_NAME = "Sequel Pro";
SEPARATE_STRIP = NO;
};
@@ -4167,10 +4157,7 @@
INSTALL_PATH = "$(HOME)/Applications";
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/Frameworks/zlib";
MACOSX_DEPLOYMENT_TARGET = 10.6;
- OTHER_LDFLAGS = (
- "$(SRCROOT)/Frameworks/zlib/libz.a",
- "-lcrypto",
- );
+ OTHER_LDFLAGS = "$(SRCROOT)/Frameworks/zlib/libz.a";
PRODUCT_NAME = "Sequel Pro";
SEPARATE_STRIP = NO;
};