From 1f7b6f61c6f271cc90fdd6710acf76713fe6ff67 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sun, 27 Aug 2017 00:44:50 -0400 Subject: [PATCH] Regression test for provisioning cipher // FREEBIE --- .../Example/TSKitiOSTestApp/Podfile.lock | 6 +- .../TSKitiOSTestApp.xcodeproj/project.pbxproj | 8 +- .../src/Devices/OWSProvisioningCipher.m | 26 +++- .../tests/Devices/OWSProvisioningCipherTest.m | 134 ++++++++++++++++++ 4 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 SignalServiceKit/tests/Devices/OWSProvisioningCipherTest.m diff --git a/SignalServiceKit/Example/TSKitiOSTestApp/Podfile.lock b/SignalServiceKit/Example/TSKitiOSTestApp/Podfile.lock index 963d596fa..31551849e 100644 --- a/SignalServiceKit/Example/TSKitiOSTestApp/Podfile.lock +++ b/SignalServiceKit/Example/TSKitiOSTestApp/Podfile.lock @@ -38,7 +38,7 @@ PODS: - Reachability (3.2) - SAMKeychain (1.5.2) - SignalServiceKit (0.9.0): - - 25519 + - '25519' - AFNetworking - AxolotlKit - CocoaLumberjack @@ -134,7 +134,7 @@ CHECKOUT OPTIONS: :git: https://github.com/facebook/SocketRocket.git SPEC CHECKSUMS: - 25519: dc4bad7e2dbcbf1efa121068a705a44cd98c80fc + '25519': dc4bad7e2dbcbf1efa121068a705a44cd98c80fc AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 AxolotlKit: a9530d6835baae0f204b1f6b9dd79b7901176f0d CocoaLumberjack: aa9dcab71bdf9eaf2a63bbd9ddc87863efe45457 @@ -154,4 +154,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 8eff8ab93f8a0a1024e17b16fee43f3a93656c2c -COCOAPODS: 1.3.1 +COCOAPODS: 1.2.1 diff --git a/SignalServiceKit/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj b/SignalServiceKit/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj index 12991eb6c..688f3648c 100644 --- a/SignalServiceKit/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj +++ b/SignalServiceKit/Example/TSKitiOSTestApp/TSKitiOSTestApp.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 34D99C891F2250FF00D284D6 /* OWSAnalyticsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D99C881F2250FF00D284D6 /* OWSAnalyticsTests.m */; }; 45046FE01D95A6130015EFF2 /* TSMessagesManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */; }; 450E3C9A1D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */; }; + 451686AE1F527A9C00AC3D4B /* OWSProvisioningCipherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 451686AD1F527A9C00AC3D4B /* OWSProvisioningCipherTest.m */; }; 4516E3E81DD153CC00DC4206 /* TSGroupThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4516E3E71DD153CC00DC4206 /* TSGroupThreadTest.m */; }; 4516E3EA1DD1542300DC4206 /* TSContactThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4516E3E91DD1542300DC4206 /* TSContactThreadTest.m */; }; 452137231E8D6D2F0048FD10 /* OWSFakeMessageSender.m in Sources */ = {isa = PBXBuildFile; fileRef = 452137221E8D6D2F0048FD10 /* OWSFakeMessageSender.m */; }; @@ -70,6 +71,7 @@ 36DA6C703F99948D553F4E3F /* Pods-TSKitiOSTestAppTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSKitiOSTestAppTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-TSKitiOSTestAppTests/Pods-TSKitiOSTestAppTests.debug.xcconfig"; sourceTree = ""; }; 45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessagesManagerTest.m; path = ../../../tests/Messages/TSMessagesManagerTest.m; sourceTree = ""; }; 450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessagesJobTest.m; path = ../../../tests/Messages/OWSDisappearingMessagesJobTest.m; sourceTree = ""; }; + 451686AD1F527A9C00AC3D4B /* OWSProvisioningCipherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSProvisioningCipherTest.m; path = ../../../tests/Devices/OWSProvisioningCipherTest.m; sourceTree = ""; }; 4516E3E71DD153CC00DC4206 /* TSGroupThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSGroupThreadTest.m; path = ../../../tests/Contacts/TSGroupThreadTest.m; sourceTree = ""; }; 4516E3E91DD1542300DC4206 /* TSContactThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSContactThreadTest.m; path = ../../../tests/Contacts/TSContactThreadTest.m; sourceTree = ""; }; 452137211E8D6D2F0048FD10 /* OWSFakeMessageSender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFakeMessageSender.h; path = ../../../tests/TestSupport/Fakes/OWSFakeMessageSender.h; sourceTree = ""; }; @@ -263,6 +265,7 @@ children = ( 45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */, 45B840201D988DA100F9E938 /* OWSReadReceiptTest.m */, + 451686AD1F527A9C00AC3D4B /* OWSProvisioningCipherTest.m */, ); name = Devices; sourceTree = ""; @@ -468,7 +471,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 276B029791E679B0E87877B7 /* [CP] Copy Pods Resources */ = { @@ -551,7 +554,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -590,6 +593,7 @@ 45AE48491E072711004D96C2 /* OWSUnitTestEnvironment.m in Sources */, 459850C11D22C6F2006FFEDB /* PhoneNumberTest.m in Sources */, 45DC30C71F3B69B7008C4378 /* OWSFakeProfileManager.m in Sources */, + 451686AE1F527A9C00AC3D4B /* OWSProvisioningCipherTest.m in Sources */, 45458B7A1CC342B600A02153 /* TSStorageSignedPreKeyStore.m in Sources */, 453E1FDB1DA83EFB00DDD7B7 /* OWSFakeContactsUpdater.m in Sources */, 452137231E8D6D2F0048FD10 /* OWSFakeMessageSender.m in Sources */, diff --git a/SignalServiceKit/src/Devices/OWSProvisioningCipher.m b/SignalServiceKit/src/Devices/OWSProvisioningCipher.m index 711e1188d..0ce4a7a63 100644 --- a/SignalServiceKit/src/Devices/OWSProvisioningCipher.m +++ b/SignalServiceKit/src/Devices/OWSProvisioningCipher.m @@ -1,4 +1,6 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSProvisioningCipher.h" #import <25519/Curve25519.h> @@ -11,21 +13,33 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSData *theirPublicKey; @property (nonatomic, readonly) ECKeyPair *ourKeyPair; +@property (nonatomic, readonly) NSData *initializationVector; @end @implementation OWSProvisioningCipher - (instancetype)initWithTheirPublicKey:(NSData *)theirPublicKey +{ + return [self initWithTheirPublicKey:theirPublicKey + ourKeyPair:[Curve25519 generateKeyPair] + initializationVector:[Cryptography generateRandomBytes:kCCBlockSizeAES128]]; +} + + +- (instancetype)initWithTheirPublicKey:(NSData *)theirPublicKey + ourKeyPair:(ECKeyPair *)ourKeyPair + initializationVector:(NSData *)initializationVector { self = [super init]; if (!self) { return self; } - + _theirPublicKey = theirPublicKey; - _ourKeyPair = [Curve25519 generateKeyPair]; - + _ourKeyPair = ourKeyPair; + _initializationVector = initializationVector; + return self; } @@ -61,7 +75,9 @@ NS_ASSUME_NONNULL_BEGIN - (NSData *)encrypt:(NSData *)dataToEncrypt withKey:(NSData *)cipherKey { - NSData *iv = [Cryptography generateRandomBytes:kCCBlockSizeAES128]; + NSData *iv = self.initializationVector; + OWSAssert(iv.length == kCCBlockSizeAES128); + // allow space for message + padding any incomplete block size_t bufferSize = dataToEncrypt.length + kCCBlockSizeAES128; void *buffer = malloc(bufferSize); diff --git a/SignalServiceKit/tests/Devices/OWSProvisioningCipherTest.m b/SignalServiceKit/tests/Devices/OWSProvisioningCipherTest.m new file mode 100644 index 000000000..edde5864c --- /dev/null +++ b/SignalServiceKit/tests/Devices/OWSProvisioningCipherTest.m @@ -0,0 +1,134 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import +#import +#import <25519/Curve25519.h> +#import + +@interface OWSProvisioningCipher(Testing) + +// Expose private method for testing. +- (instancetype)initWithTheirPublicKey:(NSData *)theirPublicKey + ourKeyPair:(ECKeyPair *)ourKeyPair + initializationVector:(NSData *)initializationVector; + +@end + +@interface OWSProvisioningCipherTest : XCTestCase + +@end + +@implementation OWSProvisioningCipherTest + +- (NSData *)knownInitializationVector +{ + uint8_t initilizationVectorBytes[] = { + 0xec, 0x67, 0x0b, 0xb7, + 0x18, 0xe1, 0xe9, 0x0a, + 0xcc, 0x5e, 0xcb, 0x37, + 0xab, 0x79, 0xe0, 0x09 + }; + return [NSData dataWithBytes:initilizationVectorBytes length:16]; +} + +- (NSData *)knownPublicKey +{ + uint8_t knownPublicKeyBytes[] = { + 0x5e, 0x23, 0xe8, 0x49, + 0xb2, 0x23, 0x21, 0xdb, + 0x2e, 0x3a, 0x77, 0x74, + 0x6f, 0x3b, 0x44, 0x18, + 0xcc, 0x6c, 0x81, 0xce, + 0xd5, 0xc2, 0x91, 0xaf, + 0xed, 0xfb, 0x21, 0x4e, + 0x59, 0xcc, 0x19, 0xa4 + }; + return [NSData dataWithBytes:knownPublicKeyBytes length: 32]; +} + +- (ECKeyPair *)knownKeyPair +{ + uint8_t privateKeyBytes[] = { + 0x60, 0xfd, 0xc1, 0xeb, + 0x6a, 0x68, 0x3d, 0x2b, + 0x51, 0x23, 0x1f, 0xea, + 0x1a, 0x5e, 0x80, 0x88, + 0x0c, 0x65, 0x2d, 0x3d, + 0x47, 0x9e, 0x28, 0xc1, + 0x9f, 0x48, 0x2c, 0x66, + 0xde, 0x48, 0x5d, 0x57 + }; + + uint8_t publicKeyBytes[] = { + 0x02, 0x62, 0x7b, 0x5c, + 0x21, 0x15, 0x59, 0x1b, + 0x37, 0xd1, 0xfe, 0xeb, + 0x15, 0x5d, 0xd2, 0x95, + 0x0a, 0xce, 0xe8, 0xb2, + 0x1e, 0x8e, 0xc8, 0xd6, + 0x53, 0x4f, 0x1a, 0xcd, + 0xf2, 0x00, 0x98, 0x32 + }; + + // Righteous hack to build a deterministic ECKeyPair + // The publicKey/privateKey ivars are private but it's possible to `initWithCoder:` given the proper keys. + NSKeyedArchiver *archiver = [NSKeyedArchiver new]; + [archiver encodeBytes:publicKeyBytes length:ECCKeyLength forKey:@"TSECKeyPairPublicKey"]; + [archiver encodeBytes:privateKeyBytes length:ECCKeyLength forKey:@"TSECKeyPairPrivateKey"]; + NSData *serialized = [archiver encodedData]; + + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:serialized]; + return [[ECKeyPair alloc] initWithCoder:unarchiver]; +} + +- (NSData *)knownData +{ + uint8_t knownBytes[] = { + 0x19, 0x33, 0x78, 0x64, + 0x96, 0x56, 0xa7, 0xd0, + 0x6e, 0xff, 0x37, 0x1d + }; + + return [NSData dataWithBytes:knownBytes length:12]; +} + +- (void)testEncrypt +{ + NSData *theirPublicKey = [self knownPublicKey]; + ECKeyPair *ourKeyPair = [self knownKeyPair]; + NSData *initializationVector = [self knownInitializationVector]; + + OWSProvisioningCipher *cipher = [[OWSProvisioningCipher alloc] initWithTheirPublicKey:theirPublicKey + ourKeyPair:ourKeyPair + initializationVector:initializationVector]; + + NSData *message = [self knownData]; + NSData *actualOutput = [cipher encrypt:message]; + + uint8_t expectedBytes[] = { + 0x01, 0xec, 0x67, 0x0b, + 0xb7, 0x18, 0xe1, 0xe9, + 0x0a, 0xcc, 0x5e, 0xcb, + 0x37, 0xab, 0x79, 0xe0, + 0x09, 0xf7, 0x2b, 0xf7, + 0x14, 0x3d, 0x45, 0xd7, + 0x45, 0x79, 0x1e, 0x4f, + 0x9d, 0x34, 0x8a, 0x2d, + 0x43, 0x64, 0xd4, 0x7d, + 0x48, 0x9a, 0xdc, 0x5a, + 0xc3, 0x72, 0xfa, 0x63, + 0x41, 0x7a, 0xa8, 0x45, + 0x36, 0xe9, 0xc5, 0xcb, + 0xee, 0x9b, 0xc1, 0x1f, + 0xec, 0x31, 0x1e, 0xc2, + 0x33, 0x2d, 0x95, 0x54, + 0xcc + }; + NSData *expectedOutput = [NSData dataWithBytes:expectedBytes length:65]; + + XCTAssertEqualObjects(expectedOutput, actualOutput); +} + +@end