/* * Copyright (c) 2016 - 2019 Jonathan Schleifer * * 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; #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]; [_keyFile release]; [_keyFileField 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] copy]; _length = [siteStorage lengthForSite: _name]; _legacy = [siteStorage isSiteLegacy: _name]; _keyFile = [[siteStorage keyFileForSite: _name] copy]; self.nameField.text = _name.NSObject; self.lengthField.text = [NSString stringWithFormat: @"%zu", _length]; self.legacySwitch.on = _legacy; self.keyFileField.text = _keyFile.NSObject; [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 generator; char *passphrase; UIStoryboard *mainStoryboard; UIViewController *activityController; if (_legacy) generator = [LegacyPasswordGenerator generator]; else generator = [NewPasswordGenerator generator]; generator.site = _name; generator.length = _length; if (_keyFile != nil) { NSString *documentDirectory; OFString *keyFilePath; if ((documentDirectory = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES).firstObject) == nil) { NSLog(@"Could not get key files: No documents " @"directory"); return; } keyFilePath = [documentDirectory.OFObject stringByAppendingPathComponent: _keyFile]; generator.keyFile = [OFMutableData dataWithContentsOfFile: keyFilePath]; } 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 { if (generator.keyFile != nil) of_explicit_memset( (void *)generator.keyFile.items, 0, generator.keyFile.count); of_explicit_memset(passphrase, 0, strlen(passphrase)); free(passphrase); } NSMutableString *password = [[[NSMutableString alloc] initWithBytes: (char *)generator.output length: generator.length encoding: NSUTF8StringEncoding] autorelease]; of_explicit_memset(generator.output, 0, generator.length); 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