Blob Blame History Raw
/*
 Password Encryption Controller

 Copyright 2013 Thincast Technologies GmbH, Author: Dorian Johnson

 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
 If a copy of the MPL was not distributed with this file, You can obtain one at
 http://mozilla.org/MPL/2.0/.
 */

#import "EncryptionController.h"
#import "SFHFKeychainUtils.h"
#import "TSXAdditions.h"

@interface EncryptionController (Private)

- (BOOL)verifyPassword:(Encryptor *)decryptor;
- (NSData *)encryptedVerificationData;
- (void)setEncryptedVerificationData:(Encryptor *)encryptor;

- (NSString *)keychainServerName;
- (NSString *)keychainUsername;
- (void)setKeychainPassword:(NSString *)password;
- (NSString *)keychainPassword;
- (NSString *)keychainDefaultPassword;

@end

static EncryptionController *_shared_encryption_controller = nil;

#pragma mark -

@implementation EncryptionController

+ (EncryptionController *)sharedEncryptionController
{
	@synchronized(self)
	{
		if (_shared_encryption_controller == nil)
			_shared_encryption_controller = [[EncryptionController alloc] init];
	}

	return _shared_encryption_controller;
}

#pragma mark Getting an encryptor or decryptor

- (Encryptor *)encryptor
{
	if (_shared_encryptor)
		return _shared_encryptor;

	NSString *saved_password = [self keychainPassword];
	if (saved_password == nil)
	{
		saved_password = [self keychainDefaultPassword];
		Encryptor *encryptor = [[[Encryptor alloc] initWithPassword:saved_password] autorelease];
		[self setEncryptedVerificationData:encryptor];
		_shared_encryptor = [encryptor retain];
	}
	else
	{
		Encryptor *encryptor = [[[Encryptor alloc] initWithPassword:saved_password] autorelease];
		if ([self verifyPassword:encryptor])
			_shared_encryptor = [encryptor retain];
	}

	return _shared_encryptor;
}

// For the current implementation, decryptors and encryptors are equivilant.
- (Encryptor *)decryptor
{
	return [self encryptor];
}

@end

#pragma mark -

@implementation EncryptionController (Private)

#pragma mark -
#pragma mark Keychain password storage

- (NSString *)keychainServerName
{
	return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
}

- (NSString *)keychainUsername
{
	return @"master.password";
}

- (void)setKeychainPassword:(NSString *)password
{
	NSError *error;
	if (password == nil)
	{
		[SFHFKeychainUtils deleteItemForUsername:[self keychainUsername]
		                           andServerName:[self keychainServerName]
		                                   error:&error];
		return;
	}

	[SFHFKeychainUtils storeUsername:[self keychainUsername]
	                     andPassword:password
	                   forServerName:[self keychainServerName]
	                  updateExisting:YES
	                           error:&error];
}

- (NSString *)keychainPassword
{
	NSError *error;
	return [SFHFKeychainUtils getPasswordForUsername:[self keychainUsername]
	                                   andServerName:[self keychainServerName]
	                                           error:&error];
}

- (NSString *)keychainDefaultPassword
{
	NSString *password = [[NSUserDefaults standardUserDefaults] stringForKey:@"UUID"];
	if ([password length] == 0)
	{
		password = [NSString stringWithUUID];
		[[NSUserDefaults standardUserDefaults] setObject:password forKey:@"UUID"];
		[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"TSXMasterPasswordVerification"];
	}
	return password;
}

#pragma mark -
#pragma mark Verification of encryption key against verification data

- (BOOL)verifyPassword:(Encryptor *)decryptor
{
	return [[decryptor plaintextPassword]
	    isEqualToString:[decryptor decryptString:[self encryptedVerificationData]]];
}

- (NSData *)encryptedVerificationData
{
	return [[NSUserDefaults standardUserDefaults] dataForKey:@"TSXMasterPasswordVerification"];
}

- (void)setEncryptedVerificationData:(Encryptor *)encryptor
{
	[[NSUserDefaults standardUserDefaults]
	    setObject:[encryptor encryptString:[encryptor plaintextPassword]]
	       forKey:@"TSXMasterPasswordVerification"];
}

@end