CryptoPassphrase  ShowDetailsController.m at [d49f2910e7]

File iOS/ShowDetailsController.m artifact 2d0d3612cd part of check-in d49f2910e7


/*
 * Copyright (c) 2016, Jonathan Schleifer <js@heap.zone>
 *
 * https://heap.zone/git/scrypt-pwgen.git
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice is present in all copies.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import <ObjFW_Bridge/ObjFW_Bridge.h>

#import "ShowDetailsController.h"

#import "SiteStorage.h"
#import "PasswordGenerator.h"
#import "NewPasswordGenerator.h"
#import "LegacyPasswordGenerator.h"

@interface ShowDetailsController ()
- (void)_generateWithCallback: (void (^)(NSMutableString *))block;
- (void)_generateAndCopy;
- (void)_generateAndShow;
@end

static void
clearNSMutableString(NSMutableString *string)
{
	/*
	 * NSMutableString does not offer a way to zero the string.
	 * This is in the hope that setting a single character at an index just
	 * replaces that character in memory, and thus allows us to zero the
	 * password.
	 */
	for (NSUInteger i = 0 ; i < string.length; i++)
		[string replaceCharactersInRange: NSMakeRange(i, 1)
				      withString: @" "];
}

@implementation ShowDetailsController
- (void)dealloc
{
	[_name release];
	[_nameField release];
	[_lengthField release];
	[_legacySwitch release];
	[_passphraseField release];
	[_mainViewController release];

	[super dealloc];
}

- (void)viewWillAppear: (BOOL)animated
{
	SiteStorage *siteStorage;
	NSIndexPath *indexPath;

	[super viewWillAppear: animated];

	siteStorage = self.mainViewController.siteStorage;
	indexPath = self.mainViewController.tableView.indexPathForSelectedRow;

	[_name release];
	_name = [self.mainViewController.sites[indexPath.row] retain];
	_length = [siteStorage lengthForSite: _name];
	_legacy = [siteStorage isSiteLegacy: _name];

	self.nameField.text = [_name NSObject];
	self.lengthField.text = [NSString stringWithFormat: @"%zu", _length];
	self.legacySwitch.on = _legacy;

	[self.mainViewController.tableView deselectRowAtIndexPath: indexPath
							 animated: YES];
}

- (void)viewDidAppear: (BOOL)animated
{
	[super viewDidAppear: animated];

	[self.passphraseField becomeFirstResponder];
}

- (BOOL)textFieldShouldReturn: (UITextField *)textField
{
	[textField resignFirstResponder];
	return NO;
}

-	  (void)tableView: (UITableView *)tableView
  didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
	[self.passphraseField resignFirstResponder];
	[tableView deselectRowAtIndexPath: indexPath
				 animated: YES];

	if (indexPath.section == 3) {
		switch (indexPath.row) {
		case 0:
			[self _generateAndCopy];
			break;
		case 1:
			[self _generateAndShow];
			break;
		}
	}
}

- (void)_generateAndCopy
{
	[self _generateWithCallback: ^ (NSMutableString *password) {
		UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
		pasteBoard.string = password;

		clearNSMutableString(password);

		UIAlertController *alert = [UIAlertController
		    alertControllerWithTitle: @"Password Generated"
				     message: @"The password has been copied "
					      @"into the clipboard."
			      preferredStyle: UIAlertControllerStyleAlert];
		[alert addAction:
		    [UIAlertAction actionWithTitle: @"OK"
					     style: UIAlertActionStyleDefault
					   handler: ^ (UIAlertAction *action) {
			[self.navigationController
			    popViewControllerAnimated: YES];
		}]];

		[self presentViewController: alert
				   animated: YES
				 completion: nil];
	}];
}

- (void)_generateAndShow
{
	[self _generateWithCallback: ^ (NSMutableString *password) {
		UIAlertController *alert = [UIAlertController
		    alertControllerWithTitle: @"Generated Passphrase"
				     message: password
			      preferredStyle: UIAlertControllerStyleAlert];
		[alert addAction:
		    [UIAlertAction actionWithTitle: @"OK"
					     style: UIAlertActionStyleDefault
					   handler: ^ (UIAlertAction *action) {
			[self.navigationController
			    popViewControllerAnimated: YES];
		}]];

		[self presentViewController: alert
				   animated: YES
				 completion: ^ {
			clearNSMutableString(password);
		}];
	}];
}

- (void)_generateWithCallback: (void (^)(NSMutableString *))block
{
	id <PasswordGenerator> generator;
	char *passphrase;
	UIStoryboard *mainStoryboard;
	UIViewController *activityController;

	if (_legacy)
		generator = [LegacyPasswordGenerator generator];
	else
		generator = [NewPasswordGenerator generator];

	generator.site = _name;
	generator.length = _length;

	passphrase = of_strdup([self.passphraseField.text UTF8String]);
	generator.passphrase = passphrase;

	mainStoryboard = [UIStoryboard storyboardWithName: @"Main"
						   bundle: nil];
	activityController = [mainStoryboard
	    instantiateViewControllerWithIdentifier: @"activityIndicator"];
	[self.navigationController.view addSubview: activityController.view];

	dispatch_async(dispatch_get_global_queue(
	    DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
		@try {
			[generator derivePassword];
		} @finally {
			of_explicit_memset(passphrase, 0, strlen(passphrase));
			free(passphrase);
		}

		NSMutableString *password = [NSMutableString
		    stringWithUTF8String: (char *)generator.output];
		of_explicit_memset(generator.output, 0,
		    strlen((char *)generator.output));

		dispatch_sync(dispatch_get_main_queue(), ^ {
			activityController.view.hidden = YES;
			block(password);
		});
	});
}

- (IBAction)remove: (id)sender
{
	UIAlertController *alert = [UIAlertController
	    alertControllerWithTitle: @"Remove Site?"
			     message: @"Do you want to remove this site?"
		      preferredStyle: UIAlertControllerStyleAlert];
	[alert addAction:
	    [UIAlertAction actionWithTitle: @"No"
				     style: UIAlertActionStyleCancel
				   handler: nil]];
	[alert addAction:
	    [UIAlertAction actionWithTitle: @"Yes"
				     style: UIAlertActionStyleDestructive
				   handler: ^ (UIAlertAction *action) {
		[self.mainViewController.siteStorage removeSite: _name];
		[self.mainViewController reset];

		[self.navigationController popViewControllerAnimated: YES];
	}]];

	[self presentViewController: alert
			   animated: YES
			 completion: nil];
}
@end