|
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
|