session-ios/SessionProtocolKit/Ratchet/RatchetingSession.m

189 lines
7.3 KiB
Objective-C

//
// Copyright (c) 2018 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 <SessionProtocolKit/SCKExceptionWrapper.h>
#import <SessionProtocolKit/OWSAsserts.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");
@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);
self = [super init];
const char *HKDFDefaultSalt[4] = {0};
NSData *salt = [NSData dataWithBytes:HKDFDefaultSalt length:sizeof(HKDFDefaultSalt)];
NSData *info = [@"WhisperText" dataUsingEncoding:NSUTF8StringEncoding];
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)];
return self;
}
@end
@implementation RatchetingSession
+ (void)throws_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];
}
+ (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
{
OWSAssert(session);
OWSAssert(parameters);
[session setVersion:sessionVersion];
[session setRemoteIdentityKey:parameters.theirIdentityKey];
[session setLocalIdentityKey:parameters.ourIdentityKeyPair.publicKey];
DHEResult *result = [self throws_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
{
OWSAssert(session);
OWSAssert(parameters);
OWSAssert(sendingRatchet);
[session setVersion:sessionVersion];
[session setRemoteIdentityKey:parameters.theirIdentityKey];
[session setLocalIdentityKey:parameters.ourIdentityKeyPair.publicKey];
DHEResult *result = [self throws_DHEKeyAgreement:parameters];
OWSAssert(result);
RKCK *sendingChain =
[result.rootKey throws_createChainWithTheirEphemeral:parameters.theirRatchetKey ourEphemeral:sendingRatchet];
OWSAssert(sendingChain);
[session addReceiverChain:parameters.theirRatchetKey chainKey:[[ChainKey alloc]initWithData:result.chainKey index:0]];
[session setSenderChain:sendingRatchet chainKey:sendingChain.chainKey];
[session setRootKey:sendingChain.rootKey];
}
+ (DHEResult *)throws_DHEKeyAgreement:(id<AxolotlParameters>)parameters
{
OWSAssert(parameters);
NSMutableData *masterKey = [NSMutableData data];
[masterKey appendData:[self discontinuityBytes]];
if ([parameters isKindOfClass:[AliceAxolotlParameters class]]) {
AliceAxolotlParameters *params = (AliceAxolotlParameters*)parameters;
[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 generateSharedSecretFromPublicKey:params.theirOneTimePrekey
andKeyPair:params.ourBaseKey]];
}
} else if ([parameters isKindOfClass:[BobAxolotlParameters class]]){
BobAxolotlParameters *params = (BobAxolotlParameters*)parameters;
[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 generateSharedSecretFromPublicKey:params.theirBaseKey
andKeyPair:params.ourOneTimePrekey]];
}
}
return [[DHEResult alloc] init_throws_withMasterKey:masterKey];
}
/**
* The discontinuity bytes enforce that the session initialization is different between protocol V2 and V3.
*
* @return Returns 32-bytes of 0xFF
*/
+ (NSData*)discontinuityBytes{
NSMutableData *discontinuity = [NSMutableData data];
int8_t byte = 0xFF;
for (int i = 0; i < 32; i++) {
[discontinuity appendBytes:&byte length:sizeof(int8_t)];
}
return [NSData dataWithData:discontinuity];
}
@end