Blame client/iOS/Models/Encryptor.m

Packit Service fa4841
/*
Packit Service fa4841
 Password Encryptor
Packit Service fa4841
Packit Service fa4841
 Copyright 2013 Thincast Technologies GmbH, Author: Dorian Johnson
Packit Service fa4841
Packit Service fa4841
 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
Packit Service fa4841
 If a copy of the MPL was not distributed with this file, You can obtain one at
Packit Service fa4841
 http://mozilla.org/MPL/2.0/.
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
/* We try to use CommonCrypto as much as possible. PBKDF2 was added to CommonCrypto in iOS 5, so use
Packit Service fa4841
 * OpenSSL only as a fallback to do PBKDF2 on pre iOS 5 systems. */
Packit Service fa4841
Packit Service fa4841
#import "Encryptor.h"
Packit Service fa4841
#import <CommonCrypto/CommonKeyDerivation.h>
Packit Service fa4841
#import <CommonCrypto/CommonCryptor.h>
Packit Service fa4841
#import <CommonCrypto/CommonDigest.h>
Packit Service fa4841
#import <openssl/evp.h> // For PBKDF2 on < 5.0
Packit Service fa4841
#include <fcntl.h>
Packit Service fa4841
Packit Service fa4841
#pragma mark -
Packit Service fa4841
Packit Service fa4841
@interface Encryptor (Private)
Packit Service fa4841
- (NSData *)randomInitializationVector;
Packit Service fa4841
@end
Packit Service fa4841
Packit Service fa4841
@implementation Encryptor
Packit Service fa4841
@synthesize plaintextPassword = _plaintext_password;
Packit Service fa4841
Packit Service fa4841
- (id)initWithPassword:(NSString *)plaintext_password
Packit Service fa4841
{
Packit Service fa4841
	if (plaintext_password == nil)
Packit Service fa4841
		return nil;
Packit Service fa4841
Packit Service fa4841
	if (!(self = [super init]))
Packit Service fa4841
		return nil;
Packit Service fa4841
Packit Service fa4841
	_plaintext_password = [plaintext_password retain];
Packit Service fa4841
	const char *plaintext_password_data =
Packit Service fa4841
	    [plaintext_password length] ? [plaintext_password UTF8String] : " ";
Packit Service fa4841
Packit Service fa4841
	if (!plaintext_password_data || !strlen(plaintext_password_data))
Packit Service fa4841
		[NSException raise:NSInternalInconsistencyException
Packit Service fa4841
		            format:@"%s: plaintext password data is zero length!", __func__];
Packit Service fa4841
Packit Service fa4841
	uint8_t *derived_key = calloc(1, TSXEncryptorPBKDF2KeySize);
Packit Service fa4841
Packit Service fa4841
	if (CCKeyDerivationPBKDF != NULL)
Packit Service fa4841
	{
Packit Service fa4841
		int ret = CCKeyDerivationPBKDF(
Packit Service fa4841
		    kCCPBKDF2, plaintext_password_data, strlen(plaintext_password_data) - 1,
Packit Service fa4841
		    (const uint8_t *)TSXEncryptorPBKDF2Salt, TSXEncryptorPBKDF2SaltLen, kCCPRFHmacAlgSHA1,
Packit Service fa4841
		    TSXEncryptorPBKDF2Rounds, derived_key, TSXEncryptorPBKDF2KeySize);
Packit Service fa4841
		// NSLog(@"CCKeyDerivationPBKDF ret = %d; key: %@", ret, [NSData
Packit Service fa4841
		// dataWithBytesNoCopy:derived_key length:TWEncryptorPBKDF2KeySize freeWhenDone:NO]);
Packit Service fa4841
Packit Service fa4841
		if (ret)
Packit Service fa4841
		{
Packit Service fa4841
			NSLog(@"%s: CCKeyDerivationPBKDF ret == %d, indicating some sort of failure.", __func__,
Packit Service fa4841
			      ret);
Packit Service fa4841
			free(derived_key);
Packit Service fa4841
			[self autorelease];
Packit Service fa4841
			return nil;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		// iOS 4.x or earlier -- use OpenSSL
Packit Service fa4841
		unsigned long ret = PKCS5_PBKDF2_HMAC_SHA1(
Packit Service fa4841
		    plaintext_password_data, (int)strlen(plaintext_password_data) - 1,
Packit Service fa4841
		    (const unsigned char *)TSXEncryptorPBKDF2Salt, TSXEncryptorPBKDF2SaltLen,
Packit Service fa4841
		    TSXEncryptorPBKDF2Rounds, TSXEncryptorPBKDF2KeySize, derived_key);
Packit Service fa4841
		// NSLog(@"PKCS5_PBKDF2_HMAC_SHA1 ret = %lu; key: %@", ret, [NSData
Packit Service fa4841
		// dataWithBytesNoCopy:derived_key length:TWEncryptorPBKDF2KeySize freeWhenDone:NO]);
Packit Service fa4841
Packit Service fa4841
		if (ret != 1)
Packit Service fa4841
		{
Packit Service fa4841
			NSLog(@"%s: PKCS5_PBKDF2_HMAC_SHA1 ret == %lu, indicating some sort of failure.",
Packit Service fa4841
			      __func__, ret);
Packit Service fa4841
			free(derived_key);
Packit Service fa4841
			[self release];
Packit Service fa4841
			return nil;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	_encryption_key = [[NSData alloc] initWithBytesNoCopy:derived_key
Packit Service fa4841
	                                               length:TSXEncryptorPBKDF2KeySize
Packit Service fa4841
	                                         freeWhenDone:YES];
Packit Service fa4841
	return self;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
#pragma mark -
Packit Service fa4841
#pragma mark Encrypting/Decrypting data
Packit Service fa4841
Packit Service fa4841
- (NSData *)encryptData:(NSData *)plaintext_data
Packit Service fa4841
{
Packit Service fa4841
	if (![plaintext_data length])
Packit Service fa4841
		return nil;
Packit Service fa4841
Packit Service fa4841
	NSData *iv = [self randomInitializationVector];
Packit Service fa4841
	NSMutableData *encrypted_data = [NSMutableData
Packit Service fa4841
	    dataWithLength:[iv length] + [plaintext_data length] + TSXEncryptorBlockCipherBlockSize];
Packit Service fa4841
	[encrypted_data replaceBytesInRange:NSMakeRange(0, [iv length]) withBytes:[iv bytes]];
Packit Service fa4841
Packit Service fa4841
	size_t data_out_moved = 0;
Packit Service fa4841
	int ret = CCCrypt(kCCEncrypt, TSXEncryptorBlockCipherAlgo, TSXEncryptorBlockCipherOptions,
Packit Service fa4841
	                  [_encryption_key bytes], TSXEncryptorBlockCipherKeySize, [iv bytes],
Packit Service fa4841
	                  [plaintext_data bytes], [plaintext_data length],
Packit Service fa4841
	                  [encrypted_data mutableBytes] + [iv length],
Packit Service fa4841
	                  [encrypted_data length] - [iv length], &data_out_moved);
Packit Service fa4841
Packit Service fa4841
	switch (ret)
Packit Service fa4841
	{
Packit Service fa4841
		case kCCSuccess:
Packit Service fa4841
			[encrypted_data setLength:[iv length] + data_out_moved];
Packit Service fa4841
			return encrypted_data;
Packit Service fa4841
Packit Service fa4841
		default:
Packit Service fa4841
			NSLog(
Packit Service fa4841
			    @"%s: uncaught error, ret CCCryptorStatus = %d (plaintext len = %lu; buffer size = "
Packit Service fa4841
			    @"%lu)",
Packit Service fa4841
			    __func__, ret, (unsigned long)[plaintext_data length],
Packit Service fa4841
			    (unsigned long)([encrypted_data length] - [iv length]));
Packit Service fa4841
			return nil;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return nil;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
- (NSData *)decryptData:(NSData *)encrypted_data
Packit Service fa4841
{
Packit Service fa4841
	if ([encrypted_data length] <= TSXEncryptorBlockCipherBlockSize)
Packit Service fa4841
		return nil;
Packit Service fa4841
Packit Service fa4841
	NSMutableData *plaintext_data =
Packit Service fa4841
	    [NSMutableData dataWithLength:[encrypted_data length] + TSXEncryptorBlockCipherBlockSize];
Packit Service fa4841
	size_t data_out_moved = 0;
Packit Service fa4841
Packit Service fa4841
	int ret =
Packit Service fa4841
	    CCCrypt(kCCDecrypt, TSXEncryptorBlockCipherAlgo, TSXEncryptorBlockCipherOptions,
Packit Service fa4841
	            [_encryption_key bytes], TSXEncryptorBlockCipherKeySize, [encrypted_data bytes],
Packit Service fa4841
	            [encrypted_data bytes] + TSXEncryptorBlockCipherBlockSize,
Packit Service fa4841
	            [encrypted_data length] - TSXEncryptorBlockCipherBlockSize,
Packit Service fa4841
	            [plaintext_data mutableBytes], [plaintext_data length], &data_out_moved);
Packit Service fa4841
Packit Service fa4841
	switch (ret)
Packit Service fa4841
	{
Packit Service fa4841
		case kCCSuccess:
Packit Service fa4841
			[plaintext_data setLength:data_out_moved];
Packit Service fa4841
			return plaintext_data;
Packit Service fa4841
Packit Service fa4841
		case kCCBufferTooSmall: // Our output buffer is big enough to decrypt valid data. This
Packit Service fa4841
		                        // return code indicates malformed data.
Packit Service fa4841
		case kCCAlignmentError: // Shouldn't get this, since we're using padding.
Packit Service fa4841
		case kCCDecodeError:    // Wrong key.
Packit Service fa4841
			return nil;
Packit Service fa4841
Packit Service fa4841
		default:
Packit Service fa4841
			NSLog(@"%s: uncaught error, ret CCCryptorStatus = %d (encrypted data len = %lu; buffer "
Packit Service fa4841
			      @"size = %lu; dom = %lu)",
Packit Service fa4841
			      __func__, ret, (unsigned long)[encrypted_data length],
Packit Service fa4841
			      (unsigned long)[plaintext_data length], data_out_moved);
Packit Service fa4841
			return nil;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return nil;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
- (NSData *)encryptString:(NSString *)plaintext_string
Packit Service fa4841
{
Packit Service fa4841
	return [self encryptData:[plaintext_string dataUsingEncoding:NSUTF8StringEncoding]];
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
- (NSString *)decryptString:(NSData *)encrypted_string
Packit Service fa4841
{
Packit Service fa4841
	return [[[NSString alloc] initWithData:[self decryptData:encrypted_string]
Packit Service fa4841
	                              encoding:NSUTF8StringEncoding] autorelease];
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
- (NSData *)randomInitializationVector
Packit Service fa4841
{
Packit Service fa4841
	NSMutableData *iv = [NSMutableData dataWithLength:TSXEncryptorBlockCipherBlockSize];
Packit Service fa4841
	int fd;
Packit Service fa4841
Packit Service fa4841
	if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
Packit Service fa4841
		return nil;
Packit Service fa4841
Packit Service fa4841
	NSInteger bytes_needed = [iv length];
Packit Service fa4841
	char *p = [iv mutableBytes];
Packit Service fa4841
Packit Service fa4841
	while (bytes_needed)
Packit Service fa4841
	{
Packit Service fa4841
		long bytes_read = read(fd, p, bytes_needed);
Packit Service fa4841
Packit Service fa4841
		if (bytes_read < 0)
Packit Service fa4841
			continue;
Packit Service fa4841
Packit Service fa4841
		p += bytes_read;
Packit Service fa4841
		bytes_needed -= bytes_read;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	close(fd);
Packit Service fa4841
	return iv;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
@end