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.
158 lines
4.8 KiB
Matlab
158 lines
4.8 KiB
Matlab
5 years ago
|
//
|
||
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#import "OWSProvisioningCipher.h"
|
||
|
#import <CommonCrypto/CommonCrypto.h>
|
||
|
#import <Curve25519Kit/Curve25519.h>
|
||
|
#import <HKDFKit/HKDFKit.h>
|
||
|
#import <SessionProtocolKit/SessionProtocolKit.h>
|
||
|
|
||
|
NS_ASSUME_NONNULL_BEGIN
|
||
|
|
||
|
@interface OWSProvisioningCipher ()
|
||
|
|
||
|
@property (nonatomic, readonly) NSData *theirPublicKey;
|
||
|
@property (nonatomic, readonly) ECKeyPair *ourKeyPair;
|
||
|
@property (nonatomic, readonly) NSData *initializationVector;
|
||
|
|
||
|
@end
|
||
|
|
||
|
#pragma mark -
|
||
|
|
||
|
@implementation OWSProvisioningCipher
|
||
|
|
||
|
- (instancetype)initWithTheirPublicKey:(NSData *)theirPublicKey
|
||
|
{
|
||
|
return [self initWithTheirPublicKey:theirPublicKey
|
||
|
ourKeyPair:[Curve25519 generateKeyPair]
|
||
|
initializationVector:[Cryptography generateRandomBytes:kCCBlockSizeAES128]];
|
||
|
}
|
||
|
|
||
|
// Private method which exposes dependencies for testing
|
||
|
- (instancetype)initWithTheirPublicKey:(NSData *)theirPublicKey
|
||
|
ourKeyPair:(ECKeyPair *)ourKeyPair
|
||
|
initializationVector:(NSData *)initializationVector
|
||
|
{
|
||
|
self = [super init];
|
||
|
if (!self) {
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
_theirPublicKey = theirPublicKey;
|
||
|
_ourKeyPair = ourKeyPair;
|
||
|
_initializationVector = initializationVector;
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
- (NSData *)ourPublicKey
|
||
|
{
|
||
|
return self.ourKeyPair.publicKey;
|
||
|
}
|
||
|
|
||
|
- (nullable NSData *)encrypt:(NSData *)dataToEncrypt
|
||
|
{
|
||
|
@try {
|
||
|
return [self throws_encryptWithData:dataToEncrypt];
|
||
|
} @catch (NSException *exception) {
|
||
|
OWSFailDebug(@"exception: %@ of type: %@ with reason: %@, user info: %@.",
|
||
|
exception.description,
|
||
|
exception.name,
|
||
|
exception.reason,
|
||
|
exception.userInfo);
|
||
|
return nil;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (nullable NSData *)throws_encryptWithData:(NSData *)dataToEncrypt
|
||
|
{
|
||
|
NSData *sharedSecret =
|
||
|
[Curve25519 generateSharedSecretFromPublicKey:self.theirPublicKey andKeyPair:self.ourKeyPair];
|
||
|
|
||
|
NSData *infoData = [@"TextSecure Provisioning Message" dataUsingEncoding:NSASCIIStringEncoding];
|
||
|
NSData *nullSalt = [[NSMutableData dataWithLength:32] copy];
|
||
|
NSData *derivedSecret = [HKDFKit deriveKey:sharedSecret info:infoData salt:nullSalt outputSize:64];
|
||
|
NSData *cipherKey = [derivedSecret subdataWithRange:NSMakeRange(0, 32)];
|
||
|
NSData *macKey = [derivedSecret subdataWithRange:NSMakeRange(32, 32)];
|
||
|
if (cipherKey.length != 32) {
|
||
|
OWSFailDebug(@"Cipher Key must be 32 bytes");
|
||
|
return nil;
|
||
|
}
|
||
|
if (macKey.length != 32) {
|
||
|
OWSFailDebug(@"Mac Key must be 32 bytes");
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
u_int8_t versionByte[] = { 0x01 };
|
||
|
NSMutableData *message = [NSMutableData dataWithBytes:&versionByte length:1];
|
||
|
|
||
|
NSData *_Nullable cipherText = [self encrypt:dataToEncrypt withKey:cipherKey];
|
||
|
if (cipherText == nil) {
|
||
|
OWSFailDebug(@"Provisioning cipher failed.");
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
[message appendData:cipherText];
|
||
|
|
||
|
NSData *_Nullable mac = [self macForMessage:message withKey:macKey];
|
||
|
if (mac == nil) {
|
||
|
OWSFailDebug(@"mac failed.");
|
||
|
return nil;
|
||
|
}
|
||
|
[message appendData:mac];
|
||
|
|
||
|
return [message copy];
|
||
|
}
|
||
|
|
||
|
- (nullable NSData *)encrypt:(NSData *)dataToEncrypt withKey:(NSData *)cipherKey
|
||
|
{
|
||
|
NSData *iv = self.initializationVector;
|
||
|
if (iv.length != kCCBlockSizeAES128) {
|
||
|
OWSFailDebug(@"Unexpected length for iv");
|
||
|
return nil;
|
||
|
}
|
||
|
if (dataToEncrypt.length >= SIZE_MAX - (kCCBlockSizeAES128 + iv.length)) {
|
||
|
OWSFailDebug(@"data is too long to encrypt.");
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
// allow space for message + padding any incomplete block. PKCS7 padding will always add at least one byte.
|
||
|
size_t ciphertextBufferSize = dataToEncrypt.length + kCCBlockSizeAES128;
|
||
|
|
||
|
NSMutableData *ciphertextData = [[NSMutableData alloc] initWithLength:ciphertextBufferSize];
|
||
|
|
||
|
size_t bytesEncrypted = 0;
|
||
|
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
|
||
|
kCCAlgorithmAES,
|
||
|
kCCOptionPKCS7Padding,
|
||
|
cipherKey.bytes,
|
||
|
cipherKey.length,
|
||
|
iv.bytes,
|
||
|
dataToEncrypt.bytes,
|
||
|
dataToEncrypt.length,
|
||
|
ciphertextData.mutableBytes,
|
||
|
ciphertextBufferSize,
|
||
|
&bytesEncrypted);
|
||
|
|
||
|
if (cryptStatus != kCCSuccess) {
|
||
|
OWSFailDebug(@"Encryption failed with status: %d", cryptStatus);
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
// message format is (iv || ciphertext)
|
||
|
NSMutableData *encryptedMessage = [NSMutableData new];
|
||
|
[encryptedMessage appendData:iv];
|
||
|
[encryptedMessage appendData:[ciphertextData subdataWithRange:NSMakeRange(0, bytesEncrypted)]];
|
||
|
return [encryptedMessage copy];
|
||
|
}
|
||
|
|
||
|
- (nullable NSData *)macForMessage:(NSData *)message withKey:(NSData *)macKey
|
||
|
{
|
||
|
return [Cryptography computeSHA256HMAC:message withHMACKey:macKey];
|
||
|
}
|
||
|
|
||
|
@end
|
||
|
|
||
|
NS_ASSUME_NONNULL_END
|