mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
8.1 KiB
Objective-C
203 lines
8.1 KiB
Objective-C
//
|
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
#import "WhisperMessage.h"
|
|
#import "AxolotlExceptions.h"
|
|
#import "Constants.h"
|
|
#import "NSData+keyVersionByte.h"
|
|
#import "SerializationUtilities.h"
|
|
#import <SignalCoreKit/OWSAsserts.h>
|
|
#import <SessionProtocolKit/SessionProtocolKit-Swift.h>
|
|
#import <SignalCoreKit/NSData+OWS.h>
|
|
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
|
#import <SignalCoreKit/SignalCoreKit-Swift.h>
|
|
#import <SessionProtocolKit/SessionProtocolKit-Swift.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
#define VERSION_LENGTH 1
|
|
|
|
@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
|
|
{
|
|
OWSAssert(macKey);
|
|
OWSAssert(senderRatchetKey);
|
|
OWSAssert(cipherText);
|
|
OWSAssert(cipherText);
|
|
OWSAssert(senderIdentityKey);
|
|
OWSAssert(receiverIdentityKey);
|
|
|
|
if (self = [super init]) {
|
|
Byte versionByte = [SerializationUtilities intsToByteHigh:version low:CURRENT_VERSION];
|
|
NSMutableData *serialized = [NSMutableData dataWithBytes:&versionByte length:1];
|
|
|
|
SPKProtoTSProtoWhisperMessageBuilder *messageBuilder = [SPKProtoTSProtoWhisperMessage builderWithRatchetKey:senderRatchetKey
|
|
counter:counter
|
|
ciphertext:cipherText];
|
|
[messageBuilder setPreviousCounter:previousCounter];
|
|
NSError *error;
|
|
NSData *_Nullable messageData = [messageBuilder buildSerializedDataAndReturnError:&error];
|
|
if (!messageData || error) {
|
|
OWSFailDebug(@"Could not serialize proto: %@.", error);
|
|
OWSRaiseException(InvalidMessageException, @"Could not serialize proto.");
|
|
}
|
|
[serialized appendData:messageData];
|
|
|
|
NSData *mac = [SerializationUtilities throws_macWithVersion:version
|
|
identityKey:[senderIdentityKey prependKeyType]
|
|
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
|
macKey:macKey
|
|
serialized:serialized];
|
|
|
|
[serialized appendData:mac];
|
|
|
|
_version = version;
|
|
_senderRatchetKey = senderRatchetKey;
|
|
_previousCounter = previousCounter;
|
|
_counter = counter;
|
|
_cipherText = cipherText;
|
|
_serialized = [serialized copy];
|
|
}
|
|
|
|
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
|
|
{
|
|
if (self = [super init]) {
|
|
if (serialized.length <= (VERSION_LENGTH + MAC_LENGTH)) {
|
|
@throw [NSException exceptionWithName:InvalidMessageException
|
|
reason:@"Message size is too short to have content"
|
|
userInfo:@{}];
|
|
}
|
|
|
|
Byte version;
|
|
[serialized getBytes:&version length:VERSION_LENGTH];
|
|
|
|
NSUInteger messageAndMacLength;
|
|
ows_sub_overflow(serialized.length, VERSION_LENGTH, &messageAndMacLength);
|
|
NSData *messageAndMac = [serialized subdataWithRange:NSMakeRange(VERSION_LENGTH, messageAndMacLength)];
|
|
|
|
NSUInteger messageLength;
|
|
ows_sub_overflow(messageAndMac.length, MAC_LENGTH, &messageLength);
|
|
NSData *messageData = [messageAndMac subdataWithRange:NSMakeRange(0, messageLength)];
|
|
|
|
if ([SerializationUtilities highBitsToIntFromByte:version] < MINIMUM_SUPPORTED_VERSION) {
|
|
@throw [NSException
|
|
exceptionWithName:LegacyMessageException
|
|
reason:@"Message was sent with an unsupported version of the TextSecure protocol."
|
|
userInfo:@{}];
|
|
}
|
|
|
|
if ([SerializationUtilities highBitsToIntFromByte:version] > CURRENT_VERSION) {
|
|
@throw [NSException exceptionWithName:InvalidMessageException
|
|
reason:@"Unknown Version"
|
|
userInfo:@{
|
|
@"Version" : [NSNumber
|
|
numberWithChar:[SerializationUtilities highBitsToIntFromByte:version]]
|
|
}];
|
|
}
|
|
|
|
NSError *error;
|
|
SPKProtoTSProtoWhisperMessage *_Nullable whisperMessage =
|
|
[SPKProtoTSProtoWhisperMessage parseData:messageData error:&error];
|
|
if (!whisperMessage || error) {
|
|
OWSFailDebug(@"Could not parse proto: %@.", error);
|
|
OWSRaiseException(InvalidMessageException, @"Could not parse proto.");
|
|
}
|
|
|
|
_serialized = serialized;
|
|
_senderRatchetKey = [whisperMessage.ratchetKey throws_removeKeyType];
|
|
_version = [SerializationUtilities highBitsToIntFromByte:version];
|
|
_counter = whisperMessage.counter;
|
|
_previousCounter = whisperMessage.previousCounter;
|
|
_cipherText = whisperMessage.ciphertext;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)throws_verifyMacWithVersion:(int)messageVersion
|
|
senderIdentityKey:(NSData *)senderIdentityKey
|
|
receiverIdentityKey:(NSData *)receiverIdentityKey
|
|
macKey:(NSData *)macKey
|
|
{
|
|
OWSAssert(senderIdentityKey);
|
|
OWSAssert(receiverIdentityKey);
|
|
OWSAssert(macKey);
|
|
|
|
OWSDataParser *dataParser = [[OWSDataParser alloc] initWithData:self.serialized];
|
|
NSError *error;
|
|
|
|
NSUInteger messageLength;
|
|
if (__builtin_sub_overflow(self.serialized.length, MAC_LENGTH, &messageLength)) {
|
|
OWSFailDebug(@"Data too short");
|
|
OWSRaiseException(InvalidMessageException, @"Data too short");
|
|
}
|
|
NSData *_Nullable data = [dataParser nextDataWithLength:messageLength
|
|
name:@"message data"
|
|
error:&error];
|
|
if (!data || error) {
|
|
OWSFailDebug(@"Could not parse data: %@.", error);
|
|
OWSRaiseException(InvalidMessageException, @"Could not parse data.");
|
|
}
|
|
NSData *_Nullable theirMac = [dataParser nextDataWithLength:MAC_LENGTH
|
|
name:@"mac data"
|
|
error:&error];
|
|
if (!theirMac || error) {
|
|
OWSFailDebug(@"Could not parse their mac: %@.", error);
|
|
OWSRaiseException(InvalidMessageException, @"Could not parse their mac.");
|
|
}
|
|
|
|
NSData *ourMac = [SerializationUtilities throws_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);
|
|
OWSRaiseException(InvalidMessageException, @"Bad Mac!");
|
|
}
|
|
}
|
|
|
|
- (CipherMessageType)cipherMessageType {
|
|
return CipherMessageType_Whisper;
|
|
}
|
|
|
|
#pragma mark - Logging
|
|
|
|
+ (NSString *)logTag
|
|
{
|
|
return [NSString stringWithFormat:@"[%@]", self.class];
|
|
}
|
|
|
|
- (NSString *)logTag
|
|
{
|
|
return self.class.logTag;
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|