Compare commits
1 Commits
master
...
charlesmch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b84adc0967 |
@ -11,7 +11,7 @@ Pod::Spec.new do |s|
|
||||
s.source_files = "AxolotlKit/Classes/**/*.{h,m,swift}", "AxolotlKit/Private/*.{h,m,swift}"
|
||||
s.public_header_files = "AxolotlKit/Classes/**/*.{h}"
|
||||
s.prefix_header_file = "AxolotlKit/SPKPrefix.h"
|
||||
s.ios.deployment_target = "10.0"
|
||||
s.ios.deployment_target = "9.0"
|
||||
s.osx.deployment_target = "10.8"
|
||||
s.requires_arc = true
|
||||
|
||||
|
||||
@ -310,7 +310,6 @@
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = B6B98F6F197D838A00B16B5E;
|
||||
@ -527,7 +526,7 @@
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = "AxolotlKit/AxolotlKit-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.whispersystems.SignalProtocolKitTestApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
@ -544,7 +543,7 @@
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "";
|
||||
INFOPLIST_FILE = "AxolotlKit/AxolotlKit-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.whispersystems.SignalProtocolKitTestApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
@ -640,7 +639,7 @@
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@ -686,7 +685,7 @@
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// PrekeyWhisperMessage.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WhisperMessage.h"
|
||||
@ -9,15 +13,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PreKeyWhisperMessage : NSObject <CipherMessage>
|
||||
|
||||
- (instancetype)init_throws_withData:(NSData *)serialized NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable instancetype)initWithData:(NSData *)serialized error:(NSError **)outError;
|
||||
- (instancetype)initWithData:(NSData *)serialized;
|
||||
|
||||
- (instancetype)init_throws_withWhisperMessage:(WhisperMessage *)whisperMessage
|
||||
registrationId:(int)registrationId
|
||||
prekeyId:(int)prekeyId
|
||||
signedPrekeyId:(int)signedPrekeyId
|
||||
baseKey:(NSData *)baseKey
|
||||
identityKey:(NSData *)identityKey NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (instancetype)initWithWhisperMessage:(WhisperMessage *)whisperMessage
|
||||
registrationId:(int)registrationId
|
||||
prekeyId:(int)prekeyId
|
||||
signedPrekeyId:(int)signedPrekeyId
|
||||
baseKey:(NSData *)baseKey
|
||||
identityKey:(NSData *)identityKey;
|
||||
|
||||
@property (nonatomic, readonly) int registrationId;
|
||||
@property (nonatomic, readonly) int version;
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
#import "Constants.h"
|
||||
#import "SerializationUtilities.h"
|
||||
#import <AxolotlKit/AxolotlKit-Swift.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -23,12 +22,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation PreKeyWhisperMessage
|
||||
|
||||
- (instancetype)init_throws_withWhisperMessage:(WhisperMessage *)whisperMessage
|
||||
registrationId:(int)registrationId
|
||||
prekeyId:(int)prekeyId
|
||||
signedPrekeyId:(int)signedPrekeyId
|
||||
baseKey:(NSData *)baseKey
|
||||
identityKey:(NSData *)identityKey
|
||||
- (instancetype)initWithWhisperMessage:(WhisperMessage *)whisperMessage
|
||||
registrationId:(int)registrationId
|
||||
prekeyId:(int)prekeyId
|
||||
signedPrekeyId:(int)signedPrekeyId
|
||||
baseKey:(NSData *)baseKey
|
||||
identityKey:(NSData *)identityKey
|
||||
{
|
||||
OWSAssert(whisperMessage);
|
||||
OWSAssert(baseKey);
|
||||
@ -70,18 +69,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithData:(NSData *)serialized error:(NSError **)outError
|
||||
{
|
||||
@try {
|
||||
self = [self init_throws_withData:serialized];
|
||||
return self;
|
||||
} @catch (NSException *exception) {
|
||||
*outError = SCKExceptionWrapperErrorMake(exception);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init_throws_withData:(NSData *)serialized
|
||||
- (instancetype)initWithData:(NSData *)serialized
|
||||
{
|
||||
if (self = [super init]) {
|
||||
if (serialized.length < 1) {
|
||||
@ -121,7 +109,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
_signedPrekeyId = preKeyWhisperMessage.signedPreKeyID;
|
||||
_baseKey = preKeyWhisperMessage.baseKey;
|
||||
_identityKey = preKeyWhisperMessage.identityKey;
|
||||
_message = [[WhisperMessage alloc] init_throws_withData:preKeyWhisperMessage.message];
|
||||
_message = [[WhisperMessage alloc] initWithData:preKeyWhisperMessage.message];
|
||||
}
|
||||
|
||||
return self;
|
||||
@ -131,18 +119,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return CipherMessageType_Prekey;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)logTag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)logTag
|
||||
{
|
||||
return self.class.logTag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// WhisperMessage.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CipherMessage.h"
|
||||
@ -18,22 +22,21 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, readonly) NSData *cipherText;
|
||||
@property (nonatomic, readonly) NSData *serialized;
|
||||
|
||||
- (instancetype)init_throws_withData:(NSData *)serialized NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable instancetype)initWithData:(NSData *)serialized error:(NSError **)outError;
|
||||
- (instancetype)initWithData:(NSData *)serialized;
|
||||
|
||||
- (instancetype)init_throws_withVersion:(int)version
|
||||
macKey:(NSData *)macKey
|
||||
senderRatchetKey:(NSData *)senderRatchetKey
|
||||
counter:(int)counter
|
||||
previousCounter:(int)previousCounter
|
||||
cipherText:(NSData *)cipherText
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (instancetype)initWithVersion:(int)version
|
||||
macKey:(NSData *)macKey
|
||||
senderRatchetKey:(NSData *)senderRatchetKey
|
||||
counter:(int)counter
|
||||
previousCounter:(int)previousCounter
|
||||
cipherText:(NSData *)cipherText
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey;
|
||||
|
||||
- (void)throws_verifyMacWithVersion:(int)messageVersion
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (void)verifyMacWithVersion:(int)messageVersion
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -5,11 +5,10 @@
|
||||
#import "WhisperMessage.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "Constants.h"
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import "NSData+keyVersionByte.h"
|
||||
#import "SerializationUtilities.h"
|
||||
#import <AxolotlKit/AxolotlKit-Swift.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
#import <SignalCoreKit/SignalCoreKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@ -18,14 +17,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation WhisperMessage
|
||||
|
||||
- (instancetype)init_throws_withVersion:(int)version
|
||||
macKey:(NSData *)macKey
|
||||
senderRatchetKey:(NSData *)senderRatchetKey
|
||||
counter:(int)counter
|
||||
previousCounter:(int)previousCounter
|
||||
cipherText:(NSData *)cipherText
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
- (instancetype)initWithVersion:(int)version
|
||||
macKey:(NSData *)macKey
|
||||
senderRatchetKey:(NSData *)senderRatchetKey
|
||||
counter:(int)counter
|
||||
previousCounter:(int)previousCounter
|
||||
cipherText:(NSData *)cipherText
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
{
|
||||
OWSAssert(macKey);
|
||||
OWSAssert(senderRatchetKey);
|
||||
@ -50,11 +49,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
[serialized appendData:messageData];
|
||||
|
||||
NSData *mac = [SerializationUtilities throws_macWithVersion:version
|
||||
identityKey:[senderIdentityKey prependKeyType]
|
||||
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
||||
macKey:macKey
|
||||
serialized:serialized];
|
||||
NSData *mac = [SerializationUtilities macWithVersion:version
|
||||
identityKey:[senderIdentityKey prependKeyType]
|
||||
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
||||
macKey:macKey
|
||||
serialized:serialized];
|
||||
|
||||
[serialized appendData:mac];
|
||||
|
||||
@ -69,18 +68,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithData:(NSData *)serialized error:(NSError **)outError
|
||||
{
|
||||
@try {
|
||||
self = [self init_throws_withData:serialized];
|
||||
return self;
|
||||
} @catch (NSException *exception) {
|
||||
*outError = SCKExceptionWrapperErrorMake(exception);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init_throws_withData:(NSData *)serialized
|
||||
- (instancetype)initWithData:(NSData *)serialized
|
||||
{
|
||||
if (self = [super init]) {
|
||||
if (serialized.length <= (VERSION_LENGTH + MAC_LENGTH)) {
|
||||
@ -125,7 +113,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
|
||||
_serialized = serialized;
|
||||
_senderRatchetKey = [whisperMessage.ratchetKey throws_removeKeyType];
|
||||
_senderRatchetKey = [whisperMessage.ratchetKey removeKeyType];
|
||||
_version = [SerializationUtilities highBitsToIntFromByte:version];
|
||||
_counter = whisperMessage.counter;
|
||||
_previousCounter = whisperMessage.previousCounter;
|
||||
@ -135,10 +123,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)throws_verifyMacWithVersion:(int)messageVersion
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
- (void)verifyMacWithVersion:(int)messageVersion
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
{
|
||||
OWSAssert(senderIdentityKey);
|
||||
OWSAssert(receiverIdentityKey);
|
||||
@ -167,11 +155,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
OWSRaiseException(InvalidMessageException, @"Could not parse their mac.");
|
||||
}
|
||||
|
||||
NSData *ourMac = [SerializationUtilities throws_macWithVersion:messageVersion
|
||||
identityKey:[senderIdentityKey prependKeyType]
|
||||
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
||||
macKey:macKey
|
||||
serialized:data];
|
||||
NSData *ourMac = [SerializationUtilities macWithVersion:messageVersion
|
||||
identityKey:[senderIdentityKey prependKeyType]
|
||||
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
||||
macKey:macKey
|
||||
serialized:data];
|
||||
|
||||
if (![theirMac ows_constantTimeIsEqualToData:ourMac]) {
|
||||
OWSFailDebug(@"Bad Mac! Their Mac: %@ Our Mac: %@", theirMac, ourMac);
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// AES-CBC.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@ -18,9 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @return ciphertext
|
||||
*/
|
||||
|
||||
+ (NSData *)throws_encryptCBCMode:(NSData *)data
|
||||
withKey:(NSData *)key
|
||||
withIV:(NSData *)iv NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (NSData *)encryptCBCMode:(NSData *)data withKey:(NSData *)key withIV:(NSData *)iv;
|
||||
|
||||
/**
|
||||
* Decrypts with AES in CBC mode
|
||||
@ -32,9 +34,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @return plaintext
|
||||
*/
|
||||
|
||||
+ (NSData *)throws_decryptCBCMode:(NSData *)data
|
||||
withKey:(NSData *)key
|
||||
withIV:(NSData *)iv NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (NSData *)decryptCBCMode:(NSData *)data withKey:(NSData *)key withIV:(NSData *)iv;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// AES-CBC.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AES-CBC.h"
|
||||
@ -15,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark AESCBC Mode
|
||||
|
||||
+ (NSData *)throws_encryptCBCMode:(NSData *)data withKey:(NSData *)key withIV:(NSData *)iv
|
||||
+ (NSData *)encryptCBCMode:(NSData *)data withKey:(NSData *)key withIV:(NSData *)iv
|
||||
{
|
||||
if (!data) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"Missing data to encrypt." userInfo:nil];
|
||||
@ -57,7 +61,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSData *)throws_decryptCBCMode:(NSData *)data withKey:(NSData *)key withIV:(NSData *)iv
|
||||
+ (NSData *)decryptCBCMode:(NSData *)data withKey:(NSData *)key withIV:(NSData *)iv
|
||||
{
|
||||
if (!data) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"Missing data to decrypt." userInfo:nil];
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// AxolotlKeyFetch.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 21/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PreKeyBundle : NSObject <NSSecureCoding>
|
||||
|
||||
@property (nonatomic, readonly) NSData *identityKey;
|
||||
@ -27,5 +29,3 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
identityKey:(NSData *)identityKey;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// AxolotlKeyFetch.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 21/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PreKeyBundle.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString* const kCoderPKBIdentityKey = @"kCoderPKBIdentityKey";
|
||||
static NSString* const kCoderPKBregistrationId = @"kCoderPKBregistrationId";
|
||||
@ -26,20 +29,20 @@ static NSString* const kCoderPKBsignedPreKeySignature = @"kCoderPKBsignedPreKeyS
|
||||
signedPreKeySignature:(NSData *)signedPreKeySignature
|
||||
identityKey:(NSData *)identityKey
|
||||
{
|
||||
if (preKeyPublic && preKeyPublic.length != 33) {
|
||||
OWSFailDebug(@"preKeyPublic && preKeyPublic.length != 33");
|
||||
if (preKeyPublic && preKeyPublic.length == 32) {
|
||||
OWSFailDebug(@"preKeyPublic && preKeyPublic.length == 32");
|
||||
return nil;
|
||||
}
|
||||
if (signedPreKeyPublic.length != 33) {
|
||||
OWSFailDebug(@"signedPreKeyPublic.length != 33");
|
||||
if (signedPreKeyPublic.length != 32) {
|
||||
OWSFailDebug(@"signedPreKeyPublic.length != 32");
|
||||
return nil;
|
||||
}
|
||||
if (!signedPreKeySignature) {
|
||||
OWSFailDebug(@"!signedPreKeySignature");
|
||||
return nil;
|
||||
}
|
||||
if (identityKey.length != 33) {
|
||||
OWSFailDebug(@"identityKey.length != 33");
|
||||
if (identityKey.length != 32) {
|
||||
OWSFailDebug(@"identityKey.length != 32");
|
||||
return nil;
|
||||
}
|
||||
|
||||
@ -59,8 +62,7 @@ static NSString* const kCoderPKBsignedPreKeySignature = @"kCoderPKBsignedPreKeyS
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder{
|
||||
int registrationId = [aDecoder decodeIntForKey:kCoderPKBregistrationId];
|
||||
int deviceId = [aDecoder decodeIntForKey:kCoderPKBdeviceId];
|
||||
int preKeyId = [aDecoder decodeIntForKey:kCoderPKBpreKeyId];
|
||||
@ -101,5 +103,3 @@ static NSString* const kCoderPKBsignedPreKeySignature = @"kCoderPKBsignedPreKeyS
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -5,20 +5,12 @@
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PreKeyRecord : NSObject <NSSecureCoding>
|
||||
|
||||
@property (nonatomic, readonly) int Id;
|
||||
@property (nonatomic, readonly) ECKeyPair *keyPair;
|
||||
@property (nonatomic, readonly, nullable) NSDate *createdAt;
|
||||
|
||||
- (instancetype)initWithId:(int)identifier
|
||||
keyPair:(ECKeyPair *)keyPair
|
||||
createdAt:(NSDate *)createdAt;
|
||||
|
||||
- (void)setCreatedAtToNow;
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair*)keyPair;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -8,11 +8,8 @@
|
||||
|
||||
#import "PreKeyRecord.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString* const kCoderPreKeyId = @"kCoderPreKeyId";
|
||||
static NSString* const kCoderPreKeyPair = @"kCoderPreKeyPair";
|
||||
static NSString* const kCoderCreatedAt = @"kCoderCreatedAt";
|
||||
|
||||
@implementation PreKeyRecord
|
||||
|
||||
@ -20,10 +17,7 @@ static NSString* const kCoderCreatedAt = @"kCoderCreatedAt";
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(int)identifier
|
||||
keyPair:(ECKeyPair*)keyPair
|
||||
createdAt:(NSDate *)createdAt
|
||||
{
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair*)keyPair{
|
||||
OWSAssert(keyPair);
|
||||
|
||||
self = [super init];
|
||||
@ -31,30 +25,20 @@ static NSString* const kCoderCreatedAt = @"kCoderCreatedAt";
|
||||
if (self) {
|
||||
_Id = identifier;
|
||||
_keyPair = keyPair;
|
||||
_createdAt = createdAt;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable id)initWithCoder:(NSCoder *)aDecoder {
|
||||
return [self initWithId:[aDecoder decodeIntForKey:kCoderPreKeyId]
|
||||
keyPair:[aDecoder decodeObjectOfClass:[ECKeyPair class] forKey:kCoderPreKeyPair]
|
||||
createdAt:[aDecoder decodeObjectOfClass:[NSDate class] forKey:kCoderCreatedAt]];
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder{
|
||||
return [self initWithId:[aDecoder decodeIntForKey:kCoderPreKeyId] keyPair:[aDecoder decodeObjectOfClass:[ECKeyPair class] forKey:kCoderPreKeyPair]];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder{
|
||||
[aCoder encodeInteger:_Id forKey:kCoderPreKeyId];
|
||||
[aCoder encodeObject:_keyPair forKey:kCoderPreKeyPair];
|
||||
if (_createdAt != nil) {
|
||||
[aCoder encodeObject:_createdAt forKey:kCoderCreatedAt];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCreatedAtToNow {
|
||||
_createdAt = [NSDate date];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -6,8 +6,6 @@
|
||||
#import "PreKeyRecord.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SignedPreKeyRecord : PreKeyRecord <NSSecureCoding>
|
||||
|
||||
@property (nonatomic, readonly) NSData *signature;
|
||||
@ -21,5 +19,3 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)markAsAcceptedByService;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
|
||||
#import "SignedPrekeyRecord.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString* const kCoderPreKeyId = @"kCoderPreKeyId";
|
||||
static NSString* const kCoderPreKeyPair = @"kCoderPreKeyPair";
|
||||
static NSString* const kCoderPreKeyDate = @"kCoderPreKeyDate";
|
||||
@ -28,9 +26,7 @@ static NSString *const kCoderPreKeyWasAcceptedByService = @"kCoderPreKeyWasAccep
|
||||
OWSAssert(signature);
|
||||
OWSAssert(generatedAt);
|
||||
|
||||
self = [super initWithId:identifier
|
||||
keyPair:keyPair
|
||||
createdAt:generatedAt];
|
||||
self = [super initWithId:identifier keyPair:keyPair];
|
||||
|
||||
if (self) {
|
||||
_signature = signature;
|
||||
@ -42,9 +38,7 @@ static NSString *const kCoderPreKeyWasAcceptedByService = @"kCoderPreKeyWasAccep
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair *)keyPair signature:(NSData*)signature generatedAt:(NSDate *)generatedAt{
|
||||
self = [super initWithId:identifier
|
||||
keyPair:keyPair
|
||||
createdAt:generatedAt];
|
||||
self = [super initWithId:identifier keyPair:keyPair];
|
||||
|
||||
if (self) {
|
||||
_signature = signature;
|
||||
@ -54,7 +48,7 @@ static NSString *const kCoderPreKeyWasAcceptedByService = @"kCoderPreKeyWasAccep
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable id)initWithCoder:(NSCoder *)aDecoder{
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder{
|
||||
return [self initWithId:[aDecoder decodeIntForKey:kCoderPreKeyId]
|
||||
keyPair:[aDecoder decodeObjectOfClass:[ECKeyPair class] forKey:kCoderPreKeyPair]
|
||||
signature:[aDecoder decodeObjectOfClass:[NSData class] forKey:kCoderPreKeySignature]
|
||||
@ -81,5 +75,3 @@ static NSString *const kCoderPreKeyWasAcceptedByService = @"kCoderPreKeyWasAccep
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// ChainKey.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/08/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Chain.h"
|
||||
@ -18,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (instancetype)nextChainKey;
|
||||
|
||||
- (MessageKeys *)throws_messageKeys NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (MessageKeys *)messageKeys;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ChainKey.h"
|
||||
@ -63,10 +63,10 @@ static uint8_t kChainKeySeed[kTSKeySeedLength] = { 02 };
|
||||
return [[ChainKey alloc] initWithData:nextCK index:nextIndex];
|
||||
}
|
||||
|
||||
- (MessageKeys *)throws_messageKeys
|
||||
- (MessageKeys *)messageKeys
|
||||
{
|
||||
NSData *inputKeyMaterial = [self baseMaterial:[NSData dataWithBytes:kMessageKeySeed length:kTSKeySeedLength]];
|
||||
TSDerivedSecrets *derivedSecrets = [TSDerivedSecrets throws_derivedMessageKeysWithData:inputKeyMaterial];
|
||||
TSDerivedSecrets *derivedSecrets = [TSDerivedSecrets derivedMessageKeysWithData:inputKeyMaterial];
|
||||
return [[MessageKeys alloc] initWithCipherKey:derivedSecrets.cipherKey
|
||||
macKey:derivedSecrets.macKey
|
||||
iv:derivedSecrets.iv
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// RKCK.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 1/15/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SessionState.h"
|
||||
#import "Chain.h"
|
||||
#import "RootKey.h"
|
||||
#import "SessionState.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class ECKeyPair;
|
||||
|
||||
@interface RKCK : NSObject
|
||||
@ -16,4 +19,4 @@
|
||||
|
||||
-(instancetype) initWithRK:(RootKey*)rootKey CK:(ChainKey*)chainKey;
|
||||
|
||||
@end
|
||||
@end
|
||||
@ -1,41 +1,27 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// RatchetingSession.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class SessionState;
|
||||
@class AliceAxolotlParameters;
|
||||
@class BobAxolotlParameters;
|
||||
@class ECKeyPair;
|
||||
@class SessionState;
|
||||
|
||||
@interface RatchetingSession : NSObject
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
AliceParameters:(AliceAxolotlParameters *)parameters NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion AliceParameters:(AliceAxolotlParameters*)parameters;
|
||||
|
||||
+ (BOOL)initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
aliceParameters:(AliceAxolotlParameters *)aliceParameters
|
||||
error:(NSError **)outError;
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
BobParameters:(BobAxolotlParameters *)parameters NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
+ (BOOL)initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
bobParameters:(BobAxolotlParameters *)bobParameters
|
||||
error:(NSError **)outError;
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion BobParameters:(BobAxolotlParameters*)parameters;
|
||||
|
||||
/**
|
||||
* For testing purposes
|
||||
*/
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
AliceParameters:(AliceAxolotlParameters *)parameters
|
||||
senderRatchet:(ECKeyPair *)ratchet NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion AliceParameters:(AliceAxolotlParameters*)parameters senderRatchet:(ECKeyPair*)ratchet;
|
||||
|
||||
@end
|
||||
|
||||
@ -1,39 +1,37 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RatchetingSession.h"
|
||||
|
||||
#import "AliceAxolotlParameters.h"
|
||||
#import "BobAxolotlParameters.h"
|
||||
#import "ChainKey.h"
|
||||
#import "RootKey.h"
|
||||
#import "SessionState.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import "ChainKey.h"
|
||||
|
||||
@interface DHEResult : NSObject
|
||||
|
||||
@property (nonatomic, readonly) RootKey *rootKey;
|
||||
@property (nonatomic, readonly) NSData *chainKey;
|
||||
|
||||
- (instancetype)init_throws_withMasterKey:(NSData *)data NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (instancetype)initWithMasterKey:(NSData*)data;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DHEResult
|
||||
|
||||
- (instancetype)init_throws_withMasterKey:(NSData *)data
|
||||
{
|
||||
// DHE Result is expected to be the result of 3 or 4 DHEs outputting 32 bytes each,
|
||||
// plus the 32 discontinuity bytes added to make V3 incompatible with V2
|
||||
OWSAssert([data length] == 32 * 4 || [data length] == 32 * 5);
|
||||
- (instancetype)initWithMasterKey:(NSData*)data{
|
||||
// DHE Result is expected to be the result of 3 or 4 DHEs outputting 32 bytes each
|
||||
OWSAssert([data length] == 32 * 4 || [data length] == 32 * 3);
|
||||
|
||||
self = [super init];
|
||||
const char *HKDFDefaultSalt[4] = {0};
|
||||
NSData *salt = [NSData dataWithBytes:HKDFDefaultSalt length:sizeof(HKDFDefaultSalt)];
|
||||
NSData *info = [@"WhisperText" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *derivedMaterial = [HKDFKit throws_deriveKey:data info:info salt:salt outputSize:64];
|
||||
NSData *derivedMaterial = [HKDFKit deriveKey:data info:info salt:salt outputSize:64];
|
||||
OWSAssert(derivedMaterial.length == 64);
|
||||
_rootKey = [[RootKey alloc] initWithData:[derivedMaterial subdataWithRange:NSMakeRange(0, 32)]];
|
||||
_chainKey = [derivedMaterial subdataWithRange:NSMakeRange(32, 32)];
|
||||
@ -46,68 +44,31 @@
|
||||
|
||||
@implementation RatchetingSession
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
AliceParameters:(AliceAxolotlParameters *)parameters
|
||||
{
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion AliceParameters:(AliceAxolotlParameters*)parameters{
|
||||
OWSAssert(session);
|
||||
OWSAssert(parameters);
|
||||
|
||||
ECKeyPair *sendingRatchetKey = [Curve25519 generateKeyPair];
|
||||
OWSAssert(sendingRatchetKey);
|
||||
[self throws_initializeSession:session
|
||||
sessionVersion:sessionVersion
|
||||
AliceParameters:parameters
|
||||
senderRatchet:sendingRatchetKey];
|
||||
[self initializeSession:session sessionVersion:sessionVersion AliceParameters:parameters senderRatchet:sendingRatchetKey];
|
||||
}
|
||||
|
||||
+ (BOOL)initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
bobParameters:(BobAxolotlParameters *)bobParameters
|
||||
error:(NSError **)outError
|
||||
{
|
||||
return [SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
[self throws_initializeSession:session sessionVersion:sessionVersion BobParameters:bobParameters];
|
||||
}
|
||||
error:outError];
|
||||
}
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
BobParameters:(BobAxolotlParameters *)parameters
|
||||
{
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion BobParameters:(BobAxolotlParameters*)parameters{
|
||||
OWSAssert(session);
|
||||
OWSAssert(parameters);
|
||||
|
||||
[session setVersion:sessionVersion];
|
||||
[session setRemoteIdentityKey:parameters.theirIdentityKey];
|
||||
[session setLocalIdentityKey:parameters.ourIdentityKeyPair.publicKey];
|
||||
|
||||
DHEResult *result = [self throws_DHEKeyAgreement:parameters];
|
||||
|
||||
DHEResult *result = [self DHEKeyAgreement:parameters];
|
||||
OWSAssert(result);
|
||||
|
||||
[session setSenderChain:parameters.ourRatchetKey chainKey:[[ChainKey alloc]initWithData:result.chainKey index:0]];
|
||||
[session setRootKey:result.rootKey];
|
||||
}
|
||||
|
||||
+ (BOOL)initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
aliceParameters:(AliceAxolotlParameters *)aliceParameters
|
||||
error:(NSError **)outError
|
||||
{
|
||||
return [SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
[self throws_initializeSession:session sessionVersion:sessionVersion AliceParameters:aliceParameters];
|
||||
}
|
||||
error:outError];
|
||||
}
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
AliceParameters:(AliceAxolotlParameters *)parameters
|
||||
senderRatchet:(ECKeyPair *)sendingRatchet
|
||||
{
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion AliceParameters:(AliceAxolotlParameters*)parameters senderRatchet:(ECKeyPair*)sendingRatchet{
|
||||
|
||||
OWSAssert(session);
|
||||
OWSAssert(parameters);
|
||||
@ -116,11 +77,10 @@
|
||||
[session setVersion:sessionVersion];
|
||||
[session setRemoteIdentityKey:parameters.theirIdentityKey];
|
||||
[session setLocalIdentityKey:parameters.ourIdentityKeyPair.publicKey];
|
||||
|
||||
DHEResult *result = [self throws_DHEKeyAgreement:parameters];
|
||||
|
||||
DHEResult *result = [self DHEKeyAgreement:parameters];
|
||||
OWSAssert(result);
|
||||
RKCK *sendingChain =
|
||||
[result.rootKey throws_createChainWithTheirEphemeral:parameters.theirRatchetKey ourEphemeral:sendingRatchet];
|
||||
RKCK *sendingChain = [result.rootKey createChainWithTheirEphemeral:parameters.theirRatchetKey ourEphemeral:sendingRatchet];
|
||||
OWSAssert(sendingChain);
|
||||
|
||||
[session addReceiverChain:parameters.theirRatchetKey chainKey:[[ChainKey alloc]initWithData:result.chainKey index:0]];
|
||||
@ -128,8 +88,7 @@
|
||||
[session setRootKey:sendingChain.rootKey];
|
||||
}
|
||||
|
||||
+ (DHEResult *)throws_DHEKeyAgreement:(id<AxolotlParameters>)parameters
|
||||
{
|
||||
+ (DHEResult*)DHEKeyAgreement:(id<AxolotlParameters>)parameters{
|
||||
OWSAssert(parameters);
|
||||
|
||||
NSMutableData *masterKey = [NSMutableData data];
|
||||
@ -139,32 +98,24 @@
|
||||
if ([parameters isKindOfClass:[AliceAxolotlParameters class]]) {
|
||||
AliceAxolotlParameters *params = (AliceAxolotlParameters*)parameters;
|
||||
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirSignedPreKey
|
||||
andKeyPair:params.ourIdentityKeyPair]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirIdentityKey
|
||||
andKeyPair:params.ourBaseKey]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirSignedPreKey
|
||||
andKeyPair:params.ourBaseKey]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirSignedPreKey andKeyPair:params.ourIdentityKeyPair]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirIdentityKey andKeyPair:params.ourBaseKey]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirSignedPreKey andKeyPair:params.ourBaseKey]];
|
||||
if (params.theirOneTimePrekey) {
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirOneTimePrekey
|
||||
andKeyPair:params.ourBaseKey]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirOneTimePrekey andKeyPair:params.ourBaseKey]];
|
||||
}
|
||||
} else if ([parameters isKindOfClass:[BobAxolotlParameters class]]){
|
||||
BobAxolotlParameters *params = (BobAxolotlParameters*)parameters;
|
||||
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirIdentityKey
|
||||
andKeyPair:params.ourSignedPrekey]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirBaseKey
|
||||
andKeyPair:params.ourIdentityKeyPair]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirBaseKey
|
||||
andKeyPair:params.ourSignedPrekey]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirIdentityKey andKeyPair:params.ourSignedPrekey]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirBaseKey andKeyPair:params.ourIdentityKeyPair]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirBaseKey andKeyPair:params.ourSignedPrekey]];
|
||||
if (params.ourOneTimePrekey) {
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirBaseKey
|
||||
andKeyPair:params.ourOneTimePrekey]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirBaseKey andKeyPair:params.ourOneTimePrekey]];
|
||||
}
|
||||
}
|
||||
|
||||
return [[DHEResult alloc] init_throws_withMasterKey:masterKey];
|
||||
|
||||
return [[DHEResult alloc] initWithMasterKey:masterKey];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// RootKey.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class ECKeyPair;
|
||||
@class RKCK;
|
||||
@class ECKeyPair;
|
||||
|
||||
@interface RootKey : NSObject <NSSecureCoding>
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data;
|
||||
- (RKCK *)throws_createChainWithTheirEphemeral:(NSData *)theirEphemeral
|
||||
ourEphemeral:(ECKeyPair *)ourEphemeral NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (RKCK*)createChainWithTheirEphemeral:(NSData*)theirEphemeral ourEphemeral:(ECKeyPair*)ourEphemeral;
|
||||
|
||||
@property (nonatomic, readonly) NSData *keyData;
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RootKey.h"
|
||||
#import "ChainKey.h"
|
||||
#import "RKCK.h"
|
||||
#import "TSDerivedSecrets.h"
|
||||
#import "RKCK.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import "ChainKey.h"
|
||||
|
||||
static NSString* const kCoderData = @"kCoderData";
|
||||
|
||||
@ -42,16 +42,14 @@ static NSString* const kCoderData = @"kCoderData";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (RKCK *)throws_createChainWithTheirEphemeral:(NSData *)theirEphemeral ourEphemeral:(ECKeyPair *)ourEphemeral
|
||||
{
|
||||
- (RKCK*)createChainWithTheirEphemeral:(NSData*)theirEphemeral ourEphemeral:(ECKeyPair*)ourEphemeral{
|
||||
OWSAssert(theirEphemeral);
|
||||
OWSAssert(ourEphemeral);
|
||||
|
||||
NSData *sharedSecret = [Curve25519 throws_generateSharedSecretFromPublicKey:theirEphemeral andKeyPair:ourEphemeral];
|
||||
NSData *sharedSecret = [Curve25519 generateSharedSecretFromPublicKey:theirEphemeral andKeyPair:ourEphemeral];
|
||||
OWSAssert(sharedSecret.length == 32);
|
||||
|
||||
TSDerivedSecrets *secrets =
|
||||
[TSDerivedSecrets throws_derivedRatchetedSecretsWithSharedSecret:sharedSecret rootKey:_keyData];
|
||||
TSDerivedSecrets *secrets = [TSDerivedSecrets derivedRatchetedSecretsWithSharedSecret:sharedSecret rootKey:_keyData];
|
||||
OWSAssert(secrets);
|
||||
|
||||
RKCK *newRKCK = [[RKCK alloc] initWithRK:[[RootKey alloc] initWithData:secrets.cipherKey]
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// TSDerivedSecrets.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 29/03/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TSDerivedSecrets : NSData
|
||||
|
||||
+ (instancetype)throws_derivedInitialSecretsWithMasterKey:(NSData *)masterKey
|
||||
NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (instancetype)throws_derivedRatchetedSecretsWithSharedSecret:(NSData *)masterKey
|
||||
rootKey:(NSData *)rootKey
|
||||
NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (instancetype)throws_derivedMessageKeysWithData:(NSData *)data NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (instancetype)derivedInitialSecretsWithMasterKey:(NSData*)masterKey;
|
||||
+ (instancetype)derivedRatchetedSecretsWithSharedSecret:(NSData*)masterKey rootKey:(NSData*)rootKey;
|
||||
+ (instancetype)derivedMessageKeysWithData:(NSData*)data;
|
||||
|
||||
@property NSData *cipherKey;
|
||||
@property NSData *macKey;
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TSDerivedSecrets.h"
|
||||
#import "HKDFKit.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
|
||||
@implementation TSDerivedSecrets
|
||||
|
||||
+ (instancetype)throws_derivedSecretsWithSeed:(NSData *)masterKey salt:(NSData *)salt info:(NSData *)info
|
||||
{
|
||||
+ (instancetype)derivedSecretsWithSeed:(NSData*)masterKey salt:(NSData*)salt info:(NSData*)info{
|
||||
OWSAssert(masterKey.length == 32);
|
||||
OWSAssert(info);
|
||||
|
||||
@ -22,7 +21,7 @@
|
||||
}
|
||||
|
||||
@try {
|
||||
NSData *derivedMaterial = [HKDFKit throws_deriveKey:masterKey info:info salt:salt outputSize:96];
|
||||
NSData *derivedMaterial = [HKDFKit deriveKey:masterKey info:info salt:salt outputSize:96];
|
||||
secrets.cipherKey = [derivedMaterial subdataWithRange:NSMakeRange(0, 32)];
|
||||
secrets.macKey = [derivedMaterial subdataWithRange:NSMakeRange(32, 32)];
|
||||
secrets.iv = [derivedMaterial subdataWithRange:NSMakeRange(64, 16)];
|
||||
@ -38,29 +37,26 @@
|
||||
return secrets;
|
||||
}
|
||||
|
||||
+ (instancetype)throws_derivedInitialSecretsWithMasterKey:(NSData *)masterKey
|
||||
{
|
||||
+ (instancetype)derivedInitialSecretsWithMasterKey:(NSData*)masterKey{
|
||||
OWSAssert(masterKey);
|
||||
|
||||
NSData *info = [@"WhisperText" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self throws_derivedSecretsWithSeed:masterKey salt:nil info:info];
|
||||
return [self derivedSecretsWithSeed:masterKey salt:nil info:info];
|
||||
}
|
||||
|
||||
+ (instancetype)throws_derivedRatchetedSecretsWithSharedSecret:(NSData *)masterKey rootKey:(NSData *)rootKey
|
||||
{
|
||||
+ (instancetype)derivedRatchetedSecretsWithSharedSecret:(NSData*)masterKey rootKey:(NSData*)rootKey{
|
||||
OWSAssert(masterKey);
|
||||
OWSAssert(rootKey);
|
||||
|
||||
NSData *info = [@"WhisperRatchet" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self throws_derivedSecretsWithSeed:masterKey salt:rootKey info:info];
|
||||
return [self derivedSecretsWithSeed:masterKey salt:rootKey info:info];
|
||||
}
|
||||
|
||||
+ (instancetype)throws_derivedMessageKeysWithData:(NSData *)data
|
||||
{
|
||||
+ (instancetype)derivedMessageKeysWithData:(NSData*)data{
|
||||
OWSAssert(data);
|
||||
|
||||
NSData *info = [@"WhisperMessageKeys" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self throws_derivedSecretsWithSeed:data salt:nil info:info];
|
||||
return [self derivedSecretsWithSeed:data salt:nil info:info];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
//
|
||||
|
||||
#import <AxolotlKit/AxolotlStore.h>
|
||||
#import <AxolotlKit/SPKMockProtocolStore.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SignalCoreKit/NSObject+OWS.h>
|
||||
#import <SignalCoreKit/OWSAsserts.h>
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlStore.h"
|
||||
#import "IdentityKeyStore.h"
|
||||
#import "PreKeyStore.h"
|
||||
#import "PreKeyWhisperMessage.h"
|
||||
#import "SPKProtocolContext.h"
|
||||
#import "SessionState.h"
|
||||
#import "SessionStore.h"
|
||||
#import "SignedPreKeyStore.h"
|
||||
@ -24,23 +23,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// protocolContext is an optional parameter that can be used to ensure that all
|
||||
// identity and session store writes are coordinated and/or occur within a single
|
||||
// transaction.
|
||||
- (id<CipherMessage>)throws_encryptMessage:(NSData *)paddedMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable id<CipherMessage>)encryptMessage:(NSData *)paddedMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError;
|
||||
- (id<CipherMessage>)encryptMessage:(NSData *)paddedMessage protocolContext:(nullable id)protocolContext;
|
||||
|
||||
- (NSData *)throws_decrypt:(id<CipherMessage>)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable NSData *)decrypt:(id<CipherMessage>)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError;
|
||||
- (NSData *)decrypt:(id<CipherMessage>)whisperMessage protocolContext:(nullable id)protocolContext;
|
||||
|
||||
- (int)throws_remoteRegistrationId:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
- (int)throws_sessionVersion:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (int)remoteRegistrationId:(nullable id)protocolContext;
|
||||
- (int)sessionVersion:(nullable id)protocolContext;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SessionCipher.h"
|
||||
@ -19,8 +19,6 @@
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -70,7 +68,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
_deviceId = deviceId;
|
||||
_sessionStore = sessionStore;
|
||||
_identityKeyStore = identityKeyStore;
|
||||
_prekeyStore = preKeyStore;
|
||||
_sessionBuilder = [[SessionBuilder alloc] initWithSessionStore:sessionStore
|
||||
preKeyStore:preKeyStore
|
||||
signedPreKeyStore:signedPreKeyStore
|
||||
@ -82,21 +79,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable id<CipherMessage>)encryptMessage:(NSData *)paddedMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError
|
||||
{
|
||||
__block id<CipherMessage> result;
|
||||
[SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
result = [self throws_encryptMessage:paddedMessage protocolContext:protocolContext];
|
||||
}
|
||||
error:outError];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (id<CipherMessage>)throws_encryptMessage:(NSData *)paddedMessage protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (id<CipherMessage>)encryptMessage:(NSData *)paddedMessage protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(paddedMessage);
|
||||
|
||||
@ -104,7 +87,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
|
||||
SessionState *sessionState = sessionRecord.sessionState;
|
||||
ChainKey *chainKey = sessionState.senderChainKey;
|
||||
MessageKeys *messageKeys = [chainKey throws_messageKeys];
|
||||
MessageKeys *messageKeys = chainKey.messageKeys;
|
||||
NSData *senderRatchetKey = sessionState.senderRatchetKey;
|
||||
int previousCounter = sessionState.previousCounter;
|
||||
int sessionVersion = sessionState.version;
|
||||
@ -113,7 +96,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
recipientId:self.recipientId
|
||||
direction:TSMessageDirectionOutgoing
|
||||
protocolContext:protocolContext]) {
|
||||
OWSLogWarn(
|
||||
DDLogWarn(
|
||||
@"%@ Previously known identity key for while encrypting for recipient: %@", self.tag, self.recipientId);
|
||||
@throw [NSException exceptionWithName:UntrustedIdentityKeyException
|
||||
reason:@"There is a previously known identity key."
|
||||
@ -124,33 +107,31 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
recipientId:self.recipientId
|
||||
protocolContext:protocolContext];
|
||||
|
||||
NSData *ciphertextBody =
|
||||
[AES_CBC throws_encryptCBCMode:paddedMessage withKey:messageKeys.cipherKey withIV:messageKeys.iv];
|
||||
NSData *ciphertextBody = [AES_CBC encryptCBCMode:paddedMessage withKey:messageKeys.cipherKey withIV:messageKeys.iv];
|
||||
|
||||
id<CipherMessage> cipherMessage =
|
||||
[[WhisperMessage alloc] init_throws_withVersion:sessionVersion
|
||||
macKey:messageKeys.macKey
|
||||
senderRatchetKey:senderRatchetKey.prependKeyType
|
||||
counter:chainKey.index
|
||||
previousCounter:previousCounter
|
||||
cipherText:ciphertextBody
|
||||
senderIdentityKey:sessionState.localIdentityKey.prependKeyType
|
||||
receiverIdentityKey:sessionState.remoteIdentityKey.prependKeyType];
|
||||
[[WhisperMessage alloc] initWithVersion:sessionVersion
|
||||
macKey:messageKeys.macKey
|
||||
senderRatchetKey:senderRatchetKey.prependKeyType
|
||||
counter:chainKey.index
|
||||
previousCounter:previousCounter
|
||||
cipherText:ciphertextBody
|
||||
senderIdentityKey:sessionState.localIdentityKey.prependKeyType
|
||||
receiverIdentityKey:sessionState.remoteIdentityKey.prependKeyType];
|
||||
|
||||
if ([sessionState hasUnacknowledgedPreKeyMessage]) {
|
||||
PendingPreKey *items = [sessionState unacknowledgedPreKeyMessageItems];
|
||||
int localRegistrationId = [sessionState localRegistrationId];
|
||||
|
||||
OWSLogInfo(@"Building PreKeyWhisperMessage for: %@.%lu with preKeyId: %d",
|
||||
self.recipientId, (unsigned long) self.deviceId, items.preKeyId);
|
||||
DDLogInfo(@"Building PreKeyWhisperMessage for: %@ with preKeyId: %d", self.recipientId, items.preKeyId);
|
||||
|
||||
cipherMessage =
|
||||
[[PreKeyWhisperMessage alloc] init_throws_withWhisperMessage:cipherMessage
|
||||
registrationId:localRegistrationId
|
||||
prekeyId:items.preKeyId
|
||||
signedPrekeyId:items.signedPreKeyId
|
||||
baseKey:items.baseKey.prependKeyType
|
||||
identityKey:sessionState.localIdentityKey.prependKeyType];
|
||||
[[PreKeyWhisperMessage alloc] initWithWhisperMessage:cipherMessage
|
||||
registrationId:localRegistrationId
|
||||
prekeyId:items.preKeyId
|
||||
signedPrekeyId:items.signedPreKeyId
|
||||
baseKey:items.baseKey.prependKeyType
|
||||
identityKey:sessionState.localIdentityKey.prependKeyType];
|
||||
}
|
||||
|
||||
[sessionState setSenderChainKey:[chainKey nextChainKey]];
|
||||
@ -162,21 +143,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return cipherMessage;
|
||||
}
|
||||
|
||||
- (nullable NSData *)decrypt:(id<CipherMessage>)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError
|
||||
{
|
||||
__block NSData *_Nullable result;
|
||||
[SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
result = [self throws_decrypt:whisperMessage protocolContext:protocolContext];
|
||||
}
|
||||
error:outError];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSData *)throws_decrypt:(id<CipherMessage>)whisperMessage protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (NSData *)decrypt:(id<CipherMessage>)whisperMessage protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(whisperMessage);
|
||||
|
||||
@ -186,33 +153,31 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
OWSFail(@"Unexpected message type: %@", [whisperMessage class]);
|
||||
return nil;
|
||||
}
|
||||
return [self throws_decryptWhisperMessage:(WhisperMessage *)whisperMessage protocolContext:protocolContext];
|
||||
return [self decryptWhisperMessage:(WhisperMessage *)whisperMessage protocolContext:protocolContext];
|
||||
case CipherMessageType_Prekey:
|
||||
if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) {
|
||||
OWSFail(@"Unexpected message type: %@", [whisperMessage class]);
|
||||
return nil;
|
||||
}
|
||||
return [self throws_decryptPreKeyWhisperMessage:(PreKeyWhisperMessage *)whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
return
|
||||
[self decryptPreKeyWhisperMessage:(PreKeyWhisperMessage *)whisperMessage protocolContext:protocolContext];
|
||||
default:
|
||||
OWSFailDebug(@"Unexpected message type: %@", [whisperMessage class]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)throws_decryptPreKeyWhisperMessage:(PreKeyWhisperMessage *)preKeyWhisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (NSData *)decryptPreKeyWhisperMessage:(PreKeyWhisperMessage *)preKeyWhisperMessage
|
||||
protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(preKeyWhisperMessage);
|
||||
|
||||
SessionRecord *sessionRecord =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
|
||||
int unsignedPreKeyId = [self.sessionBuilder throws_processPrekeyWhisperMessage:preKeyWhisperMessage
|
||||
withSession:sessionRecord
|
||||
protocolContext:protocolContext];
|
||||
NSData *plaintext = [self throws_decryptWithSessionRecord:sessionRecord
|
||||
whisperMessage:preKeyWhisperMessage.message
|
||||
protocolContext:protocolContext];
|
||||
int unsignedPreKeyId = [self.sessionBuilder processPrekeyWhisperMessage:preKeyWhisperMessage withSession:sessionRecord protocolContext:protocolContext];
|
||||
NSData *plaintext = [self decryptWithSessionRecord:sessionRecord
|
||||
whisperMessage:preKeyWhisperMessage.message
|
||||
protocolContext:protocolContext];
|
||||
|
||||
[self.sessionStore storeSession:self.recipientId
|
||||
deviceId:self.deviceId
|
||||
@ -221,44 +186,35 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// If there was an unsigned PreKey
|
||||
if (unsignedPreKeyId >= 0) {
|
||||
[self.prekeyStore removePreKey:unsignedPreKeyId
|
||||
protocolContext:protocolContext];
|
||||
[self.prekeyStore removePreKey:unsignedPreKeyId];
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
- (NSData *)throws_decryptWhisperMessage:(WhisperMessage *)whisperMessage protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (NSData *)decryptWhisperMessage:(WhisperMessage *)whisperMessage protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(whisperMessage);
|
||||
|
||||
SessionRecord *sessionRecord =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
|
||||
NSData *plaintext = [self throws_decryptWithSessionRecord:sessionRecord
|
||||
whisperMessage:whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
NSData *plaintext =
|
||||
[self decryptWithSessionRecord:sessionRecord whisperMessage:whisperMessage protocolContext:protocolContext];
|
||||
|
||||
// Our current session state may not have a remote identity key
|
||||
// if we decrypted this message using an old session. It's safe
|
||||
// to ignore this, as the identity key message was already surfaced
|
||||
// when it originally changed.
|
||||
if (sessionRecord.sessionState.remoteIdentityKey) {
|
||||
if (![self.identityKeyStore isTrustedIdentityKey:sessionRecord.sessionState.remoteIdentityKey
|
||||
recipientId:self.recipientId
|
||||
direction:TSMessageDirectionIncoming
|
||||
protocolContext:protocolContext]) {
|
||||
OWSLogWarn(
|
||||
@"%@ Previously known identity key for while decrypting from recipient: %@", self.tag, self.recipientId);
|
||||
@throw [NSException exceptionWithName:UntrustedIdentityKeyException
|
||||
reason:@"There is a previously known identity key."
|
||||
userInfo:@{}];
|
||||
}
|
||||
|
||||
[self.identityKeyStore saveRemoteIdentity:sessionRecord.sessionState.remoteIdentityKey
|
||||
recipientId:self.recipientId
|
||||
protocolContext:protocolContext];
|
||||
if (![self.identityKeyStore isTrustedIdentityKey:sessionRecord.sessionState.remoteIdentityKey
|
||||
recipientId:self.recipientId
|
||||
direction:TSMessageDirectionIncoming
|
||||
protocolContext:protocolContext]) {
|
||||
DDLogWarn(
|
||||
@"%@ Previously known identity key for while decrypting from recipient: %@", self.tag, self.recipientId);
|
||||
@throw [NSException exceptionWithName:UntrustedIdentityKeyException
|
||||
reason:@"There is a previously known identity key."
|
||||
userInfo:@{}];
|
||||
}
|
||||
|
||||
[self.identityKeyStore saveRemoteIdentity:sessionRecord.sessionState.remoteIdentityKey
|
||||
recipientId:self.recipientId
|
||||
protocolContext:protocolContext];
|
||||
[self.sessionStore storeSession:self.recipientId
|
||||
deviceId:self.deviceId
|
||||
session:sessionRecord
|
||||
@ -267,14 +223,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
- (void)logDecryptionFailureForWhisperMessage:(WhisperMessage *)whisperMessage sessionState:(SessionState *)sessionState
|
||||
{
|
||||
OWSFailDebug(@"Failed to decrypt whisper message with ratchet key: %@ and counter: %d. Session loaded using recipientId: %@ and deviceId: %d. Local session has base key: %@ and counter: %d", whisperMessage.senderRatchetKey.hexadecimalString, whisperMessage.counter, self.recipientId, self.deviceId, sessionState.senderRatchetKey.hexadecimalString, sessionState.previousCounter);
|
||||
}
|
||||
|
||||
- (NSData *)throws_decryptWithSessionRecord:(SessionRecord *)sessionRecord
|
||||
whisperMessage:(WhisperMessage *)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (NSData *)decryptWithSessionRecord:(SessionRecord *)sessionRecord
|
||||
whisperMessage:(WhisperMessage *)whisperMessage
|
||||
protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(sessionRecord);
|
||||
OWSAssert(whisperMessage);
|
||||
@ -283,52 +234,53 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
NSMutableArray *exceptions = [NSMutableArray array];
|
||||
|
||||
@try {
|
||||
NSData *decryptedData = [self throws_decryptWithSessionState:sessionState
|
||||
whisperMessage:whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
OWSLogDebug(@"%@ successfully decrypted with current session state: %@", self.tag, sessionState);
|
||||
NSData *decryptedData =
|
||||
[self decryptWithSessionState:sessionState whisperMessage:whisperMessage protocolContext:protocolContext];
|
||||
DDLogDebug(@"%@ successfully decrypted with current session state: %@", self.tag, sessionState);
|
||||
return decryptedData;
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
if ([exception.name isEqualToString:InvalidMessageException]) {
|
||||
[exceptions addObject:exception];
|
||||
} else {
|
||||
[self logDecryptionFailureForWhisperMessage:whisperMessage sessionState:sessionState];
|
||||
@throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
// If we can decrypt the message with an "old" session state, that means the sender is using an "old" session.
|
||||
// We can continue to receive messages from this sender, but we will send to them using our current session.
|
||||
__block NSData *_Nullable decryptedData;
|
||||
// In which case, we promote that session to "active" so as to converge on a single session for sending/receiving.
|
||||
__block NSUInteger stateToPromoteIdx;
|
||||
__block NSData *decryptedData;
|
||||
[[sessionRecord previousSessionStates]
|
||||
enumerateObjectsUsingBlock:^(SessionState *_Nonnull previousState, NSUInteger idx, BOOL *_Nonnull stop) {
|
||||
@try {
|
||||
decryptedData = [self throws_decryptWithSessionState:previousState
|
||||
whisperMessage:whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
OWSLogInfo(@"%@ successfully decrypted with PREVIOUS session state: %@", self.tag, previousState);
|
||||
decryptedData = [self decryptWithSessionState:previousState
|
||||
whisperMessage:whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
DDLogInfo(@"%@ successfully decrypted with PREVIOUS session state: %@", self.tag, previousState);
|
||||
OWSAssert(decryptedData != nil);
|
||||
stateToPromoteIdx = idx;
|
||||
*stop = YES;
|
||||
} @catch (NSException *exception) {
|
||||
if (![exception.name isEqualToString:InvalidMessageException]) {
|
||||
[self logDecryptionFailureForWhisperMessage:whisperMessage sessionState:sessionState];
|
||||
}
|
||||
|
||||
[exceptions addObject:exception];
|
||||
}
|
||||
}];
|
||||
|
||||
if (decryptedData) {
|
||||
SessionState *sessionStateToPromote = [sessionRecord previousSessionStates][stateToPromoteIdx];
|
||||
OWSAssert(sessionStateToPromote != nil);
|
||||
DDLogInfo(@"%@ promoting session: %@", self.tag, sessionStateToPromote);
|
||||
[[sessionRecord previousSessionStates] removeObjectAtIndex:stateToPromoteIdx];
|
||||
[sessionRecord promoteState:sessionStateToPromote];
|
||||
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
BOOL containsActiveSession =
|
||||
[self.sessionStore containsSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
|
||||
OWSLogError(@"%@ No valid session for recipient: %@.%lu containsActiveSession: %@, previousStates: %lu",
|
||||
DDLogError(@"%@ No valid session for recipient: %@ containsActiveSession: %@, previousStates: %lu",
|
||||
self.tag,
|
||||
self.recipientId,
|
||||
(unsigned long) self.deviceId,
|
||||
(containsActiveSession ? @"YES" : @"NO"),
|
||||
(unsigned long)sessionRecord.previousSessionStates.count);
|
||||
|
||||
@ -346,9 +298,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)throws_decryptWithSessionState:(SessionState *)sessionState
|
||||
whisperMessage:(WhisperMessage *)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (NSData *)decryptWithSessionState:(SessionState *)sessionState
|
||||
whisperMessage:(WhisperMessage *)whisperMessage
|
||||
protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(sessionState);
|
||||
OWSAssert(whisperMessage);
|
||||
@ -366,30 +318,28 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
|
||||
int messageVersion = whisperMessage.version;
|
||||
NSData *theirEphemeral = whisperMessage.senderRatchetKey.throws_removeKeyType;
|
||||
NSData *theirEphemeral = whisperMessage.senderRatchetKey.removeKeyType;
|
||||
int counter = whisperMessage.counter;
|
||||
ChainKey *chainKey = [self throws_getOrCreateChainKeys:sessionState theirEphemeral:theirEphemeral];
|
||||
ChainKey *chainKey = [self getOrCreateChainKeys:sessionState theirEphemeral:theirEphemeral];
|
||||
OWSAssert(chainKey);
|
||||
MessageKeys *messageKeys = [self throws_getOrCreateMessageKeysForSession:sessionState
|
||||
theirEphemeral:theirEphemeral
|
||||
chainKey:chainKey
|
||||
counter:counter];
|
||||
MessageKeys *messageKeys = [self getOrCreateMessageKeysForSession:sessionState theirEphemeral:theirEphemeral chainKey:chainKey counter:counter];
|
||||
OWSAssert(messageKeys);
|
||||
|
||||
[whisperMessage throws_verifyMacWithVersion:messageVersion
|
||||
senderIdentityKey:sessionState.remoteIdentityKey
|
||||
receiverIdentityKey:sessionState.localIdentityKey
|
||||
macKey:messageKeys.macKey];
|
||||
[whisperMessage verifyMacWithVersion:messageVersion
|
||||
senderIdentityKey:sessionState.remoteIdentityKey
|
||||
receiverIdentityKey:sessionState.localIdentityKey
|
||||
macKey:messageKeys.macKey];
|
||||
|
||||
NSData *plaintext =
|
||||
[AES_CBC throws_decryptCBCMode:whisperMessage.cipherText withKey:messageKeys.cipherKey withIV:messageKeys.iv];
|
||||
[AES_CBC decryptCBCMode:whisperMessage.cipherText withKey:messageKeys.cipherKey withIV:messageKeys.iv];
|
||||
|
||||
[sessionState clearUnacknowledgedPreKeyMessage];
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
- (ChainKey *)throws_getOrCreateChainKeys:(SessionState *)sessionState theirEphemeral:(NSData *)theirEphemeral
|
||||
- (ChainKey *)getOrCreateChainKeys:(SessionState *)sessionState
|
||||
theirEphemeral:(NSData *)theirEphemeral
|
||||
{
|
||||
OWSAssert(sessionState);
|
||||
OWSGuardWithException(theirEphemeral, InvalidMessageException);
|
||||
@ -397,24 +347,22 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@try {
|
||||
if ([sessionState hasReceiverChain:theirEphemeral]) {
|
||||
OWSLogInfo(@"%@ %@.%d has existing receiver chain.", self.tag, self.recipientId, self.deviceId);
|
||||
DDLogInfo(@"%@ %@.%d has existing receiver chain.", self.tag, self.recipientId, self.deviceId);
|
||||
return [sessionState receiverChainKey:theirEphemeral];
|
||||
} else{
|
||||
OWSLogInfo(@"%@ %@.%d creating new chains.", self.tag, self.recipientId, self.deviceId);
|
||||
DDLogInfo(@"%@ %@.%d creating new chains.", self.tag, self.recipientId, self.deviceId);
|
||||
RootKey *rootKey = [sessionState rootKey];
|
||||
OWSAssert(rootKey.keyData.length == 32);
|
||||
|
||||
ECKeyPair *ourEphemeral = [sessionState senderRatchetKeyPair];
|
||||
OWSAssert(ourEphemeral.publicKey.length == 32);
|
||||
|
||||
RKCK *receiverChain =
|
||||
[rootKey throws_createChainWithTheirEphemeral:theirEphemeral ourEphemeral:ourEphemeral];
|
||||
RKCK *receiverChain = [rootKey createChainWithTheirEphemeral:theirEphemeral ourEphemeral:ourEphemeral];
|
||||
|
||||
ECKeyPair *ourNewEphemeral = [Curve25519 generateKeyPair];
|
||||
OWSAssert(ourNewEphemeral.publicKey.length == 32);
|
||||
|
||||
RKCK *senderChain = [receiverChain.rootKey throws_createChainWithTheirEphemeral:theirEphemeral
|
||||
ourEphemeral:ourNewEphemeral];
|
||||
RKCK *senderChain = [receiverChain.rootKey createChainWithTheirEphemeral:theirEphemeral ourEphemeral:ourNewEphemeral];
|
||||
|
||||
OWSAssert(senderChain.rootKey.keyData.length == 32);
|
||||
[sessionState setRootKey:senderChain.rootKey];
|
||||
@ -435,10 +383,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
}
|
||||
|
||||
- (MessageKeys *)throws_getOrCreateMessageKeysForSession:(SessionState *)sessionState
|
||||
theirEphemeral:(NSData *)theirEphemeral
|
||||
chainKey:(ChainKey *)chainKey
|
||||
counter:(int)counter
|
||||
- (MessageKeys *)getOrCreateMessageKeysForSession:(SessionState *)sessionState
|
||||
theirEphemeral:(NSData *)theirEphemeral
|
||||
chainKey:(ChainKey *)chainKey
|
||||
counter:(int)counter
|
||||
{
|
||||
OWSAssert(sessionState);
|
||||
OWSGuardWithException(theirEphemeral, InvalidMessageException);
|
||||
@ -449,41 +397,39 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
if ([sessionState hasMessageKeys:theirEphemeral counter:counter]) {
|
||||
return [sessionState removeMessageKeys:theirEphemeral counter:counter];
|
||||
} else {
|
||||
OWSLogInfo(
|
||||
DDLogInfo(
|
||||
@"%@ %@.%d Duplicate message for counter: %d", self.tag, self.recipientId, self.deviceId, counter);
|
||||
OWSLogFlush();
|
||||
@throw [NSException exceptionWithName:DuplicateMessageException reason:@"Received message with old counter!" userInfo:@{}];
|
||||
}
|
||||
}
|
||||
|
||||
NSUInteger kCounterLimit = 25000;
|
||||
NSUInteger kCounterLimit = 2000;
|
||||
int counterOffset;
|
||||
if (__builtin_sub_overflow(counter, chainKey.index, &counterOffset)) {
|
||||
OWSFailDebug(@"Overflow while calculating counter offset");
|
||||
OWSRaiseException(InvalidMessageException, @"Overflow while calculating counter offset");
|
||||
}
|
||||
if (counterOffset > kCounterLimit) {
|
||||
OWSLogError(@"%@ %@.%d Exceeded future message limit: %lu, index: %d, counter: %d)",
|
||||
DDLogError(@"%@ %@.%d Exceeded future message limit: %lu, index: %d, counter: %d)",
|
||||
self.tag,
|
||||
self.recipientId,
|
||||
self.deviceId,
|
||||
(unsigned long)kCounterLimit,
|
||||
chainKey.index,
|
||||
counter);
|
||||
OWSLogFlush();
|
||||
@throw [NSException exceptionWithName:InvalidMessageException
|
||||
reason:@"Exceeded message keys chain length limit"
|
||||
userInfo:@{}];
|
||||
}
|
||||
|
||||
while (chainKey.index < counter) {
|
||||
MessageKeys *messageKeys = [chainKey throws_messageKeys];
|
||||
MessageKeys *messageKeys = [chainKey messageKeys];
|
||||
[sessionState setMessageKeys:theirEphemeral messageKeys:messageKeys];
|
||||
chainKey = chainKey.nextChainKey;
|
||||
}
|
||||
|
||||
[sessionState setReceiverChainKey:theirEphemeral chainKey:[chainKey nextChainKey]];
|
||||
return [chainKey throws_messageKeys];
|
||||
return [chainKey messageKeys];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -498,7 +444,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return versionByte;
|
||||
}
|
||||
|
||||
- (int)throws_remoteRegistrationId:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
- (int)remoteRegistrationId:(nullable id)protocolContext
|
||||
{
|
||||
SessionRecord *_Nullable record =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:_deviceId protocolContext:protocolContext];
|
||||
@ -510,7 +456,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return record.sessionState.remoteRegistrationId;
|
||||
}
|
||||
|
||||
- (int)throws_sessionVersion:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
- (int)sessionVersion:(nullable id)protocolContext
|
||||
{
|
||||
SessionRecord *_Nullable record =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:_deviceId protocolContext:protocolContext];
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlStore.h"
|
||||
#import "IdentityKeyStore.h"
|
||||
#import "PreKeyBundle.h"
|
||||
#import "PreKeyStore.h"
|
||||
#import "SPKProtocolContext.h"
|
||||
#import "SessionStore.h"
|
||||
#import "SignedPreKeyStore.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
@ -30,15 +29,11 @@ extern const int kPreKeyOfLastResortId;
|
||||
recipientId:(NSString *)recipientId
|
||||
deviceId:(int)deviceId;
|
||||
|
||||
- (void)throws_processPrekeyBundle:(PreKeyBundle *)preKeyBundle
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (BOOL)processPrekeyBundle:(PreKeyBundle *)preKeyBundle
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError;
|
||||
- (void)processPrekeyBundle:(PreKeyBundle *)preKeyBundle protocolContext:(nullable id)protocolContext;
|
||||
|
||||
- (int)throws_processPrekeyWhisperMessage:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (int)processPrekeyWhisperMessage:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id)protocolContext;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SessionBuilder.h"
|
||||
@ -15,8 +15,6 @@
|
||||
#import "SessionState.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -79,23 +77,12 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)processPrekeyBundle:(PreKeyBundle *)preKeyBundle
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError
|
||||
{
|
||||
return [SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
[self throws_processPrekeyBundle:preKeyBundle protocolContext:protocolContext];
|
||||
}
|
||||
error:outError];
|
||||
}
|
||||
|
||||
- (void)throws_processPrekeyBundle:(PreKeyBundle *)preKeyBundle protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (void)processPrekeyBundle:(PreKeyBundle *)preKeyBundle protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(preKeyBundle);
|
||||
|
||||
NSData *theirIdentityKey = preKeyBundle.identityKey.throws_removeKeyType;
|
||||
NSData *theirSignedPreKey = preKeyBundle.signedPreKeyPublic.throws_removeKeyType;
|
||||
NSData *theirIdentityKey = preKeyBundle.identityKey.removeKeyType;
|
||||
NSData *theirSignedPreKey = preKeyBundle.signedPreKeyPublic.removeKeyType;
|
||||
|
||||
if (![self.identityStore isTrustedIdentityKey:theirIdentityKey
|
||||
recipientId:self.recipientId
|
||||
@ -104,17 +91,14 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
@throw [NSException exceptionWithName:UntrustedIdentityKeyException reason:@"Identity key is not valid" userInfo:@{}];
|
||||
}
|
||||
|
||||
// NOTE: we use preKeyBundle.signedPreKeyPublic which has the key type byte.
|
||||
if (![Ed25519 throws_verifySignature:preKeyBundle.signedPreKeySignature
|
||||
publicKey:theirIdentityKey
|
||||
data:preKeyBundle.signedPreKeyPublic]) {
|
||||
if (![Ed25519 verifySignature:preKeyBundle.signedPreKeySignature publicKey:theirIdentityKey data:preKeyBundle.signedPreKeyPublic]) {
|
||||
@throw [NSException exceptionWithName:InvalidKeyException reason:@"KeyIsNotValidlySigned" userInfo:nil];
|
||||
}
|
||||
|
||||
SessionRecord *sessionRecord =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:preKeyBundle.deviceId protocolContext:protocolContext];
|
||||
ECKeyPair *ourBaseKey = [Curve25519 generateKeyPair];
|
||||
NSData *theirOneTimePreKey = preKeyBundle.preKeyPublic.throws_removeKeyType;
|
||||
NSData *theirOneTimePreKey = preKeyBundle.preKeyPublic.removeKeyType;
|
||||
int theirOneTimePreKeyId = preKeyBundle.preKeyId;
|
||||
int theirSignedPreKeyId = preKeyBundle.signedPreKeyId;
|
||||
|
||||
@ -130,10 +114,8 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
if (!sessionRecord.isFresh) {
|
||||
[sessionRecord archiveCurrentState];
|
||||
}
|
||||
|
||||
[RatchetingSession throws_initializeSession:[sessionRecord sessionState]
|
||||
sessionVersion:CURRENT_VERSION
|
||||
AliceParameters:params];
|
||||
|
||||
[RatchetingSession initializeSession:[sessionRecord sessionState] sessionVersion:CURRENT_VERSION AliceParameters:params];
|
||||
|
||||
DDLogInfo(@"setUnacknowledgedPreKeyMessage for: %@ with preKeyId: %d", self.recipientId, theirOneTimePreKeyId);
|
||||
|
||||
@ -146,6 +128,12 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
BOOL previousIdentityExisted = [self.identityStore saveRemoteIdentity:theirIdentityKey
|
||||
recipientId:self.recipientId
|
||||
protocolContext:protocolContext];
|
||||
if (previousIdentityExisted) {
|
||||
DDLogInfo(@"%@ PKBundle removing previous session states for changed identity for recipient:%@",
|
||||
self.logTag,
|
||||
self.recipientId);
|
||||
[sessionRecord removePreviousSessionStates];
|
||||
}
|
||||
|
||||
[self.sessionStore storeSession:self.recipientId
|
||||
deviceId:self.deviceId
|
||||
@ -153,15 +141,15 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
protocolContext:protocolContext];
|
||||
}
|
||||
|
||||
- (int)throws_processPrekeyWhisperMessage:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (int)processPrekeyWhisperMessage:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(message);
|
||||
OWSAssert(sessionRecord);
|
||||
|
||||
int messageVersion = message.version;
|
||||
NSData *theirIdentityKey = message.identityKey.throws_removeKeyType;
|
||||
NSData *theirIdentityKey = message.identityKey.removeKeyType;
|
||||
|
||||
if (![self.identityStore isTrustedIdentityKey:theirIdentityKey
|
||||
recipientId:self.recipientId
|
||||
@ -174,8 +162,7 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
|
||||
switch (messageVersion) {
|
||||
case 3:
|
||||
unSignedPrekeyId =
|
||||
[self throws_processPrekeyV3:message withSession:sessionRecord protocolContext:protocolContext];
|
||||
unSignedPrekeyId = [self processPrekeyV3:message withSession:sessionRecord protocolContext:protocolContext];
|
||||
break;
|
||||
default:
|
||||
@throw [NSException exceptionWithName:InvalidVersionException reason:@"Trying to initialize with unknown version" userInfo:@{}];
|
||||
@ -189,48 +176,31 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
return unSignedPrekeyId;
|
||||
}
|
||||
|
||||
- (int)throws_processPrekeyV3:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
- (int)processPrekeyV3:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id)protocolContext
|
||||
{
|
||||
OWSAssert(message);
|
||||
OWSAssert(sessionRecord);
|
||||
|
||||
NSData *baseKey = message.baseKey.throws_removeKeyType;
|
||||
|
||||
NSData *baseKey = message.baseKey.removeKeyType;
|
||||
|
||||
if ([sessionRecord hasSessionState:message.version baseKey:baseKey]) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SignedPreKeyRecord *_Nullable signedPreKeyRecord = [self.signedPreKeyStore loadSignedPreKey:message.signedPrekeyId
|
||||
protocolContext:protocolContext];
|
||||
if (signedPreKeyRecord == nil) {
|
||||
OWSLogWarn(@"Signed prekey id: %lu, prekey id: %lu.",
|
||||
(unsigned long) message.signedPrekeyId,
|
||||
(unsigned long) message.prekeyID);
|
||||
OWSLogWarn(@"Available signed prekey ids: %@",
|
||||
[self.signedPreKeyStore availableSignedPreKeyIdsWithProtocolContext:protocolContext]);
|
||||
|
||||
OWSRaiseException(InvalidKeyIdException, @"No signed prekey found matching key id");
|
||||
}
|
||||
ECKeyPair *ourSignedPrekey = signedPreKeyRecord.keyPair;
|
||||
|
||||
ECKeyPair *ourSignedPrekey = [self.signedPreKeyStore loadSignedPrekey:message.signedPrekeyId].keyPair;
|
||||
|
||||
ECKeyPair *_Nullable ourOneTimePreKey;
|
||||
if (message.prekeyID >= 0) {
|
||||
PreKeyRecord *_Nullable preKey = [self.prekeyStore loadPreKey:message.prekeyID
|
||||
protocolContext:protocolContext];
|
||||
if (preKey == nil) {
|
||||
OWSFailDebug(@"Missing prekeyID: %lu", (unsigned long) message.prekeyID);
|
||||
OWSRaiseException(InvalidKeyIdException, @"No pre key found matching key id");
|
||||
}
|
||||
ourOneTimePreKey = preKey.keyPair;
|
||||
ourOneTimePreKey = [self.prekeyStore loadPreKey:message.prekeyID].keyPair;
|
||||
} else {
|
||||
DDLogWarn(@"%@ Processing PreKey message which had no one-time prekey.", self.tag);
|
||||
DDLogWarn(@"%@ Processing PreKey message which had no one-time prekey.", self.logTag);
|
||||
}
|
||||
|
||||
BobAxolotlParameters *params =
|
||||
[[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:[self.identityStore identityKeyPair:protocolContext]
|
||||
theirIdentityKey:message.identityKey.throws_removeKeyType
|
||||
theirIdentityKey:message.identityKey.removeKeyType
|
||||
ourSignedPrekey:ourSignedPrekey
|
||||
ourRatchetKey:ourSignedPrekey
|
||||
ourOneTimePrekey:ourOneTimePreKey
|
||||
@ -239,11 +209,9 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
if (!sessionRecord.isFresh) {
|
||||
[sessionRecord archiveCurrentState];
|
||||
}
|
||||
|
||||
[RatchetingSession throws_initializeSession:sessionRecord.sessionState
|
||||
sessionVersion:message.version
|
||||
BobParameters:params];
|
||||
|
||||
|
||||
[RatchetingSession initializeSession:sessionRecord.sessionState sessionVersion:message.version BobParameters:params];
|
||||
|
||||
[sessionRecord.sessionState setLocalRegistrationId:[self.identityStore localRegistrationId:protocolContext]];
|
||||
[sessionRecord.sessionState setRemoteRegistrationId:message.registrationId];
|
||||
[sessionRecord.sessionState setAliceBaseKey:baseKey];
|
||||
@ -256,18 +224,6 @@ const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -11,11 +11,13 @@
|
||||
|
||||
- (BOOL)hasSessionState:(int)version baseKey:(NSData*)aliceBaseKey;
|
||||
- (SessionState*)sessionState;
|
||||
- (NSArray<SessionState *> *)previousSessionStates;
|
||||
- (NSMutableArray<SessionState *> *)previousSessionStates;
|
||||
|
||||
- (void)removePreviousSessionStates;
|
||||
- (BOOL)isFresh;
|
||||
- (void)markAsUnFresh;
|
||||
- (void)archiveCurrentState;
|
||||
- (void)promoteState:(SessionState*)promotedState;
|
||||
- (void)setState:(SessionState*)sessionState;
|
||||
|
||||
@end
|
||||
|
||||
@ -71,11 +71,16 @@
|
||||
return _sessionState;
|
||||
}
|
||||
|
||||
- (NSArray<SessionState *> *)previousSessionStates
|
||||
- (NSMutableArray<SessionState *> *)previousSessionStates
|
||||
{
|
||||
return _previousStates;
|
||||
}
|
||||
|
||||
- (void)removePreviousSessionStates
|
||||
{
|
||||
[_previousStates removeAllObjects];
|
||||
}
|
||||
|
||||
- (BOOL)isFresh{
|
||||
return _fresh;
|
||||
}
|
||||
@ -86,10 +91,6 @@
|
||||
}
|
||||
|
||||
- (void)archiveCurrentState{
|
||||
if (self.sessionState.isFresh) {
|
||||
OWSLogInfo(@"Skipping archive, current session state is fresh.");
|
||||
return;
|
||||
}
|
||||
[self promoteState:[SessionState new]];
|
||||
}
|
||||
|
||||
@ -102,7 +103,7 @@
|
||||
ows_sub_overflow(self.previousStates.count, ARCHIVED_STATES_MAX_LENGTH, &deleteCount);
|
||||
NSIndexSet *indexesToDelete =
|
||||
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(ARCHIVED_STATES_MAX_LENGTH, deleteCount)];
|
||||
[self.previousStates removeObjectsAtIndexes:indexesToDelete];
|
||||
[self.previousSessionStates removeObjectsAtIndexes:indexesToDelete];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -39,8 +39,6 @@
|
||||
@property(nonatomic)int remoteRegistrationId;
|
||||
@property(nonatomic)int localRegistrationId;
|
||||
|
||||
@property (nonatomic, readonly) BOOL isFresh;
|
||||
|
||||
- (NSData*)senderRatchetKey;
|
||||
- (ECKeyPair*)senderRatchetKeyPair;
|
||||
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import "SessionState.h"
|
||||
#import "ChainAndIndex.h"
|
||||
#import "ReceivingChain.h"
|
||||
#import "SendingChain.h"
|
||||
#import "ChainAndIndex.h"
|
||||
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
@implementation PendingPreKey
|
||||
|
||||
@ -119,11 +118,6 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
[aCoder encodeObject:self.pendingPreKey forKey:kCoderPendingPrekey];
|
||||
}
|
||||
|
||||
- (BOOL)isFresh
|
||||
{
|
||||
return self.remoteIdentityKey == nil && self.localIdentityKey == nil && self.sendingChain == nil && self.receivingChains.count == 0 && self.pendingPreKey == nil;
|
||||
}
|
||||
|
||||
- (NSData*)senderRatchetKey{
|
||||
return [[self senderRatchetKeyPair] publicKey];
|
||||
}
|
||||
@ -197,7 +191,7 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
|
||||
if ([self.receivingChains count] > 5) {
|
||||
DDLogInfo(
|
||||
@"%@ Trimming excessive receivingChain count: %lu", self.tag, (unsigned long)self.receivingChains.count);
|
||||
@"%@ Trimming excessive receivingChain count: %lu", self.logTag, (unsigned long)self.receivingChains.count);
|
||||
// We keep 5 receiving chains to be able to decrypt out of order messages.
|
||||
[self.receivingChains removeObjectAtIndex:0];
|
||||
}
|
||||
@ -303,16 +297,4 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
self.pendingPreKey = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SPKProtocolContext.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@ -18,9 +17,9 @@ typedef NS_ENUM(NSInteger, TSMessageDirection) {
|
||||
// See a discussion of the protocolContext in SessionCipher.h.
|
||||
@protocol IdentityKeyStore <NSObject>
|
||||
|
||||
- (nullable ECKeyPair *)identityKeyPair:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
- (nullable ECKeyPair *)identityKeyPair:(nullable id)protocolContext;
|
||||
|
||||
- (int)localRegistrationId:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
- (int)localRegistrationId:(nullable id)protocolContext;
|
||||
|
||||
/**
|
||||
* Record a recipients identity key
|
||||
@ -33,7 +32,7 @@ typedef NS_ENUM(NSInteger, TSMessageDirection) {
|
||||
*/
|
||||
- (BOOL)saveRemoteIdentity:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
protocolContext:(nullable id)protocolContext;
|
||||
|
||||
/**
|
||||
* @param identityKey key data used to identify the recipient
|
||||
@ -47,12 +46,11 @@ typedef NS_ENUM(NSInteger, TSMessageDirection) {
|
||||
- (BOOL)isTrustedIdentityKey:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
direction:(TSMessageDirection)direction
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
protocolContext:(nullable id)protocolContext;
|
||||
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId;
|
||||
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId protocolContext:(nullable id)protocolContext;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PreKeyRecord.h"
|
||||
@ -9,14 +9,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol PreKeyStore <NSObject>
|
||||
|
||||
- (nullable PreKeyRecord *)loadPreKey:(int)preKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
- (PreKeyRecord *)loadPreKey:(int)preKeyId;
|
||||
|
||||
- (void)storePreKey:(int)preKeyId preKeyRecord:(PreKeyRecord *)record
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
- (void)storePreKey:(int)preKeyId preKeyRecord:(PreKeyRecord *)record;
|
||||
|
||||
- (void)removePreKey:(int)preKeyId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
- (BOOL)containsPreKey:(int)preKeyId;
|
||||
|
||||
- (void)removePreKey:(int)preKeyId;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SPKProtocolContext.h"
|
||||
#import "SessionRecord.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@ -21,24 +20,24 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (SessionRecord *)loadSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
protocolContext:(nullable id)protocolContext;
|
||||
|
||||
- (NSArray *)subDevicesSessions:(NSString *)contactIdentifier protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext __attribute__((deprecated));
|
||||
- (NSArray *)subDevicesSessions:(NSString *)contactIdentifier protocolContext:(nullable id)protocolContext __attribute__((deprecated));
|
||||
|
||||
- (void)storeSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
session:(SessionRecord *)session
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
protocolContext:(nullable id)protocolContext;
|
||||
|
||||
- (BOOL)containsSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
protocolContext:(nullable id)protocolContext;
|
||||
|
||||
- (void)deleteSessionForContact:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
protocolContext:(nullable id)protocolContext;
|
||||
|
||||
- (void)deleteAllSessionsForContact:(NSString *)contactIdentifier protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
- (void)deleteAllSessionsForContact:(NSString *)contactIdentifier protocolContext:(nullable id)protocolContext;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SignedPrekeyRecord.h"
|
||||
@ -9,22 +9,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol SignedPreKeyStore <NSObject>
|
||||
|
||||
- (nullable SignedPreKeyRecord *)loadSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
- (SignedPreKeyRecord *)loadSignedPrekey:(int)signedPreKeyId;
|
||||
|
||||
- (NSArray<SignedPreKeyRecord *> *)loadSignedPreKeysWithProtocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
- (nullable SignedPreKeyRecord *)loadSignedPrekeyOrNil:(int)signedPreKeyId;
|
||||
|
||||
- (void)storeSignedPreKey:(int)signedPreKeyId
|
||||
signedPreKeyRecord:(SignedPreKeyRecord *)signedPreKeyRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
- (NSArray<SignedPreKeyRecord *> *)loadSignedPreKeys;
|
||||
|
||||
- (BOOL)containsSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
- (void)storeSignedPreKey:(int)signedPreKeyId signedPreKeyRecord:(SignedPreKeyRecord *)signedPreKeyRecord;
|
||||
|
||||
- (void)removeSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
- (BOOL)containsSignedPreKey:(int)signedPreKeyId;
|
||||
|
||||
- (NSArray<NSString *> *)availableSignedPreKeyIdsWithProtocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
- (void)removeSignedPreKey:(int)signedPrekeyId;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlStore.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SPKMockProtocolStore : NSObject <AxolotlStore>
|
||||
|
||||
- (instancetype)initWithIdentityKeyPair:(ECKeyPair *)identityKeyPair
|
||||
localRegistrationId:(int)localRegistrationId NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,264 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SPKMockProtocolStore.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SPKMockProtocolStore ()
|
||||
|
||||
@property NSMutableDictionary<NSString *, NSMutableDictionary *> *sessionRecords;
|
||||
|
||||
// Signed PreKey Store
|
||||
|
||||
@property NSMutableDictionary *preKeyStore;
|
||||
@property NSMutableDictionary *signedPreKeyStore;
|
||||
|
||||
@property NSMutableDictionary *trustedKeys;
|
||||
|
||||
@property ECKeyPair *identityKeyPair;
|
||||
@property int localRegistrationId;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SPKMockProtocolStore
|
||||
|
||||
|
||||
#pragma mark General
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithIdentityKeyPair:[Curve25519 generateKeyPair] localRegistrationId:arc4random() % 16380];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentityKeyPair:(ECKeyPair *)identityKeyPair localRegistrationId:(int)localRegistrationId
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_identityKeyPair = identityKeyPair;
|
||||
_localRegistrationId = localRegistrationId;
|
||||
|
||||
_preKeyStore = [NSMutableDictionary dictionary];
|
||||
_signedPreKeyStore = [NSMutableDictionary dictionary];
|
||||
_trustedKeys = [NSMutableDictionary dictionary];
|
||||
_sessionRecords = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Signed PreKey Store
|
||||
|
||||
- (nullable SignedPreKeyRecord *)loadSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
return [self.signedPreKeyStore objectForKey:[NSNumber numberWithInt:signedPreKeyId]];
|
||||
}
|
||||
|
||||
- (NSArray<SignedPreKeyRecord *> *)loadSignedPreKeysWithProtocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
for (SignedPreKeyRecord *signedPrekey in [self.signedPreKeyStore allValues]) {
|
||||
[results addObject:signedPrekey];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)availableSignedPreKeyIdsWithProtocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
return @[];
|
||||
}
|
||||
|
||||
- (void)storeSignedPreKey:(int)signedPreKeyId
|
||||
signedPreKeyRecord:(SignedPreKeyRecord *)signedPreKeyRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.signedPreKeyStore setObject:signedPreKeyRecord forKey:[NSNumber numberWithInteger:signedPreKeyId]];
|
||||
}
|
||||
|
||||
- (BOOL)containsSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
if ([[self.signedPreKeyStore allKeys] containsObject:[NSNumber numberWithInteger:signedPreKeyId]]) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
- (void)removeSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.signedPreKeyStore removeObjectForKey:[NSNumber numberWithInteger:signedPreKeyId]];
|
||||
}
|
||||
|
||||
#pragma mark PreKey Store
|
||||
|
||||
- (nullable PreKeyRecord *)loadPreKey:(int)preKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
{
|
||||
return [self.preKeyStore objectForKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
- (NSArray *)loadPreKeys
|
||||
{
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
for (PreKeyRecord *prekey in [self.preKeyStore allValues]) {
|
||||
[results addObject:prekey];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
- (void)storePreKey:(int)preKeyId preKeyRecord:(PreKeyRecord *)record
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.preKeyStore setObject:record forKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
- (void)removePreKey:(int)preKeyId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.preKeyStore removeObjectForKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
#pragma mark IdentityKeyStore
|
||||
|
||||
- (nullable ECKeyPair *)identityKeyPair:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
return self.identityKeyPair;
|
||||
}
|
||||
|
||||
- (int)localRegistrationId:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
return self.localRegistrationId;
|
||||
}
|
||||
|
||||
- (BOOL)saveRemoteIdentity:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
NSData *existingKey = [self.trustedKeys objectForKey:recipientId];
|
||||
|
||||
if ([existingKey isEqualToData:existingKey]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self.trustedKeys setObject:identityKey forKey:recipientId];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isTrustedIdentityKey:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
direction:(TSMessageDirection)direction
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
NSData *data = [self.trustedKeys objectForKey:recipientId];
|
||||
if (!data) {
|
||||
// Trust on first use
|
||||
return YES;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case TSMessageDirectionIncoming:
|
||||
return YES;
|
||||
case TSMessageDirectionOutgoing:
|
||||
// In a real implementation you may wish to ensure the use has been properly notified of any
|
||||
// recent identity change before sending outgoing messages.
|
||||
return [data isEqualToData:identityKey];
|
||||
case TSMessageDirectionUnknown:
|
||||
NSAssert(NO, @"unknown message direction");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId
|
||||
{
|
||||
return [self identityKeyForRecipientId:recipientId protocolContext:nil];
|
||||
}
|
||||
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
NSData *_Nullable data = [self.trustedKeys objectForKey:recipientId];
|
||||
return data;
|
||||
}
|
||||
|
||||
#pragma mark Session Store
|
||||
|
||||
- (SessionRecord *)loadSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
SessionRecord *sessionRecord = [[self deviceSessionRecordsForContactIdentifier:contactIdentifier]
|
||||
objectForKey:[NSNumber numberWithInteger:deviceId]];
|
||||
|
||||
if (!sessionRecord) {
|
||||
sessionRecord = [SessionRecord new];
|
||||
}
|
||||
|
||||
return sessionRecord;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
|
||||
- (NSArray *)subDevicesSessions:(NSString *)contactIdentifier protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
return [[self deviceSessionRecordsForContactIdentifier:contactIdentifier] allKeys];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (NSMutableDictionary *)deviceSessionRecordsForContactIdentifier:(NSString *)contactIdentifier
|
||||
{
|
||||
return [self.sessionRecords objectForKey:contactIdentifier];
|
||||
}
|
||||
|
||||
- (void)storeSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
session:(SessionRecord *)session
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
NSAssert(session, @"Session can't be nil");
|
||||
NSMutableDictionary *deviceSessions = self.sessionRecords[contactIdentifier];
|
||||
if (!deviceSessions) {
|
||||
deviceSessions = [NSMutableDictionary new];
|
||||
}
|
||||
deviceSessions[@(deviceId)] = session;
|
||||
|
||||
self.sessionRecords[contactIdentifier] = deviceSessions;
|
||||
}
|
||||
|
||||
- (BOOL)containsSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
|
||||
if ([[self.sessionRecords objectForKey:contactIdentifier] objectForKey:[NSNumber numberWithInt:deviceId]]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)deleteSessionForContact:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
NSMutableDictionary<NSNumber *, SessionRecord *> *sessions =
|
||||
[self deviceSessionRecordsForContactIdentifier:contactIdentifier];
|
||||
[sessions removeObjectForKey:@(deviceId)];
|
||||
}
|
||||
|
||||
- (void)deleteAllSessionsForContact:(NSString *)contactIdentifier protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.sessionRecords removeObjectForKey:contactIdentifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,18 +1,16 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// NSData+keyVersionByte.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSData (keyVersionByte)
|
||||
|
||||
- (instancetype)prependKeyType;
|
||||
|
||||
- (instancetype)throws_removeKeyType NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable instancetype)removeKeyTypeAndReturnError:(NSError **)outError;
|
||||
- (instancetype)removeKeyType;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// NSData+keyVersionByte.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSData+keyVersionByte.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#import "NSData+keyVersionByte.h"
|
||||
|
||||
@implementation NSData (keyVersionByte)
|
||||
|
||||
@ -23,18 +24,7 @@ const Byte DJB_TYPE = 0x05;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)removeKeyTypeAndReturnError:(NSError **)outError
|
||||
{
|
||||
@try {
|
||||
return self.throws_removeKeyType;
|
||||
} @catch (NSException *exception) {
|
||||
*outError = SCKExceptionWrapperErrorMake(exception);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)throws_removeKeyType
|
||||
{
|
||||
- (instancetype)removeKeyType {
|
||||
if (self.length == 33) {
|
||||
if ([[self subdataWithRange:NSMakeRange(0, 1)] isEqualToData:[NSData dataWithBytes:&DJB_TYPE length:1]]) {
|
||||
return [self subdataWithRange:NSMakeRange(1, 32)];
|
||||
@ -48,5 +38,3 @@ const Byte DJB_TYPE = 0x05;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol SPKProtocolReadContext <NSObject>
|
||||
|
||||
@end
|
||||
|
||||
@protocol SPKProtocolWriteContext <SPKProtocolReadContext>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@ -16,11 +16,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
+ (Byte)intsToByteHigh:(int)highValue low:(int)lowValue;
|
||||
|
||||
+ (NSData *)throws_macWithVersion:(int)version
|
||||
identityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
serialized:(NSData *)serialized NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (NSData *)macWithVersion:(int)version
|
||||
identityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
serialized:(NSData *)serialized;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SerializationUtilities.h"
|
||||
@ -24,11 +24,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return (Byte)((highValue << 4 | lowValue) & 0xFF);
|
||||
}
|
||||
|
||||
+ (NSData *)throws_macWithVersion:(int)version
|
||||
identityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
serialized:(NSData *)serialized
|
||||
+ (NSData *)macWithVersion:(int)version
|
||||
identityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
serialized:(NSData *)serialized
|
||||
{
|
||||
if (!macKey) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Missing macKey." userInfo:nil];
|
||||
|
||||
15
AxolotlKitTests/AxolotlInMemoryStore.h
Normal file
15
AxolotlKitTests/AxolotlInMemoryStore.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// AxolotlInMemoryStore.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 17/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "AxolotlStore.h"
|
||||
|
||||
@interface AxolotlInMemoryStore : NSObject <AxolotlStore>
|
||||
|
||||
@end
|
||||
230
AxolotlKitTests/AxolotlInMemoryStore.m
Normal file
230
AxolotlKitTests/AxolotlInMemoryStore.m
Normal file
@ -0,0 +1,230 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlInMemoryStore.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AxolotlInMemoryStore ()
|
||||
|
||||
@property NSMutableDictionary<NSString *, NSMutableDictionary *> *sessionRecords;
|
||||
|
||||
// Signed PreKey Store
|
||||
|
||||
@property NSMutableDictionary *preKeyStore;
|
||||
@property NSMutableDictionary *signedPreKeyStore;
|
||||
|
||||
@property NSMutableDictionary *trustedKeys;
|
||||
|
||||
@property ECKeyPair *_identityKeyPair;
|
||||
@property int _localRegistrationId;
|
||||
|
||||
@end
|
||||
|
||||
@implementation AxolotlInMemoryStore
|
||||
|
||||
|
||||
# pragma mark General
|
||||
|
||||
- (instancetype)init{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
self._identityKeyPair = [Curve25519 generateKeyPair];
|
||||
self._localRegistrationId = arc4random() % 16380;
|
||||
|
||||
_preKeyStore = [NSMutableDictionary dictionary];
|
||||
_signedPreKeyStore = [NSMutableDictionary dictionary];
|
||||
_trustedKeys = [NSMutableDictionary dictionary];
|
||||
_sessionRecords = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
# pragma mark Signed PreKey Store
|
||||
|
||||
- (SignedPreKeyRecord *)loadSignedPrekey:(int)signedPreKeyId{
|
||||
if (![[self.signedPreKeyStore allKeys] containsObject:[NSNumber numberWithInt:signedPreKeyId]]) {
|
||||
@throw [NSException exceptionWithName:InvalidKeyIdException reason:@"No such signedprekeyrecord" userInfo:nil];
|
||||
}
|
||||
|
||||
return [self.signedPreKeyStore objectForKey:[NSNumber numberWithInt:signedPreKeyId]];
|
||||
}
|
||||
|
||||
- (nullable SignedPreKeyRecord *)loadSignedPrekeyOrNil:(int)signedPreKeyId
|
||||
{
|
||||
if ([self containsSignedPreKey:signedPreKeyId]) {
|
||||
return [self loadSignedPrekey:signedPreKeyId];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<SignedPreKeyRecord *> *)loadSignedPreKeys
|
||||
{
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
for (SignedPreKeyRecord *signedPrekey in [self.signedPreKeyStore allValues]) {
|
||||
[results addObject:signedPrekey];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
- (void)storeSignedPreKey:(int)signedPreKeyId signedPreKeyRecord:(SignedPreKeyRecord *)signedPreKeyRecord{
|
||||
[self.signedPreKeyStore setObject:signedPreKeyRecord forKey:[NSNumber numberWithInteger:signedPreKeyId]];
|
||||
}
|
||||
|
||||
- (BOOL)containsSignedPreKey:(int)signedPreKeyId{
|
||||
if ([[self.signedPreKeyStore allKeys] containsObject:[NSNumber numberWithInteger:signedPreKeyId]]) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
- (void)removeSignedPreKey:(int)signedPrekeyId{
|
||||
[self.signedPreKeyStore removeObjectForKey:[NSNumber numberWithInteger:signedPrekeyId]];
|
||||
}
|
||||
|
||||
# pragma mark PreKey Store
|
||||
|
||||
- (PreKeyRecord *)loadPreKey:(int)preKeyId{
|
||||
if (![[self.preKeyStore allKeys] containsObject:[NSNumber numberWithInt:preKeyId]]) {
|
||||
@throw [NSException exceptionWithName:InvalidKeyIdException reason:@"No such signedprekeyrecord" userInfo:nil];
|
||||
}
|
||||
|
||||
return [self.preKeyStore objectForKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
- (NSArray *)loadPreKeys{
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
for (PreKeyRecord *prekey in [self.preKeyStore allValues]) {
|
||||
[results addObject:prekey];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
- (void)storePreKey:(int)preKeyId preKeyRecord:(PreKeyRecord *)record{
|
||||
[self.preKeyStore setObject:record forKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
- (BOOL)containsPreKey:(int)preKeyId{
|
||||
if ([[self.preKeyStore allKeys] containsObject:[NSNumber numberWithInteger:preKeyId]]) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
- (void)removePreKey:(int)preKeyId{
|
||||
[self.preKeyStore removeObjectForKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
# pragma mark IdentityKeyStore
|
||||
|
||||
- (nullable ECKeyPair *)identityKeyPair
|
||||
{
|
||||
return __identityKeyPair;
|
||||
}
|
||||
|
||||
- (int)localRegistrationId{
|
||||
return __localRegistrationId;
|
||||
}
|
||||
|
||||
- (BOOL)saveRemoteIdentity:(NSData *)identityKey recipientId:(NSString *)recipientId
|
||||
{
|
||||
NSData *existingKey = [self.trustedKeys objectForKey:recipientId];
|
||||
|
||||
if ([existingKey isEqualToData:existingKey]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self.trustedKeys setObject:identityKey forKey:recipientId];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isTrustedIdentityKey:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
direction:(TSMessageDirection)direction
|
||||
{
|
||||
|
||||
NSData *data = [self.trustedKeys objectForKey:recipientId];
|
||||
if (!data) {
|
||||
// Trust on first use
|
||||
return YES;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case TSMessageDirectionIncoming:
|
||||
return YES;
|
||||
case TSMessageDirectionOutgoing:
|
||||
// In a real implementation you may wish to ensure the use has been properly notified of any
|
||||
// recent identity change before sending outgoing messages.
|
||||
return [data isEqualToData:identityKey];
|
||||
case TSMessageDirectionUnknown:
|
||||
NSAssert(NO, @"unknown message direction");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
# pragma mark Session Store
|
||||
|
||||
-(SessionRecord*)loadSession:(NSString*)contactIdentifier deviceId:(int)deviceId{
|
||||
SessionRecord *sessionRecord = [[self deviceSessionRecordsForContactIdentifier:contactIdentifier] objectForKey:[NSNumber numberWithInteger:deviceId]];
|
||||
|
||||
if (!sessionRecord) {
|
||||
sessionRecord = [SessionRecord new];
|
||||
}
|
||||
|
||||
return sessionRecord;
|
||||
}
|
||||
|
||||
- (NSArray*)subDevicesSessions:(NSString*)contactIdentifier{
|
||||
return [[self deviceSessionRecordsForContactIdentifier:contactIdentifier] allKeys];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)deviceSessionRecordsForContactIdentifier:(NSString *)contactIdentifier
|
||||
{
|
||||
return [self.sessionRecords objectForKey:contactIdentifier];
|
||||
}
|
||||
|
||||
- (void)storeSession:(NSString*)contactIdentifier deviceId:(int)deviceId session:(SessionRecord *)session{
|
||||
NSAssert(session, @"Session can't be nil");
|
||||
NSMutableDictionary *deviceSessions = self.sessionRecords[contactIdentifier];
|
||||
if (!deviceSessions) {
|
||||
deviceSessions = [NSMutableDictionary new];
|
||||
}
|
||||
deviceSessions[@(deviceId)] = session;
|
||||
|
||||
self.sessionRecords[contactIdentifier] = deviceSessions;
|
||||
}
|
||||
|
||||
- (BOOL)containsSession:(NSString*)contactIdentifier deviceId:(int)deviceId{
|
||||
|
||||
if ([[self.sessionRecords objectForKey:contactIdentifier] objectForKey:[NSNumber numberWithInt:deviceId]]){
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)deleteSessionForContact:(NSString *)contactIdentifier deviceId:(int)deviceId
|
||||
{
|
||||
NSMutableDictionary<NSNumber *, SessionRecord *> *sessions =
|
||||
[self deviceSessionRecordsForContactIdentifier:contactIdentifier];
|
||||
[sessions removeObjectForKey:@(deviceId)];
|
||||
}
|
||||
|
||||
- (void)deleteAllSessionsForContact:(NSString *)contactIdentifier
|
||||
{
|
||||
[self.sessionRecords removeObjectForKey:contactIdentifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
17
AxolotlKitTests/AxolotlKit_Tests.m
Normal file
17
AxolotlKitTests/AxolotlKit_Tests.m
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// AxolotlKit_Tests.m
|
||||
// AxolotlKit Tests
|
||||
//
|
||||
// Created by Frederic Jacobs on 21/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface AxolotlKit_Tests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation AxolotlKit_Tests
|
||||
|
||||
@end
|
||||
@ -1,11 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "ECKeyPair+ECKeyPairTesting.h"
|
||||
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface ECCTests : XCTestCase
|
||||
|
||||
@ -73,15 +75,13 @@
|
||||
(Byte) 0xe6, (Byte) 0x29};
|
||||
|
||||
NSData *sharedSecret = [NSData dataWithBytes:sharedBytes length:32];
|
||||
|
||||
ECKeyPair *aliceKeyPair = [ECKeyPair throws_keyPairWithPrivateKey:alicePrivateKey publicKey:alicePublicKey];
|
||||
ECKeyPair *bobKeyPair = [ECKeyPair throws_keyPairWithPrivateKey:bobPrivateKey publicKey:bobPublicKey];
|
||||
|
||||
NSData *aliceShared =
|
||||
[Curve25519 throws_generateSharedSecretFromPublicKey:[bobKeyPair publicKey] andKeyPair:aliceKeyPair];
|
||||
NSData *bobShared =
|
||||
[Curve25519 throws_generateSharedSecretFromPublicKey:[aliceKeyPair publicKey] andKeyPair:bobKeyPair];
|
||||
|
||||
|
||||
ECKeyPair *aliceKeyPair = [ECKeyPair keyPairWithPrivateKey:alicePrivateKey publicKey:alicePublicKey];
|
||||
ECKeyPair *bobKeyPair = [ECKeyPair keyPairWithPrivateKey:bobPrivateKey publicKey:bobPublicKey];
|
||||
|
||||
NSData *aliceShared = [Curve25519 generateSharedSecretFromPublicKey:[bobKeyPair publicKey] andKeyPair:aliceKeyPair];
|
||||
NSData *bobShared = [Curve25519 generateSharedSecretFromPublicKey:[aliceKeyPair publicKey] andKeyPair:bobKeyPair];
|
||||
|
||||
XCTAssert([aliceShared isEqualToData:sharedSecret], @"Alice's shared secret is equal to the expected one.");
|
||||
XCTAssert([bobShared isEqualToData:sharedSecret], @"Bob's shared secret is equal to the expected one.");
|
||||
}
|
||||
@ -90,11 +90,8 @@
|
||||
for (int i=0;i<100;i++) {
|
||||
ECKeyPair *aliceKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobKeyPair = [Curve25519 generateKeyPair];
|
||||
|
||||
XCTAssert([[Curve25519 throws_generateSharedSecretFromPublicKey:[aliceKeyPair publicKey] andKeyPair:bobKeyPair]
|
||||
isEqualToData:[Curve25519 throws_generateSharedSecretFromPublicKey:[bobKeyPair publicKey]
|
||||
andKeyPair:aliceKeyPair]],
|
||||
@"Randomly generated keypairs produce same shared secret.");
|
||||
|
||||
XCTAssert([[Curve25519 generateSharedSecretFromPublicKey:[aliceKeyPair publicKey] andKeyPair:bobKeyPair] isEqualToData:[Curve25519 generateSharedSecretFromPublicKey:[bobKeyPair publicKey] andKeyPair:aliceKeyPair]], @"Randomly generated keypairs produce same shared secret.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,11 +140,11 @@
|
||||
NSData *ephemPublic = [NSData dataWithBytes:aliceEphemeralPublic length:33];
|
||||
|
||||
NSData *signature = [NSData dataWithBytes:aliceSignature length:ECCSignatureLength];
|
||||
|
||||
if (![Ed25519 throws_verifySignature:signature publicKey:alicePublic data:ephemPublic]) {
|
||||
|
||||
if (![Ed25519 verifySignature:signature publicKey:alicePublic data:ephemPublic]) {
|
||||
XCTAssert(NO, @"Sig verification failed!");
|
||||
}
|
||||
|
||||
|
||||
for (int i=0;i<[signature length];i++) {
|
||||
|
||||
NSMutableData *modifiedSignature = [signature mutableCopy];
|
||||
@ -159,8 +156,8 @@
|
||||
replacedByte ^= 0x01;
|
||||
|
||||
[modifiedSignature replaceBytesInRange:NSMakeRange(i, 1) withBytes:&replacedByte length:1];
|
||||
|
||||
if ([Ed25519 throws_verifySignature:modifiedSignature publicKey:alicePublic data:ephemPublic]) {
|
||||
|
||||
if ([Ed25519 verifySignature:modifiedSignature publicKey:alicePublic data:ephemPublic]) {
|
||||
XCTAssert(NO, @"Modified signature shouldn't be verified correctly");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// HKDFTest.m
|
||||
//
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/09/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
|
||||
@interface HKDFTest : XCTestCase
|
||||
|
||||
@ -40,7 +45,7 @@
|
||||
|
||||
NSData *okmData = [NSData dataWithBytes:okm length:42];
|
||||
|
||||
NSData *actualOutput = [HKDFKit throws_deriveKey:ikmData info:infoData salt:saltData outputSize:42];
|
||||
NSData *actualOutput = [HKDFKit deriveKey:ikmData info:infoData salt:saltData outputSize:42];
|
||||
|
||||
XCTAssert([okmData isEqualToData:actualOutput], @"HKDF output matches test vector");
|
||||
}
|
||||
@ -122,7 +127,7 @@
|
||||
|
||||
NSData *okmData = [NSData dataWithBytes:okm length:82];
|
||||
|
||||
NSData *actualOutput = [HKDFKit throws_deriveKey:ikmData info:infoData salt:saltData outputSize:82];
|
||||
NSData *actualOutput = [HKDFKit deriveKey:ikmData info:infoData salt:saltData outputSize:82];
|
||||
|
||||
XCTAssert([actualOutput isEqualToData:okmData], @"HKDF output matches long test vector");
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <AxolotlKit/PreKeyBundle.h>
|
||||
#import <AxolotlKit/NSData+keyVersionByte.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
@interface PreKeyBundleTests : XCTestCase
|
||||
@ -28,11 +27,11 @@
|
||||
PreKeyBundle *bundle = [[PreKeyBundle alloc] initWithRegistrationId:1
|
||||
deviceId:2
|
||||
preKeyId:3
|
||||
preKeyPublic:[Curve25519 generateKeyPair].publicKey.prependKeyType
|
||||
signedPreKeyPublic:[Curve25519 generateKeyPair].publicKey.prependKeyType
|
||||
preKeyPublic:[Curve25519 generateKeyPair].publicKey
|
||||
signedPreKeyPublic:[Curve25519 generateKeyPair].publicKey
|
||||
signedPreKeyId:4
|
||||
signedPreKeySignature:[Curve25519 generateKeyPair].publicKey
|
||||
identityKey:[Curve25519 generateKeyPair].publicKey.prependKeyType];
|
||||
identityKey:[Curve25519 generateKeyPair].publicKey];
|
||||
|
||||
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:bundle];
|
||||
|
||||
@ -30,9 +30,9 @@
|
||||
int previousCounter = 1;
|
||||
|
||||
SPKProtoTSProtoWhisperMessageBuilder *builder =
|
||||
[SPKProtoTSProtoWhisperMessage builderWithRatchetKey:ratchetKey
|
||||
counter:counter
|
||||
ciphertext:cipherText];
|
||||
[[SPKProtoTSProtoWhisperMessageBuilder alloc] initWithRatchetKey:ratchetKey
|
||||
counter:counter
|
||||
ciphertext:cipherText];
|
||||
[builder setPreviousCounter:previousCounter];
|
||||
SPKProtoTSProtoWhisperMessage *message = [builder buildIgnoringErrors];
|
||||
NSData *serializedMessage = [message serializedDataIgnoringErrors];
|
||||
|
||||
@ -1,19 +1,22 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ECKeyPair+ECKeyPairTesting.h"
|
||||
#import <AxolotlKit/AliceAxolotlParameters.h>
|
||||
#import <AxolotlKit/BobAxolotlParameters.h>
|
||||
#import <AxolotlKit/ChainKey.h>
|
||||
#import <AxolotlKit/RatchetingSession.h>
|
||||
#import <AxolotlKit/SPKMockProtocolStore.h>
|
||||
#import <AxolotlKit/SessionCipher.h>
|
||||
#import <AxolotlKit/SessionRecord.h>
|
||||
#import <AxolotlKit/SessionState.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "AxolotlInMemoryStore.h"
|
||||
#import "AliceAxolotlParameters.h"
|
||||
#import "BobAxolotlParameters.h"
|
||||
#import "SessionCipher.h"
|
||||
#import "SessionState.h"
|
||||
#import "RatchetingSession.h"
|
||||
#import "SessionRecord.h"
|
||||
#import "ChainKey.h"
|
||||
|
||||
#import "ECKeyPair+ECKeyPairTesting.h"
|
||||
|
||||
@interface RatchetingSessionTest : XCTestCase
|
||||
|
||||
@ -296,27 +299,22 @@
|
||||
(Byte) 0x8D, (Byte) 0xF7, (Byte) 0x22, (Byte) 0xDC,
|
||||
(Byte) 0x22, (Byte) 0x76, (Byte) 0xC3, (Byte) 0xA6};
|
||||
NSData *aliceCipherTextData = [NSData dataWithBytes:aliceCipherText length:32];
|
||||
|
||||
|
||||
ECKeyPair *aliceIdentityKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceIdentityPrivateKeyData publicKey:aliceIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *bobIdentityKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:bobIdentityPrivateKeyData publicKey:bobIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *aliceBaseKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceBasePrivateKeyData publicKey:aliceBasePublicKeyData];
|
||||
|
||||
ECKeyPair *bobBaseKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:bobBasePrivateKeyData publicKey:bobBasePublicKeyData];
|
||||
|
||||
ECKeyPair *aliceSendingRatchet =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceSendingRatchetPrivateData publicKey:aliceSendingRatchetPublicData];
|
||||
|
||||
|
||||
|
||||
ECKeyPair *aliceIdentityKey = [ECKeyPair keyPairWithPrivateKey:aliceIdentityPrivateKeyData publicKey:aliceIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *bobIdentityKey = [ECKeyPair keyPairWithPrivateKey:bobIdentityPrivateKeyData publicKey:bobIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *aliceBaseKey = [ECKeyPair keyPairWithPrivateKey:aliceBasePrivateKeyData publicKey:aliceBasePublicKeyData];
|
||||
|
||||
ECKeyPair *bobBaseKey = [ECKeyPair keyPairWithPrivateKey:bobBasePrivateKeyData publicKey:bobBasePublicKeyData];
|
||||
|
||||
ECKeyPair *aliceSendingRatchet = [ECKeyPair keyPairWithPrivateKey:aliceSendingRatchetPrivateData publicKey:aliceSendingRatchetPublicData];
|
||||
|
||||
// ---
|
||||
|
||||
SPKMockProtocolStore *aliceStore = [SPKMockProtocolStore new];
|
||||
SPKMockProtocolStore *bobStore = [SPKMockProtocolStore new];
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
@ -324,16 +322,11 @@
|
||||
AliceAxolotlParameters *aliceAxolotlParams = [[AliceAxolotlParameters alloc] initWithIdentityKey:aliceIdentityKey theirIdentityKey:bobIdentityKey.publicKey ourBaseKey:aliceBaseKey theirSignedPreKey:bobBaseKey.publicKey theirOneTimePreKey:nil theirRatchetKey:bobBaseKey.publicKey];
|
||||
|
||||
BobAxolotlParameters *bobAxolotlParams = [[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:bobIdentityKey theirIdentityKey:aliceIdentityKey.publicKey ourSignedPrekey:bobBaseKey ourRatchetKey:bobBaseKey ourOneTimePrekey:nil theirBaseKey:aliceBaseKey.publicKey];
|
||||
|
||||
[RatchetingSession throws_initializeSession:aliceSessionRecord.sessionState
|
||||
sessionVersion:3
|
||||
AliceParameters:aliceAxolotlParams
|
||||
senderRatchet:aliceSendingRatchet];
|
||||
|
||||
[RatchetingSession throws_initializeSession:bobSessionRecord.sessionState
|
||||
sessionVersion:3
|
||||
BobParameters:bobAxolotlParams];
|
||||
|
||||
|
||||
[RatchetingSession initializeSession:aliceSessionRecord.sessionState sessionVersion:3 AliceParameters:aliceAxolotlParams senderRatchet:aliceSendingRatchet];
|
||||
|
||||
[RatchetingSession initializeSession:bobSessionRecord.sessionState sessionVersion:3 BobParameters:bobAxolotlParams];
|
||||
|
||||
NSString *aliceIdentifier = @"+483294823482";
|
||||
NSString *bobIdentifier = @"+389424728942";
|
||||
|
||||
@ -341,45 +334,42 @@
|
||||
XCTAssert([[@"This is a plaintext message." dataUsingEncoding:NSUTF8StringEncoding] isEqualToData:alicePlaintextData], @"Encoding is not correct");
|
||||
XCTAssert([aliceSessionRecord.sessionState.rootKey.keyData isEqualToData:aliceSessionRecordRootKeyData]);
|
||||
XCTAssert([aliceSessionRecord.sessionState.senderChainKey.key isEqualToData:aliceSendingChainKeyData]);
|
||||
XCTAssert([aliceSendingCipherKeyData
|
||||
isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.cipherKey]);
|
||||
XCTAssert(
|
||||
[aliceSendingIVKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.iv]);
|
||||
XCTAssert([aliceSendingMacKeyData
|
||||
isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.macKey]);
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord protocolContext:nil];
|
||||
XCTAssert([aliceSendingCipherKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.cipherKey]);
|
||||
XCTAssert([aliceSendingIVKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.iv]);
|
||||
XCTAssert([aliceSendingMacKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.macKey]);
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord];
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:bobIdentifier deviceId:1];
|
||||
|
||||
WhisperMessage *message = [aliceSessionCipher throws_encryptMessage:alicePlaintextData protocolContext:nil];
|
||||
|
||||
WhisperMessage *message = [aliceSessionCipher encryptMessage:alicePlaintextData];
|
||||
XCTAssert([aliceCipherTextData isEqualToData:message.cipherText]);
|
||||
|
||||
// Logging's Bob's Session initialization and first message decryption
|
||||
|
||||
XCTAssert([bobRootKeyData isEqualToData:bobSessionRecord.sessionState.rootKey.keyData]);
|
||||
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord protocolContext:nil];
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord];
|
||||
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:aliceIdentifier deviceId:1];
|
||||
|
||||
NSData *plainData = [bobSessionCipher throws_decrypt:message protocolContext:nil];
|
||||
|
||||
|
||||
NSData *plainData = [bobSessionCipher decrypt:message];
|
||||
|
||||
XCTAssert([plainData isEqualToData:alicePlaintextData]);
|
||||
|
||||
for (int i = 0; i<100; i++) {
|
||||
NSData *message = [[NSString stringWithFormat:@"Message: %i", i] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
WhisperMessage *encrypted = [aliceSessionCipher throws_encryptMessage:message protocolContext:nil];
|
||||
|
||||
XCTAssert([message isEqualToData:[bobSessionCipher throws_decrypt:encrypted protocolContext:nil]]);
|
||||
|
||||
WhisperMessage *encrypted = [aliceSessionCipher encryptMessage:message];
|
||||
|
||||
XCTAssert([message isEqualToData:[bobSessionCipher decrypt:encrypted]]);
|
||||
}
|
||||
|
||||
for (int i = 0; i<100; i++) {
|
||||
NSData *message = [[NSString stringWithFormat:@"Message: %i", i] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
WhisperMessage *encrypted = [bobSessionCipher throws_encryptMessage:message protocolContext:nil];
|
||||
|
||||
XCTAssert([message isEqualToData:[aliceSessionCipher throws_decrypt:encrypted protocolContext:nil]]);
|
||||
|
||||
WhisperMessage *encrypted = [bobSessionCipher encryptMessage:message];
|
||||
|
||||
XCTAssert([message isEqualToData:[aliceSessionCipher decrypt:encrypted]]);
|
||||
}
|
||||
|
||||
NSMutableArray *plainTexts = [NSMutableArray new];
|
||||
@ -388,12 +378,11 @@
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
NSData *message = [[NSString stringWithFormat:@"Message: %i", i] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[plainTexts addObject:message];
|
||||
[cipherMessages addObject:[bobSessionCipher throws_encryptMessage:message protocolContext:nil]];
|
||||
[cipherMessages addObject:[bobSessionCipher encryptMessage:message]];
|
||||
}
|
||||
|
||||
for (int i = 0; i < plainTexts.count; i++) {
|
||||
XCTAssert([[aliceSessionCipher throws_decrypt:[cipherMessages objectAtIndex:i] protocolContext:nil]
|
||||
isEqualToData:[plainTexts objectAtIndex:i]]);
|
||||
XCTAssert([[aliceSessionCipher decrypt:[cipherMessages objectAtIndex:i]] isEqualToData:[plainTexts objectAtIndex:i]]);
|
||||
}
|
||||
|
||||
}
|
||||
@ -658,27 +647,22 @@
|
||||
(Byte) 0x8D, (Byte) 0xF7, (Byte) 0x22, (Byte) 0xDC,
|
||||
(Byte) 0x22, (Byte) 0x76, (Byte) 0xC3, (Byte) 0xA6};
|
||||
NSData *aliceCipherTextData = [NSData dataWithBytes:aliceCipherText length:32];
|
||||
|
||||
|
||||
ECKeyPair *aliceIdentityKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceIdentityPrivateKeyData publicKey:aliceIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *bobIdentityKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:bobIdentityPrivateKeyData publicKey:bobIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *aliceBaseKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceBasePrivateKeyData publicKey:aliceBasePublicKeyData];
|
||||
|
||||
ECKeyPair *bobBaseKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:bobBasePrivateKeyData publicKey:bobBasePublicKeyData];
|
||||
|
||||
ECKeyPair *aliceSendingRatchet =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceSendingRatchetPrivateData publicKey:aliceSendingRatchetPublicData];
|
||||
|
||||
|
||||
|
||||
ECKeyPair *aliceIdentityKey = [ECKeyPair keyPairWithPrivateKey:aliceIdentityPrivateKeyData publicKey:aliceIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *bobIdentityKey = [ECKeyPair keyPairWithPrivateKey:bobIdentityPrivateKeyData publicKey:bobIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *aliceBaseKey = [ECKeyPair keyPairWithPrivateKey:aliceBasePrivateKeyData publicKey:aliceBasePublicKeyData];
|
||||
|
||||
ECKeyPair *bobBaseKey = [ECKeyPair keyPairWithPrivateKey:bobBasePrivateKeyData publicKey:bobBasePublicKeyData];
|
||||
|
||||
ECKeyPair *aliceSendingRatchet = [ECKeyPair keyPairWithPrivateKey:aliceSendingRatchetPrivateData publicKey:aliceSendingRatchetPublicData];
|
||||
|
||||
// ---
|
||||
|
||||
SPKMockProtocolStore *aliceStore = [SPKMockProtocolStore new];
|
||||
SPKMockProtocolStore *bobStore = [SPKMockProtocolStore new];
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
@ -686,16 +670,11 @@
|
||||
AliceAxolotlParameters *aliceAxolotlParams = [[AliceAxolotlParameters alloc] initWithIdentityKey:aliceIdentityKey theirIdentityKey:bobIdentityKey.publicKey ourBaseKey:aliceBaseKey theirSignedPreKey:bobBaseKey.publicKey theirOneTimePreKey:nil theirRatchetKey:bobBaseKey.publicKey];
|
||||
|
||||
BobAxolotlParameters *bobAxolotlParams = [[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:bobIdentityKey theirIdentityKey:aliceIdentityKey.publicKey ourSignedPrekey:bobBaseKey ourRatchetKey:bobBaseKey ourOneTimePrekey:nil theirBaseKey:aliceBaseKey.publicKey];
|
||||
|
||||
[RatchetingSession throws_initializeSession:aliceSessionRecord.sessionState
|
||||
sessionVersion:3
|
||||
AliceParameters:aliceAxolotlParams
|
||||
senderRatchet:aliceSendingRatchet];
|
||||
|
||||
[RatchetingSession throws_initializeSession:bobSessionRecord.sessionState
|
||||
sessionVersion:3
|
||||
BobParameters:bobAxolotlParams];
|
||||
|
||||
|
||||
[RatchetingSession initializeSession:aliceSessionRecord.sessionState sessionVersion:3 AliceParameters:aliceAxolotlParams senderRatchet:aliceSendingRatchet];
|
||||
|
||||
[RatchetingSession initializeSession:bobSessionRecord.sessionState sessionVersion:3 BobParameters:bobAxolotlParams];
|
||||
|
||||
NSString *aliceIdentifier = @"+483294823482";
|
||||
NSString *bobIdentifier = @"+389424728942";
|
||||
|
||||
@ -703,29 +682,26 @@
|
||||
XCTAssert([[@"This is a plaintext message." dataUsingEncoding:NSUTF8StringEncoding] isEqualToData:alicePlaintextData], @"Encoding is not correct");
|
||||
XCTAssert([aliceSessionRecord.sessionState.rootKey.keyData isEqualToData:aliceSessionRecordRootKeyData]);
|
||||
XCTAssert([aliceSessionRecord.sessionState.senderChainKey.key isEqualToData:aliceSendingChainKeyData]);
|
||||
XCTAssert([aliceSendingCipherKeyData
|
||||
isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.cipherKey]);
|
||||
XCTAssert(
|
||||
[aliceSendingIVKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.iv]);
|
||||
XCTAssert([aliceSendingMacKeyData
|
||||
isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.macKey]);
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord protocolContext:nil];
|
||||
XCTAssert([aliceSendingCipherKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.cipherKey]);
|
||||
XCTAssert([aliceSendingIVKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.iv]);
|
||||
XCTAssert([aliceSendingMacKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.macKey]);
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord];
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:bobIdentifier deviceId:1];
|
||||
|
||||
WhisperMessage *message = [aliceSessionCipher throws_encryptMessage:alicePlaintextData protocolContext:nil];
|
||||
|
||||
WhisperMessage *message = [aliceSessionCipher encryptMessage:alicePlaintextData];
|
||||
XCTAssert([aliceCipherTextData isEqualToData:message.cipherText]);
|
||||
|
||||
// Logging's Bob's Session initialization and first message decryption
|
||||
|
||||
XCTAssert([bobRootKeyData isEqualToData:bobSessionRecord.sessionState.rootKey.keyData]);
|
||||
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord protocolContext:nil];
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord];
|
||||
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:aliceIdentifier deviceId:1];
|
||||
|
||||
NSData *plainData = [bobSessionCipher throws_decrypt:message protocolContext:nil];
|
||||
|
||||
|
||||
NSData *plainData = [bobSessionCipher decrypt:message];
|
||||
|
||||
XCTAssert([plainData isEqualToData:alicePlaintextData]);
|
||||
|
||||
|
||||
@ -735,12 +711,11 @@
|
||||
for (int i = 0 ; i < 30; i++) {
|
||||
NSData *message = [[NSString stringWithFormat:@"Message: %i", i] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[plainTexts addObject:message];
|
||||
[cipherMessages addObject:[bobSessionCipher throws_encryptMessage:message protocolContext:nil]];
|
||||
[cipherMessages addObject:[bobSessionCipher encryptMessage:message]];
|
||||
}
|
||||
|
||||
for (NSUInteger i = plainTexts.count-1; i > 0; i--) {
|
||||
XCTAssert([[aliceSessionCipher throws_decrypt:[cipherMessages objectAtIndex:i] protocolContext:nil]
|
||||
isEqualToData:[plainTexts objectAtIndex:i]]);
|
||||
XCTAssert([[aliceSessionCipher decrypt:[cipherMessages objectAtIndex:i]] isEqualToData:[plainTexts objectAtIndex:i]]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,30 +1,25 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlInMemoryStore.h"
|
||||
#import <AxolotlKit/AxolotlExceptions.h>
|
||||
#import <AxolotlKit/NSData+keyVersionByte.h>
|
||||
#import <AxolotlKit/SPKMockProtocolStore.h>
|
||||
#import <AxolotlKit/SessionBuilder.h>
|
||||
#import <AxolotlKit/SessionCipher.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface PreKeyWhisperMessage ()
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
|
||||
|
||||
@interface PreKeyWhisperMessage ()
|
||||
@property (nonatomic, readwrite) NSData *identityKey;
|
||||
@property (nonatomic, readwrite) NSData *baseKey;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface SessionBuilderTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation SessionBuilderTests
|
||||
|
||||
@ -47,57 +42,46 @@
|
||||
NSString *BOB_RECIPIENT_ID = @"+3828923892";
|
||||
NSString *ALICE_RECIPIENT_ID = @"alice@gmail.com";
|
||||
|
||||
SPKMockProtocolStore *aliceStore = [SPKMockProtocolStore new];
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
SessionBuilder *aliceSessionBuilder = [[SessionBuilder alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
SPKMockProtocolStore *bobStore = [SPKMockProtocolStore new];
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
ECKeyPair *bobPreKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobSignedPreKeyPair = [Curve25519 generateKeyPair];
|
||||
NSData *bobSignedPreKeySignature =
|
||||
[Ed25519 throws_sign:bobSignedPreKeyPair.publicKey.prependKeyType withKeyPair:[bobStore identityKeyPair:nil]];
|
||||
|
||||
PreKeyBundle *bobPreKey = [[PreKeyBundle alloc]initWithRegistrationId:[bobStore localRegistrationId:nil]
|
||||
NSData *bobSignedPreKeySignature = [Ed25519 sign:bobSignedPreKeyPair.publicKey withKeyPair:bobStore.identityKeyPair];
|
||||
|
||||
PreKeyBundle *bobPreKey = [[PreKeyBundle alloc]initWithRegistrationId:bobStore.localRegistrationId
|
||||
deviceId:1
|
||||
preKeyId:31337
|
||||
preKeyPublic:bobPreKeyPair.publicKey.prependKeyType
|
||||
signedPreKeyPublic:bobSignedPreKeyPair.publicKey.prependKeyType
|
||||
preKeyPublic:bobPreKeyPair.publicKey
|
||||
signedPreKeyPublic:bobSignedPreKeyPair.publicKey
|
||||
signedPreKeyId:22
|
||||
signedPreKeySignature:bobSignedPreKeySignature
|
||||
identityKey:[bobStore identityKeyPair:nil].publicKey.prependKeyType];
|
||||
|
||||
[aliceSessionBuilder throws_processPrekeyBundle:bobPreKey protocolContext:nil];
|
||||
|
||||
XCTAssert([aliceStore containsSession:BOB_RECIPIENT_ID deviceId:1 protocolContext:nil]);
|
||||
XCTAssert([aliceStore loadSession:BOB_RECIPIENT_ID deviceId:1 protocolContext:nil].sessionState.version == 3);
|
||||
identityKey:bobStore.identityKeyPair.publicKey];
|
||||
|
||||
[aliceSessionBuilder processPrekeyBundle:bobPreKey];
|
||||
|
||||
XCTAssert([aliceStore containsSession:BOB_RECIPIENT_ID deviceId:1]);
|
||||
XCTAssert([aliceStore loadSession:BOB_RECIPIENT_ID deviceId:1].sessionState.version == 3);
|
||||
|
||||
NSString *originalMessage = @"Freedom is the right to tell people what they do not want to hear.";
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
WhisperMessage *outgoingMessage =
|
||||
[aliceSessionCipher throws_encryptMessage:[originalMessage dataUsingEncoding:NSUTF8StringEncoding]
|
||||
protocolContext:nil];
|
||||
|
||||
|
||||
WhisperMessage *outgoingMessage = [aliceSessionCipher encryptMessage:[originalMessage dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
XCTAssert([outgoingMessage isKindOfClass:[PreKeyWhisperMessage class]], @"Message should be PreKey type");
|
||||
|
||||
PreKeyWhisperMessage *incomingMessage = (PreKeyWhisperMessage*)outgoingMessage;
|
||||
[bobStore storePreKey:31337 preKeyRecord:[[PreKeyRecord alloc] initWithId:bobPreKey.preKeyId
|
||||
keyPair:bobPreKeyPair
|
||||
createdAt:[NSDate date]]
|
||||
protocolContext:nil];
|
||||
[bobStore storeSignedPreKey:22
|
||||
signedPreKeyRecord:[[SignedPreKeyRecord alloc] initWithId:22
|
||||
keyPair:bobSignedPreKeyPair
|
||||
signature:bobSignedPreKeySignature
|
||||
generatedAt:[NSDate date]]
|
||||
protocolContext:nil];
|
||||
[bobStore storePreKey:31337 preKeyRecord:[[PreKeyRecord alloc] initWithId:bobPreKey.preKeyId keyPair:bobPreKeyPair]];
|
||||
[bobStore storeSignedPreKey:22 signedPreKeyRecord:[[SignedPreKeyRecord alloc] initWithId:22 keyPair:bobSignedPreKeyPair signature:bobSignedPreKeySignature generatedAt:[NSDate date]]];
|
||||
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:ALICE_RECIPIENT_ID deviceId:1];
|
||||
[bobSessionCipher throws_decrypt:incomingMessage protocolContext:nil];
|
||||
|
||||
XCTAssert([bobStore containsSession:ALICE_RECIPIENT_ID deviceId:1 protocolContext:nil]);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1 protocolContext:nil].sessionState.version == 3);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1 protocolContext:nil].sessionState.aliceBaseKey != nil);
|
||||
}
|
||||
[bobSessionCipher decrypt:incomingMessage];
|
||||
|
||||
XCTAssert([bobStore containsSession:ALICE_RECIPIENT_ID deviceId:1]);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1].sessionState.version == 3);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1].sessionState.aliceBaseKey != nil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the case where an attacker would send a new PreKeyWhisperMessage with another IdentityKey
|
||||
@ -107,57 +91,53 @@
|
||||
|
||||
NSString *BOB_RECIPIENT_ID = @"+3828923892";
|
||||
|
||||
SPKMockProtocolStore *aliceStore = [SPKMockProtocolStore new];
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
SessionBuilder *aliceSessionBuilder = [[SessionBuilder alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
SPKMockProtocolStore *bobStore = [SPKMockProtocolStore new];
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
ECKeyPair *bobIdentityKeyPair1 = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobPreKeyPair1 = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobSignedPreKeyPair1 = [Curve25519 generateKeyPair];
|
||||
NSData *bobSignedPreKeySignature1 =
|
||||
[Ed25519 throws_sign:bobSignedPreKeyPair1.publicKey.prependKeyType withKeyPair:bobIdentityKeyPair1];
|
||||
NSData *bobSignedPreKeySignature1 = [Ed25519 sign:bobSignedPreKeyPair1.publicKey withKeyPair:bobIdentityKeyPair1];
|
||||
|
||||
PreKeyBundle *bobPreKey1 = [[PreKeyBundle alloc] initWithRegistrationId:[bobStore localRegistrationId:nil]
|
||||
PreKeyBundle *bobPreKey1 = [[PreKeyBundle alloc] initWithRegistrationId:bobStore.localRegistrationId
|
||||
deviceId:1
|
||||
preKeyId:31337
|
||||
preKeyPublic:bobPreKeyPair1.publicKey.prependKeyType
|
||||
signedPreKeyPublic:bobSignedPreKeyPair1.publicKey.prependKeyType
|
||||
preKeyPublic:bobPreKeyPair1.publicKey
|
||||
signedPreKeyPublic:bobSignedPreKeyPair1.publicKey
|
||||
signedPreKeyId:22
|
||||
signedPreKeySignature:bobSignedPreKeySignature1
|
||||
identityKey:bobIdentityKeyPair1.publicKey.prependKeyType];
|
||||
identityKey:bobIdentityKeyPair1.publicKey];
|
||||
|
||||
[aliceSessionBuilder throws_processPrekeyBundle:bobPreKey1 protocolContext:nil];
|
||||
[aliceSessionBuilder processPrekeyBundle:bobPreKey1];
|
||||
|
||||
XCTAssert([aliceStore containsSession:BOB_RECIPIENT_ID deviceId:1 protocolContext:nil]);
|
||||
XCTAssert([aliceStore loadSession:BOB_RECIPIENT_ID deviceId:1 protocolContext:nil].sessionState.version == 3);
|
||||
XCTAssert([aliceStore containsSession:BOB_RECIPIENT_ID deviceId:1]);
|
||||
XCTAssert([aliceStore loadSession:BOB_RECIPIENT_ID deviceId:1].sessionState.version == 3);
|
||||
|
||||
NSString *messageText = @"Freedom is the right to tell people what they do not want to hear.";
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
WhisperMessage *outgoingMessage1 =
|
||||
[aliceSessionCipher throws_encryptMessage:[messageText dataUsingEncoding:NSUTF8StringEncoding]
|
||||
protocolContext:nil];
|
||||
[aliceSessionCipher encryptMessage:[messageText dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
XCTAssert([outgoingMessage1 isKindOfClass:[PreKeyWhisperMessage class]], @"Message should be PreKey type");
|
||||
|
||||
ECKeyPair *bobIdentityKeyPair2 = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobPreKeyPair2 = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobSignedPreKeyPair2 = [Curve25519 generateKeyPair];
|
||||
NSData *bobSignedPreKeySignature2 =
|
||||
[Ed25519 throws_sign:bobSignedPreKeyPair2.publicKey.prependKeyType withKeyPair:bobIdentityKeyPair2];
|
||||
NSData *bobSignedPreKeySignature2 = [Ed25519 sign:bobSignedPreKeyPair2.publicKey withKeyPair:bobIdentityKeyPair2];
|
||||
|
||||
PreKeyBundle *bobPreKey2 = [[PreKeyBundle alloc] initWithRegistrationId:[bobStore localRegistrationId:nil]
|
||||
PreKeyBundle *bobPreKey2 = [[PreKeyBundle alloc] initWithRegistrationId:bobStore.localRegistrationId
|
||||
deviceId:1
|
||||
preKeyId:31337
|
||||
preKeyPublic:bobPreKeyPair2.publicKey.prependKeyType
|
||||
signedPreKeyPublic:bobSignedPreKeyPair2.publicKey.prependKeyType
|
||||
preKeyPublic:bobPreKeyPair2.publicKey
|
||||
signedPreKeyPublic:bobSignedPreKeyPair2.publicKey
|
||||
signedPreKeyId:22
|
||||
signedPreKeySignature:bobSignedPreKeySignature2
|
||||
identityKey:bobIdentityKeyPair2.publicKey.prependKeyType];
|
||||
identityKey:bobIdentityKeyPair2.publicKey];
|
||||
|
||||
XCTAssertThrowsSpecificNamed([aliceSessionBuilder throws_processPrekeyBundle:bobPreKey2 protocolContext:nil],
|
||||
NSException,
|
||||
UntrustedIdentityKeyException);
|
||||
XCTAssertThrowsSpecificNamed(
|
||||
[aliceSessionBuilder processPrekeyBundle:bobPreKey2], NSException, UntrustedIdentityKeyException);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,28 +1,27 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AxolotlKit/AliceAxolotlParameters.h>
|
||||
#import <AxolotlKit/BobAxolotlParameters.h>
|
||||
#import <AxolotlKit/ChainKey.h>
|
||||
#import <AxolotlKit/RatchetingSession.h>
|
||||
#import <AxolotlKit/SPKMockProtocolStore.h>
|
||||
#import <AxolotlKit/SessionBuilder.h>
|
||||
#import <AxolotlKit/SessionCipher.h>
|
||||
#import <AxolotlKit/SessionState.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface SessionRecord (Private)
|
||||
- (void)promoteState:(SessionState *)promotedState;
|
||||
@end
|
||||
#import "AliceAxolotlParameters.h"
|
||||
#import "AxolotlInMemoryStore.h"
|
||||
#import "BobAxolotlParameters.h"
|
||||
#import "ChainKey.h"
|
||||
#import "RatchetingSession.h"
|
||||
#import "SessionBuilder.h"
|
||||
#import "SessionCipher.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
#import "SessionState.h"
|
||||
|
||||
@interface SessionCipherTest : XCTestCase
|
||||
|
||||
@property (nonatomic, readonly) NSString *aliceIdentifier;
|
||||
@property (nonatomic, readonly) NSString *bobIdentifier;
|
||||
@property (nonatomic, readonly) SPKMockProtocolStore *aliceStore;
|
||||
@property (nonatomic, readonly) SPKMockProtocolStore *bobStore;
|
||||
@property (nonatomic, readonly) AxolotlInMemoryStore *aliceStore;
|
||||
@property (nonatomic, readonly) AxolotlInMemoryStore *bobStore;
|
||||
|
||||
@end
|
||||
|
||||
@ -40,8 +39,8 @@
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
_aliceStore = [SPKMockProtocolStore new];
|
||||
_bobStore = [SPKMockProtocolStore new];
|
||||
_aliceStore = [AxolotlInMemoryStore new];
|
||||
_bobStore = [AxolotlInMemoryStore new];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
@ -53,10 +52,34 @@
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
|
||||
[self throws_sessionInitializationWithAliceSessionRecord:aliceSessionRecord bobSessionRecord:bobSessionRecord];
|
||||
[self sessionInitializationWithAliceSessionRecord:aliceSessionRecord bobSessionRecord:bobSessionRecord];
|
||||
[self runInteractionWithAliceRecord:aliceSessionRecord bobRecord:bobSessionRecord];
|
||||
}
|
||||
|
||||
- (void)testBasicSessionCipherDispatchQueue {
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"session cipher completed"];
|
||||
|
||||
dispatch_queue_t sessionCipherDispatchQueue = dispatch_queue_create("session cipher queue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
[SessionCipher setSessionCipherDispatchQueue:sessionCipherDispatchQueue];
|
||||
dispatch_async(sessionCipherDispatchQueue, ^{
|
||||
[self sessionInitializationWithAliceSessionRecord:aliceSessionRecord bobSessionRecord:bobSessionRecord];
|
||||
[self runInteractionWithAliceRecord:aliceSessionRecord bobRecord:bobSessionRecord];
|
||||
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) {
|
||||
if (error) {
|
||||
XCTFail(@"Expectation failed with error: %@", error);
|
||||
}
|
||||
}];
|
||||
[SessionCipher setSessionCipherDispatchQueue:nil];
|
||||
}
|
||||
|
||||
- (void)testPromotingOldSessionState
|
||||
{
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
@ -64,9 +87,9 @@
|
||||
|
||||
// 1.) Given Alice and Bob have initialized some session together
|
||||
SessionState *initialSessionState = bobSessionRecord.sessionState;
|
||||
[self throws_sessionInitializationWithAliceSessionRecord:aliceSessionRecord bobSessionRecord:bobSessionRecord];
|
||||
[self sessionInitializationWithAliceSessionRecord:aliceSessionRecord bobSessionRecord:bobSessionRecord];
|
||||
|
||||
SessionRecord *activeSession = [self.bobStore loadSession:self.aliceIdentifier deviceId:1 protocolContext:nil];
|
||||
SessionRecord *activeSession = [self.bobStore loadSession:self.aliceIdentifier deviceId:1];
|
||||
XCTAssertNotNil(activeSession);
|
||||
XCTAssertEqualObjects(initialSessionState, activeSession.sessionState);
|
||||
|
||||
@ -74,24 +97,23 @@
|
||||
SessionState *newSessionState = [SessionState new];
|
||||
[bobSessionRecord promoteState:newSessionState];
|
||||
XCTAssertEqual(1, bobSessionRecord.previousSessionStates.count);
|
||||
[self.bobStore storeSession:self.aliceIdentifier deviceId:1 session:bobSessionRecord protocolContext:nil];
|
||||
[self.bobStore storeSession:self.aliceIdentifier deviceId:1 session:bobSessionRecord];
|
||||
|
||||
activeSession = [self.bobStore loadSession:self.aliceIdentifier deviceId:1 protocolContext:nil];
|
||||
activeSession = [self.bobStore loadSession:self.aliceIdentifier deviceId:1];
|
||||
XCTAssertNotNil(activeSession);
|
||||
XCTAssertNotEqualObjects(initialSessionState, activeSession.sessionState);
|
||||
XCTAssertEqualObjects(newSessionState, activeSession.sessionState);
|
||||
|
||||
// 3.) Bob should decrypt with initial session after receiving a message from that old session,
|
||||
// but importantly *not* promote it to be the active session.
|
||||
// 3.) Bob should promote back the initial session after receiving a message from that old session.
|
||||
[self runInteractionWithAliceRecord:aliceSessionRecord bobRecord:bobSessionRecord];
|
||||
XCTAssertNotEqualObjects(initialSessionState, activeSession.sessionState);
|
||||
XCTAssertEqualObjects(newSessionState, activeSession.sessionState);
|
||||
XCTAssertNotEqualObjects(newSessionState, activeSession.sessionState);
|
||||
XCTAssertEqualObjects(initialSessionState, activeSession.sessionState);
|
||||
XCTAssertEqual(1, bobSessionRecord.previousSessionStates.count);
|
||||
XCTAssertEqual(0, aliceSessionRecord.previousSessionStates.count);
|
||||
}
|
||||
|
||||
- (void)throws_sessionInitializationWithAliceSessionRecord:(SessionRecord *)aliceSessionRecord
|
||||
bobSessionRecord:(SessionRecord *)bobSessionRecord
|
||||
- (void)sessionInitializationWithAliceSessionRecord:(SessionRecord *)aliceSessionRecord
|
||||
bobSessionRecord:(SessionRecord *)bobSessionRecord
|
||||
{
|
||||
|
||||
SessionState *aliceSessionState = aliceSessionRecord.sessionState;
|
||||
@ -107,16 +129,16 @@
|
||||
AliceAxolotlParameters *aliceParams = [[AliceAxolotlParameters alloc] initWithIdentityKey:aliceIdentityKeyPair theirIdentityKey:[bobIdentityKeyPair publicKey] ourBaseKey:aliceBaseKey theirSignedPreKey:[bobBaseKey publicKey] theirOneTimePreKey:[bobOneTimePK publicKey] theirRatchetKey:[bobBaseKey publicKey]];
|
||||
|
||||
BobAxolotlParameters *bobParams = [[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:bobIdentityKeyPair theirIdentityKey:[aliceIdentityKeyPair publicKey] ourSignedPrekey:bobBaseKey ourRatchetKey:bobBaseKey ourOneTimePrekey:bobOneTimePK theirBaseKey:[aliceBaseKey publicKey]];
|
||||
|
||||
[RatchetingSession initializeSession:bobSessionState sessionVersion:3 BobParameters:bobParams];
|
||||
|
||||
[RatchetingSession initializeSession:aliceSessionState sessionVersion:3 AliceParameters:aliceParams];
|
||||
|
||||
[RatchetingSession throws_initializeSession:bobSessionState sessionVersion:3 BobParameters:bobParams];
|
||||
[self.aliceStore saveRemoteIdentity:bobIdentityKeyPair.publicKey recipientId:self.bobIdentifier];
|
||||
[self.aliceStore storeSession:self.bobIdentifier deviceId:1 session:aliceSessionRecord];
|
||||
|
||||
[RatchetingSession throws_initializeSession:aliceSessionState sessionVersion:3 AliceParameters:aliceParams];
|
||||
|
||||
[self.aliceStore saveRemoteIdentity:bobIdentityKeyPair.publicKey recipientId:self.bobIdentifier protocolContext:nil];
|
||||
[self.aliceStore storeSession:self.bobIdentifier deviceId:1 session:aliceSessionRecord protocolContext:nil];
|
||||
|
||||
[self.bobStore saveRemoteIdentity:aliceIdentityKeyPair.publicKey recipientId:self.aliceIdentifier protocolContext:nil];
|
||||
[self.bobStore storeSession:self.aliceIdentifier deviceId:1 session:bobSessionRecord protocolContext:nil];
|
||||
[self.bobStore saveRemoteIdentity:aliceIdentityKeyPair.publicKey recipientId:self.aliceIdentifier];
|
||||
[self.bobStore storeSession:self.aliceIdentifier deviceId:1 session:bobSessionRecord];
|
||||
|
||||
XCTAssert([aliceSessionState.remoteIdentityKey isEqualToData:bobSessionState.localIdentityKey]);
|
||||
}
|
||||
@ -128,10 +150,10 @@
|
||||
[[SessionCipher alloc] initWithAxolotlStore:self.bobStore recipientId:self.aliceIdentifier deviceId:1];
|
||||
|
||||
NSData *alicePlainText = [@"This is a plaintext message!" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
WhisperMessage *cipherText = [aliceSessionCipher throws_encryptMessage:alicePlainText protocolContext:nil];
|
||||
|
||||
NSData *bobPlaintext = [bobSessionCipher throws_decrypt:cipherText protocolContext:nil];
|
||||
|
||||
WhisperMessage *cipherText = [aliceSessionCipher encryptMessage:alicePlainText];
|
||||
|
||||
NSData *bobPlaintext = [bobSessionCipher decrypt:cipherText];
|
||||
|
||||
XCTAssert([bobPlaintext isEqualToData:alicePlainText]);
|
||||
}
|
||||
|
||||
|
||||
@ -4,13 +4,8 @@
|
||||
|
||||
#import "Curve25519.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ECKeyPair (ECKeyPairTesting)
|
||||
|
||||
+ (ECKeyPair *)throws_keyPairWithPrivateKey:(NSData *)privateKey
|
||||
publicKey:(NSData *)publicKey NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (ECKeyPair *)keyPairWithPrivateKey:(NSData *)privateKey publicKey:(NSData *)publicKey;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -5,26 +5,28 @@
|
||||
#import "ECKeyPair+ECKeyPairTesting.h"
|
||||
#import "NSData+keyVersionByte.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
@interface ECKeyPair (ECKeyPairTestingPrivate)
|
||||
|
||||
- (nullable id)initWithPublicKey:(NSData *)publicKey privateKey:(NSData *)privateKey;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation ECKeyPair (testing)
|
||||
|
||||
+ (ECKeyPair *)throws_keyPairWithPrivateKey:(NSData *)privateKey publicKey:(NSData *)publicKey
|
||||
+ (ECKeyPair *)keyPairWithPrivateKey:(NSData *)privateKey publicKey:(NSData *)publicKey
|
||||
{
|
||||
if (([publicKey length] == 33)) {
|
||||
publicKey = [publicKey throws_removeKeyType];
|
||||
publicKey = [publicKey removeKeyType];
|
||||
}
|
||||
|
||||
if ([privateKey length] != ECCKeyLength && [publicKey length] != ECCKeyLength) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Public or Private key is not required size" userInfo:@{@"PrivateKey":privateKey, @"Public Key":publicKey}];
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
ECKeyPair *_Nullable keyPairCopy = [[ECKeyPair alloc] initWithPublicKeyData:[publicKey copy] privateKeyData:[privateKey copy] error:&error];
|
||||
OWSAssertDebug(error == nil && keyPairCopy != nil);
|
||||
ECKeyPair *keyPairCopy = [[ECKeyPair alloc] initWithPublicKey:[publicKey copy] privateKey:[privateKey copy]];
|
||||
return keyPairCopy;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AxolotlKit/WhisperMessage.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
#import <AxolotlKit/WhisperMessage.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface WhisperMessageSerialization : XCTestCase
|
||||
@ -29,30 +30,17 @@
|
||||
NSData *cipherText = [@"I'm not really ciphertext" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
ECKeyPair *senderIdentityKey = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *receiverIdentityKey = [Curve25519 generateKeyPair];
|
||||
|
||||
WhisperMessage *message = [[WhisperMessage alloc] init_throws_withVersion:3
|
||||
macKey:fakeMacKey.publicKey
|
||||
senderRatchetKey:keyPair.publicKey
|
||||
counter:counter
|
||||
previousCounter:0
|
||||
cipherText:cipherText
|
||||
senderIdentityKey:senderIdentityKey.publicKey
|
||||
receiverIdentityKey:receiverIdentityKey.publicKey];
|
||||
|
||||
WhisperMessage *deserializedMessage = [[WhisperMessage alloc] init_throws_withData:message.serialized];
|
||||
|
||||
|
||||
|
||||
WhisperMessage *message = [[WhisperMessage alloc] initWithVersion:3 macKey:fakeMacKey.publicKey senderRatchetKey:keyPair.publicKey counter:counter previousCounter:0 cipherText:cipherText senderIdentityKey:senderIdentityKey.publicKey receiverIdentityKey:receiverIdentityKey.publicKey];
|
||||
|
||||
WhisperMessage *deserializedMessage = [[WhisperMessage alloc] initWithData:message.serialized];
|
||||
|
||||
|
||||
XCTAssert([[message.serialized subdataWithRange:NSMakeRange(0, message.serialized.length-8)] isEqualToData:[deserializedMessage.serialized subdataWithRange:NSMakeRange(0, deserializedMessage.serialized.length-8)]]);
|
||||
|
||||
[message throws_verifyMacWithVersion:3
|
||||
senderIdentityKey:senderIdentityKey.publicKey
|
||||
receiverIdentityKey:receiverIdentityKey.publicKey
|
||||
macKey:fakeMacKey.publicKey];
|
||||
[deserializedMessage throws_verifyMacWithVersion:3
|
||||
senderIdentityKey:senderIdentityKey.publicKey
|
||||
receiverIdentityKey:receiverIdentityKey.publicKey
|
||||
macKey:fakeMacKey.publicKey];
|
||||
|
||||
|
||||
[message verifyMacWithVersion:3 senderIdentityKey:senderIdentityKey.publicKey receiverIdentityKey:receiverIdentityKey.publicKey macKey:fakeMacKey.publicKey];
|
||||
[deserializedMessage verifyMacWithVersion:3 senderIdentityKey:senderIdentityKey.publicKey receiverIdentityKey:receiverIdentityKey.publicKey macKey:fakeMacKey.publicKey];
|
||||
|
||||
XCTAssert([message.cipherText isEqualToData:deserializedMessage.cipherText]);
|
||||
XCTAssert(message.version == deserializedMessage.version);
|
||||
XCTAssert([message.serialized isEqualToData:deserializedMessage.serialized]);
|
||||
|
||||
215
Gemfile.lock
215
Gemfile.lock
@ -1,87 +1,80 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.0)
|
||||
activesupport (4.2.10)
|
||||
CFPropertyList (2.3.5)
|
||||
activesupport (4.2.8)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
atomos (0.1.3)
|
||||
addressable (2.5.0)
|
||||
public_suffix (~> 2.0, >= 2.0.2)
|
||||
babosa (1.0.2)
|
||||
claide (1.0.2)
|
||||
cocoapods (1.5.3)
|
||||
claide (1.0.1)
|
||||
cocoapods (1.2.0)
|
||||
activesupport (>= 4.0.2, < 5)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.5.3)
|
||||
cocoapods-deintegrate (>= 1.0.2, < 2.0)
|
||||
cocoapods-downloader (>= 1.2.0, < 2.0)
|
||||
claide (>= 1.0.1, < 2.0)
|
||||
cocoapods-core (= 1.2.0)
|
||||
cocoapods-deintegrate (>= 1.0.1, < 2.0)
|
||||
cocoapods-downloader (>= 1.1.3, < 2.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-stats (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.3.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.1.2, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
colored (~> 1.2)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (~> 2.0.1)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.6.5)
|
||||
molinillo (~> 0.5.5)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (~> 1.1)
|
||||
xcodeproj (>= 1.5.7, < 2.0)
|
||||
cocoapods-core (1.5.3)
|
||||
activesupport (>= 4.0.2, < 6)
|
||||
ruby-macho (~> 0.2.5)
|
||||
xcodeproj (>= 1.4.1, < 2.0)
|
||||
cocoapods-core (1.2.0)
|
||||
activesupport (>= 4.0.2, < 5)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.2)
|
||||
cocoapods-downloader (1.2.2)
|
||||
cocoapods-deintegrate (1.0.1)
|
||||
cocoapods-downloader (1.1.3)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.0)
|
||||
cocoapods-stats (1.0.0)
|
||||
cocoapods-trunk (1.3.1)
|
||||
cocoapods-trunk (1.1.2)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
netrc (= 0.7.8)
|
||||
cocoapods-try (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
commander-fastlane (4.4.4)
|
||||
highline (~> 1.7.2)
|
||||
concurrent-ruby (1.1.3)
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
domain_name (0.5.20180417)
|
||||
domain_name (0.5.20170223)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.5.0)
|
||||
emoji_regex (0.1.1)
|
||||
dotenv (2.2.0)
|
||||
escape (0.0.4)
|
||||
excon (0.62.0)
|
||||
faraday (0.15.3)
|
||||
excon (0.55.0)
|
||||
faraday (0.11.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
faraday (>= 0.7.4)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday_middleware (0.12.2)
|
||||
faraday_middleware (0.11.0.1)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.4)
|
||||
fastlane (2.108.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
fastimage (2.1.0)
|
||||
fastlane (2.23.0)
|
||||
activesupport (< 5)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 2.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
commander-fastlane (>= 4.4.0, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (~> 0.1)
|
||||
excon (>= 0.45.0, < 1.0.0)
|
||||
faraday (~> 0.9)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 0.9)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.21.2, < 0.24.0)
|
||||
fastimage (>= 1.6)
|
||||
gh_inspector (>= 1.0.1, < 2.0.0)
|
||||
google-api-client (~> 0.9.2)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
mini_magick (~> 4.5.1)
|
||||
@ -89,105 +82,99 @@ GEM
|
||||
multi_xml (~> 0.5)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
public_suffix (~> 2.0.0)
|
||||
rubyzip (>= 1.2.2, < 2.0.0)
|
||||
rubyzip (>= 1.1.0, < 2.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
slack-notifier (>= 1.3, < 2.0.0)
|
||||
terminal-notifier (>= 1.6.2, < 2.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
tty-screen (~> 0.5.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.6.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcodeproj (>= 0.20, < 2.0.0)
|
||||
xcpretty (>= 0.2.4, < 1.0.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fourflusher (2.0.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.23.9)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.5, < 0.7.0)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.9)
|
||||
googleauth (0.6.7)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
gh_inspector (1.0.3)
|
||||
google-api-client (0.9.28)
|
||||
addressable (~> 2.3)
|
||||
googleauth (~> 0.5)
|
||||
httpclient (~> 2.7)
|
||||
hurley (~> 0.1)
|
||||
memoist (~> 0.11)
|
||||
mime-types (>= 1.6)
|
||||
representable (~> 2.3.0)
|
||||
retriable (~> 2.0)
|
||||
googleauth (0.5.1)
|
||||
faraday (~> 0.9)
|
||||
jwt (~> 1.4)
|
||||
logging (~> 2.0)
|
||||
memoist (~> 0.12)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
os (~> 0.9)
|
||||
signet (~> 0.7)
|
||||
highline (1.7.10)
|
||||
highline (1.7.8)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.1.0)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.0)
|
||||
mime-types (3.2.2)
|
||||
hurley (0.2)
|
||||
i18n (0.8.1)
|
||||
json (2.0.3)
|
||||
jwt (1.5.6)
|
||||
little-plugger (1.1.4)
|
||||
logging (2.2.0)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.10)
|
||||
memoist (0.15.0)
|
||||
mime-types (3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2018.0812)
|
||||
mime-types-data (3.2016.0521)
|
||||
mini_magick (4.5.1)
|
||||
minitest (5.11.3)
|
||||
molinillo (0.6.6)
|
||||
multi_json (1.13.1)
|
||||
minitest (5.10.1)
|
||||
molinillo (0.5.7)
|
||||
multi_json (1.12.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.2.6)
|
||||
nanaimo (0.2.3)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.0)
|
||||
netrc (0.11.0)
|
||||
os (1.0.0)
|
||||
plist (3.4.0)
|
||||
netrc (0.7.8)
|
||||
os (0.9.6)
|
||||
plist (3.2.0)
|
||||
public_suffix (2.0.5)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (1.3.1)
|
||||
rubyzip (1.2.2)
|
||||
representable (2.3.0)
|
||||
uber (~> 0.0.7)
|
||||
retriable (2.1.0)
|
||||
rouge (1.11.1)
|
||||
ruby-macho (0.2.6)
|
||||
rubyzip (1.2.1)
|
||||
security (0.1.3)
|
||||
signet (0.11.0)
|
||||
signet (0.7.3)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
jwt (~> 1.5)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.5)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (1.8.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
slack-notifier (1.5.1)
|
||||
terminal-notifier (1.7.1)
|
||||
terminal-table (1.7.3)
|
||||
unicode-display_width (~> 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
tty-cursor (0.6.0)
|
||||
tty-screen (0.6.5)
|
||||
tty-spinner (0.8.0)
|
||||
tty-cursor (>= 0.5.0)
|
||||
tzinfo (1.2.5)
|
||||
tty-screen (0.5.0)
|
||||
tzinfo (1.2.3)
|
||||
thread_safe (~> 0.1)
|
||||
uber (0.1.0)
|
||||
uber (0.0.15)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.4.0)
|
||||
unf_ext (0.0.7.2)
|
||||
unicode-display_width (1.1.3)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.7.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.2.6)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcodeproj (1.4.2)
|
||||
CFPropertyList (~> 2.3.3)
|
||||
activesupport (>= 3)
|
||||
claide (>= 1.0.1, < 2.0)
|
||||
colored (~> 1.2)
|
||||
nanaimo (~> 0.2.3)
|
||||
xcpretty (0.2.4)
|
||||
rouge (~> 1.8)
|
||||
xcpretty-travis-formatter (0.0.4)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
@ -198,4 +185,4 @@ DEPENDENCIES
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.2
|
||||
1.14.6
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
**Deprecation Warning**: It is recommended that the swift interface of [libsignal-client](https://github.com/signalapp/libsignal-client) be used for all new iOS applications. This library is no longer used by us or maintained.
|
||||
**Deprecation Warning**: It is recommended that [libsignal-protocol-c](https://github.com/whispersystems/libsignal-protocol-c) be used for all new applications.
|
||||
|
||||
|
||||
# SignalProtocolKit [](https://travis-ci.org/WhisperSystems/AxolotlKit)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user