Merge branch 'mkirk/fix-provisioning-cipher'

pull/1/head
Michael Kirk 8 years ago
commit 6156fcb247

@ -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

@ -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 = "<group>"; };
45046FDF1D95A6130015EFF2 /* TSMessagesManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessagesManagerTest.m; path = ../../../tests/Messages/TSMessagesManagerTest.m; sourceTree = "<group>"; };
450E3C991D96DD2600BF4EB6 /* OWSDisappearingMessagesJobTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDisappearingMessagesJobTest.m; path = ../../../tests/Messages/OWSDisappearingMessagesJobTest.m; sourceTree = "<group>"; };
451686AD1F527A9C00AC3D4B /* OWSProvisioningCipherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSProvisioningCipherTest.m; path = ../../../tests/Devices/OWSProvisioningCipherTest.m; sourceTree = "<group>"; };
4516E3E71DD153CC00DC4206 /* TSGroupThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSGroupThreadTest.m; path = ../../../tests/Contacts/TSGroupThreadTest.m; sourceTree = "<group>"; };
4516E3E91DD1542300DC4206 /* TSContactThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSContactThreadTest.m; path = ../../../tests/Contacts/TSContactThreadTest.m; sourceTree = "<group>"; };
452137211E8D6D2F0048FD10 /* OWSFakeMessageSender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSFakeMessageSender.h; path = ../../../tests/TestSupport/Fakes/OWSFakeMessageSender.h; sourceTree = "<group>"; };
@ -263,6 +265,7 @@
children = (
45D7243E1D67899F00E0CA54 /* OWSDeviceProvisionerTest.m */,
45B840201D988DA100F9E938 /* OWSReadReceiptTest.m */,
451686AD1F527A9C00AC3D4B /* OWSProvisioningCipherTest.m */,
);
name = Devices;
sourceTree = "<group>";
@ -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 */,

@ -1,9 +1,12 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSDeviceProvisioner.h"
#import "OWSDeviceProvisioningCodeService.h"
#import "OWSDeviceProvisioningService.h"
#import "OWSProvisioningMessage.h"
#import "OWSError.h"
NS_ASSUME_NONNULL_BEGIN
@ -88,7 +91,14 @@ NS_ASSUME_NONNULL_BEGIN
profileKey:self.profileKey
provisioningCode:provisioningCode];
[self.provisioningService provisionWithMessageBody:[message buildEncryptedMessageBody]
NSData *_Nullable messageBody = [message buildEncryptedMessageBody];
if (messageBody == nil) {
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToEncryptMessage, @"Failed building provisioning message");
failureCallback(error);
return;
}
[self.provisioningService provisionWithMessageBody:messageBody
ephemeralDeviceId:self.ephemeralDeviceId
success:^{
DDLogInfo(@"ProvisioningService SUCCEEDED");

@ -1,4 +1,6 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@ -7,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) NSData *ourPublicKey;
- (instancetype)initWithTheirPublicKey:(NSData *)theirPublicKey;
- (NSData *)encrypt:(NSData *)plainText;
- (nullable NSData *)encrypt:(NSData *)plainText;
@end

@ -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]];
}
// Private method which exposes dependencies for testing
- (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;
}
@ -34,7 +48,7 @@ NS_ASSUME_NONNULL_BEGIN
return self.ourKeyPair.publicKey;
}
- (NSData *)encrypt:(NSData *)dataToEncrypt
- (nullable NSData *)encrypt:(NSData *)dataToEncrypt
{
NSData *sharedSecret =
[Curve25519 generateSharedSecretFromPublicKey:self.theirPublicKey andKeyPair:self.ourKeyPair];
@ -50,7 +64,12 @@ NS_ASSUME_NONNULL_BEGIN
u_int8_t versionByte[] = { 0x01 };
NSMutableData *message = [NSMutableData dataWithBytes:&versionByte length:1];
NSData *cipherText = [self encrypt:dataToEncrypt withKey:cipherKey];
NSData *_Nullable cipherText = [self encrypt:dataToEncrypt withKey:cipherKey];
if (cipherText == nil) {
OWSFail(@"Provisioning cipher failed.");
return nil;
}
[message appendData:cipherText];
NSData *mac = [self macForMessage:message withKey:macKey];
@ -59,12 +78,27 @@ NS_ASSUME_NONNULL_BEGIN
return [message copy];
}
- (NSData *)encrypt:(NSData *)dataToEncrypt withKey:(NSData *)cipherKey
- (nullable NSData *)encrypt:(NSData *)dataToEncrypt withKey:(NSData *)cipherKey
{
NSData *iv = [Cryptography generateRandomBytes:kCCBlockSizeAES128];
NSData *iv = self.initializationVector;
if (iv.length != kCCBlockSizeAES128) {
OWSFail(@"Unexpected length for iv");
return nil;
}
// allow space for message + padding any incomplete block
size_t bufferSize = dataToEncrypt.length + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
NSUInteger blockCount = ceil((double)dataToEncrypt.length / (double)kCCBlockSizeAES128);
size_t ciphertextBufferSize = blockCount * kCCBlockSizeAES128;
// message format is (iv || ciphertext)
NSMutableData *encryptedMessage = [NSMutableData dataWithLength:iv.length + ciphertextBufferSize];
// write the iv
[encryptedMessage replaceBytesInRange:NSMakeRange(0, iv.length) withBytes:iv.bytes];
// cipher text follows iv
char *ciphertextBuffer = encryptedMessage.mutableBytes + iv.length;
size_t bytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
@ -75,26 +109,25 @@ NS_ASSUME_NONNULL_BEGIN
iv.bytes,
dataToEncrypt.bytes,
dataToEncrypt.length,
buffer,
bufferSize,
ciphertextBuffer,
ciphertextBufferSize,
&bytesEncrypted);
if (cryptStatus != kCCSuccess) {
DDLogError(@"Encryption failed with status: %d", cryptStatus);
return nil;
}
NSMutableData *encryptedMessage = [[NSMutableData alloc] initWithData:iv];
[encryptedMessage appendBytes:buffer length:bytesEncrypted];
return [encryptedMessage copy];
}
- (NSData *)macForMessage:(NSData *)message withKey:(NSData *)macKey
{
uint8_t hmacBytes[CC_SHA256_DIGEST_LENGTH] = { 0 };
CCHmac(kCCHmacAlgSHA256, macKey.bytes, macKey.length, message.bytes, message.length, hmacBytes);
NSMutableData *hmac = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, macKey.bytes, macKey.length, message.bytes, message.length, hmac.mutableBytes);
return [NSData dataWithBytes:hmacBytes length:CC_SHA256_DIGEST_LENGTH];
return [hmac copy];
}

@ -1,4 +1,6 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@ -11,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
profileKey:(NSData *)profileKey
provisioningCode:(NSString *)provisioningCode;
- (NSData *)buildEncryptedMessageBody;
- (nullable NSData *)buildEncryptedMessageBody;
@end

@ -1,4 +1,6 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSProvisioningMessage.h"
#import "OWSProvisioningCipher.h"
@ -44,7 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
- (NSData *)buildEncryptedMessageBody
- (nullable NSData *)buildEncryptedMessageBody
{
OWSProvisioningProtosProvisionMessageBuilder *messageBuilder = [OWSProvisioningProtosProvisionMessageBuilder new];
[messageBuilder setIdentityKeyPublic:self.myPublicKey];
@ -57,7 +59,11 @@ NS_ASSUME_NONNULL_BEGIN
NSData *plainTextProvisionMessage = [[messageBuilder build] data];
OWSProvisioningCipher *cipher = [[OWSProvisioningCipher alloc] initWithTheirPublicKey:self.theirPublicKey];
NSData *encryptedProvisionMessage = [cipher encrypt:plainTextProvisionMessage];
NSData *_Nullable encryptedProvisionMessage = [cipher encrypt:plainTextProvisionMessage];
if (encryptedProvisionMessage == nil) {
DDLogError(@"Failed to encrypt provision message");
return nil;
}
OWSProvisioningProtosProvisionEnvelopeBuilder *envelopeBuilder = [OWSProvisioningProtosProvisionEnvelopeBuilder new];
// Note that this is a one-time-use *cipher* public key, not our Signal *identity* public key

@ -0,0 +1,134 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <XCTest/XCTest.h>
#import <SignalServiceKit/OWSProvisioningCipher.h>
#import <25519/Curve25519.h>
#import <SignalServiceKit/Cryptography.h>
@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
Loading…
Cancel
Save