Index: LegacyPasswordGenerator.h ================================================================== --- LegacyPasswordGenerator.h +++ LegacyPasswordGenerator.h @@ -24,10 +24,10 @@ @interface LegacyPasswordGenerator: OFObject { size_t _length; OFString *_site; - OFData *_keyfile; + OFData *_keyFile; const char *_passphrase; unsigned char *_output; } @end Index: LegacyPasswordGenerator.m ================================================================== --- LegacyPasswordGenerator.m +++ LegacyPasswordGenerator.m @@ -21,11 +21,11 @@ */ #import "LegacyPasswordGenerator.h" @implementation LegacyPasswordGenerator -@synthesize site = _site, keyfile = _keyfile, passphrase = _passphrase; +@synthesize site = _site, keyFile = _keyFile, passphrase = _passphrase; @synthesize output = _output; + (instancetype)generator { return [[[self alloc] init] autorelease]; @@ -68,26 +68,26 @@ } _output = [self allocMemoryWithSize: _length + 1]; passphraseLength = combinedPassphraseLength = strlen(_passphrase); - if (_keyfile != nil) { - if (SIZE_MAX - combinedPassphraseLength < _keyfile.count) + if (_keyFile != nil) { + if (SIZE_MAX - combinedPassphraseLength < _keyFile.count) @throw [OFOutOfRangeException exception]; - combinedPassphraseLength += _keyfile.count; + combinedPassphraseLength += _keyFile.count; } if ((combinedPassphrase = malloc(combinedPassphraseLength)) == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: combinedPassphraseLength]; @try { memcpy(combinedPassphrase, _passphrase, passphraseLength); - if (_keyfile != nil) + if (_keyFile != nil) memcpy(combinedPassphrase + passphraseLength, - _keyfile.items, _keyfile.count); + _keyFile.items, _keyFile.count); of_scrypt(8, 524288, 2, siteHash.digest, [siteHash.class digestSize], combinedPassphrase, combinedPassphraseLength, _output, _length); } @finally { Index: NewPasswordGenerator.h ================================================================== --- NewPasswordGenerator.h +++ NewPasswordGenerator.h @@ -24,10 +24,10 @@ @interface NewPasswordGenerator: OFObject { size_t _length; OFString *_site; - OFData *_keyfile; + OFData *_keyFile; const char *_passphrase; unsigned char *_output; } @end Index: NewPasswordGenerator.m ================================================================== --- NewPasswordGenerator.m +++ NewPasswordGenerator.m @@ -21,11 +21,11 @@ */ #import "NewPasswordGenerator.h" @implementation NewPasswordGenerator -@synthesize length = _length, site = _site, keyfile = _keyfile; +@synthesize length = _length, site = _site, keyFile = _keyFile; @synthesize passphrase = _passphrase, output = _output; + (instancetype)generator { return [[[self alloc] init] autorelease]; @@ -55,26 +55,26 @@ } _output = [self allocMemoryWithSize: _length + 1]; passphraseLength = combinedPassphraseLength = strlen(_passphrase); - if (_keyfile != nil) { - if (SIZE_MAX - combinedPassphraseLength < _keyfile.count) + if (_keyFile != nil) { + if (SIZE_MAX - combinedPassphraseLength < _keyFile.count) @throw [OFOutOfRangeException exception]; - combinedPassphraseLength += _keyfile.count; + combinedPassphraseLength += _keyFile.count; } if ((combinedPassphrase = malloc(combinedPassphraseLength)) == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: combinedPassphraseLength]; @try { memcpy(combinedPassphrase, _passphrase, passphraseLength); - if (_keyfile != nil) + if (_keyFile != nil) memcpy(combinedPassphrase + passphraseLength, - _keyfile.items, _keyfile.count); + _keyFile.items, _keyFile.count); of_scrypt(8, 524288, 2, siteHash.digest, [siteHash.class digestSize], combinedPassphrase, combinedPassphraseLength, _output, _length); } @finally { Index: PasswordGenerator.h ================================================================== --- PasswordGenerator.h +++ PasswordGenerator.h @@ -23,11 +23,11 @@ #import @protocol PasswordGenerator @property (nonatomic) size_t length; @property (copy, nonatomic) OFString *site; -@property (retain, nonatomic) OFData *keyfile; +@property (retain, nonatomic) OFData *keyFile; @property (nonatomic) const char *passphrase; @property (readonly, nonatomic) unsigned char *output; + (instancetype)generator; - (void)derivePassword; Index: ScryptPWGen.m ================================================================== --- ScryptPWGen.m +++ ScryptPWGen.m @@ -47,23 +47,23 @@ } @implementation ScryptPWGen - (void)applicationDidFinishLaunching { - OFString *keyfilePath, *lengthString; + OFString *keyFilePath, *lengthString; const of_options_parser_option_t options[] = { { 'h', @"help", 0, NULL, NULL }, - { 'k', @"keyfile", 1, NULL, &keyfilePath }, + { 'k', @"keyfile", 1, NULL, &keyFilePath }, { 'l', @"length", 1, NULL, &lengthString }, { 'L', @"legacy", 0, &_legacy, NULL }, { 'r', @"repeat", 0, &_repeat, NULL }, { '\0', nil, 0, NULL, NULL } }; OFOptionsParser *optionsParser = [OFOptionsParser parserWithOptions: options]; of_unichar_t option; - OFMutableData *keyfile = nil; + OFMutableData *keyFile = nil; OFString *prompt; const char *promptCString; char *passphrase; while ((option = [optionsParser nextOption]) != '\0') { @@ -138,12 +138,12 @@ prompt = [OFString stringWithFormat: @"Passphrase for site \"%@\": ", generator.site]; promptCString = [prompt cStringWithEncoding: [OFLocalization encoding]]; - if (keyfilePath != nil) - keyfile = [OFMutableData dataWithContentsOfFile: keyfilePath]; + if (keyFilePath != nil) + keyFile = [OFMutableData dataWithContentsOfFile: keyFilePath]; passphrase = getpass(promptCString); @try { if (_repeat) { char *passphraseCopy = of_strdup(passphrase); @@ -171,11 +171,11 @@ strlen(passphraseCopy)); free(passphraseCopy); } } - generator.keyfile = keyfile; + generator.keyFile = keyFile; generator.passphrase = passphrase; [generator derivePassword]; @try { [of_stdout writeBuffer: generator.output @@ -187,12 +187,12 @@ generator.length); } } @finally { of_explicit_memset(passphrase, 0, strlen(passphrase)); - if (keyfile != nil) - of_explicit_memset(keyfile.items, 0, keyfile.count); + if (keyFile != nil) + of_explicit_memset(keyFile.items, 0, keyFile.count); } [OFApplication terminate]; } @end Index: iOS/AddSiteController.h ================================================================== --- iOS/AddSiteController.h +++ iOS/AddSiteController.h @@ -22,14 +22,15 @@ #import #import "MainViewController.h" -@interface AddSiteController: UITableViewController +@interface AddSiteController: UITableViewController @property (nonatomic, retain) IBOutlet UITextField *nameField; @property (nonatomic, retain) IBOutlet UITextField *lengthField; @property (nonatomic, retain) IBOutlet UISwitch *legacySwitch; +@property (nonatomic, retain) IBOutlet UILabel *keyFileLabel; @property (retain) MainViewController *mainViewController; - (IBAction)done: (id)sender; - (IBAction)cancel: (id)sender; @end Index: iOS/AddSiteController.m ================================================================== --- iOS/AddSiteController.m +++ iOS/AddSiteController.m @@ -45,14 +45,39 @@ - (void)dealloc { [_nameField release]; [_lengthField release]; [_legacySwitch release]; + [_keyFileLabel release]; [_mainViewController release]; [super dealloc]; } + +- (void)tableView: (UITableView *)tableView + didSelectRowAtIndexPath: (NSIndexPath *)indexPath +{ + [tableView deselectRowAtIndexPath: indexPath + animated: YES]; + + if (indexPath.section == 1 && indexPath.row == 1) + [self selectKeyFile]; +} + +- (NSIndexPath *)tableView: (UITableView *)tableView + willSelectRowAtIndexPath: (NSIndexPath *)indexPath +{ + if (indexPath.section == 1 && indexPath.row == 1) + return indexPath; + + return nil; +} + +- (void)selectKeyFile +{ + showAlert(self, @"Not Supported", @"Key files are not supported yet"); +} - (IBAction)done: (id)sender { OFString *name = self.nameField.text.OFObject; OFString *lengthString = self.lengthField.text.OFObject; @@ -85,11 +110,12 @@ return; } [self.mainViewController.siteStorage setSite: name length: length - legacy: self.legacySwitch.on]; + legacy: self.legacySwitch.on + keyFile: nil]; [self.mainViewController reset]; [self.navigationController popViewControllerAnimated: YES]; } Index: iOS/Base.lproj/Main.storyboard ================================================================== --- iOS/Base.lproj/Main.storyboard +++ iOS/Base.lproj/Main.storyboard @@ -1,13 +1,13 @@ - + - + @@ -129,11 +129,11 @@ - + @@ -144,20 +144,20 @@ - + @@ -176,20 +176,20 @@ - + @@ -212,20 +212,20 @@ - + @@ -232,16 +232,48 @@ + + + + + + + + + + + + + + + + + + + - + @@ -255,10 +287,11 @@ + @@ -265,11 +298,11 @@ - + @@ -286,20 +319,20 @@ - + @@ -318,20 +351,20 @@ - + @@ -354,20 +387,20 @@ - + @@ -374,32 +407,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + - + - + @@ -418,18 +483,18 @@ - + @@ -440,18 +505,18 @@ - + @@ -478,10 +543,11 @@ + Index: iOS/ShowDetailsController.h ================================================================== --- iOS/ShowDetailsController.h +++ iOS/ShowDetailsController.h @@ -28,15 +28,17 @@ UITextFieldDelegate> { OFString *_name; size_t _length; bool _legacy; + OFString *_keyFile; } @property (retain, nonatomic) IBOutlet UITextField *nameField; @property (retain, nonatomic) IBOutlet UITextField *lengthField; @property (retain, nonatomic) IBOutlet UISwitch *legacySwitch; +@property (retain, nonatomic) IBOutlet UITextField *keyFileField; @property (retain, nonatomic) IBOutlet UITextField *passphraseField; @property (retain) MainViewController *mainViewController; - (IBAction)remove: (id)sender; @end Index: iOS/ShowDetailsController.m ================================================================== --- iOS/ShowDetailsController.m +++ iOS/ShowDetailsController.m @@ -54,10 +54,12 @@ { [_name release]; [_nameField release]; [_lengthField release]; [_legacySwitch release]; + [_keyFile release]; + [_keyFileField release]; [_passphraseField release]; [_mainViewController release]; [super dealloc]; } @@ -71,17 +73,19 @@ siteStorage = self.mainViewController.siteStorage; indexPath = self.mainViewController.tableView.indexPathForSelectedRow; [_name release]; - _name = [self.mainViewController.sites[indexPath.row] retain]; + _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]; } Index: iOS/SiteStorage.h ================================================================== --- iOS/SiteStorage.h +++ iOS/SiteStorage.h @@ -23,19 +23,21 @@ #import @interface SiteStorage: OFObject { OFString *_path; - OFMutableDictionary *> + OFMutableDictionary *> *_storage; OFArray *_sites; } - (OFArray *)sitesWithFilter: (OFString *)filter; - (bool)hasSite: (OFString *)name; - (size_t)lengthForSite: (OFString *)name; - (bool)isSiteLegacy: (OFString *)name; +- (OFString *)keyFileForSite: (OFString *)name; - (void)setSite: (OFString *)site length: (size_t)length - legacy: (bool)legacy; + legacy: (bool)legacy + keyFile: (OFString *)keyFile; - (void)removeSite: (OFString *)name; @end Index: iOS/SiteStorage.m ================================================================== --- iOS/SiteStorage.m +++ iOS/SiteStorage.m @@ -26,44 +26,46 @@ @interface SiteStorage () - (void)_update; @end -static OFNumber *lengthField, *legacyField; +static OFNumber *lengthField, *legacyField, *keyFileField; @implementation SiteStorage + (void)initialize { lengthField = [@(UINT8_C(0)) retain]; legacyField = [@(UINT8_C(1)) retain]; + keyFileField = [@(UINT8_C(2)) retain]; } -- init +- (instancetype)init { self = [super init]; @try { - void *pool = objc_autoreleasePoolPush(); - OFFileManager *fileManager = [OFFileManager defaultManager]; - OFString *userDataPath = [OFSystemInfo userDataPath]; - - if (![fileManager directoryExistsAtPath: userDataPath]) - [fileManager createDirectoryAtPath: userDataPath]; - - _path = [[userDataPath stringByAppendingPathComponent: - @"sites.msgpack"] retain]; - - @try { - _storage = [[[OFData dataWithContentsOfFile: _path] - messagePackValue] mutableCopy]; - } @catch (id e) { - _storage = [[OFMutableDictionary alloc] init]; - } - - _sites = [[[_storage allKeys] sortedArray] retain]; - - objc_autoreleasePoolPop(pool); + @autoreleasepool { + OFFileManager *fileManager = + OFFileManager.defaultManager; + OFString *userDataPath = OFSystemInfo.userDataPath; + + if (![fileManager directoryExistsAtPath: userDataPath]) + [fileManager + createDirectoryAtPath: userDataPath]; + + _path = [[userDataPath stringByAppendingPathComponent: + @"sites.msgpack"] copy]; + + @try { + _storage = [[OFData dataWithContentsOfFile: + _path].messagePackValue mutableCopy]; + } @catch (id e) { + _storage = [[OFMutableDictionary alloc] init]; + } + + _sites = [_storage.allKeys.sortedArray retain]; + } } @catch (id e) { [self release]; @throw e; } @@ -79,25 +81,29 @@ [super dealloc]; } - (OFArray *)sitesWithFilter: (OFString *)filter { - void *pool = objc_autoreleasePoolPush(); - /* - * FIXME: We need case folding here, but there is no method for it yet. - */ - filter = [filter lowercaseString]; - OFArray *sites = [[[_storage allKeys] sortedArray] - filteredArrayUsingBlock: ^ (id name, size_t index) { - if (filter == nil) - return true; - - return [[name lowercaseString] containsString: filter]; - }]; - - [sites retain]; - objc_autoreleasePoolPop(pool); + OFArray *sites; + + @autoreleasepool { + /* + * FIXME: We need case folding here, but there is no method for + * it yet. + */ + filter = filter.lowercaseString; + sites = [_storage.allKeys.sortedArray + filteredArrayUsingBlock: ^ (OFString *name, size_t index) { + if (filter == nil) + return true; + + return [name.lowercaseString containsString: filter]; + }]; + + [sites retain]; + } + return [sites autorelease]; } - (bool)hasSite: (OFString *)name { @@ -104,41 +110,63 @@ return (_storage[name] != nil); } - (size_t)lengthForSite: (OFString *)name { - OFDictionary *site = _storage[name]; + OFDictionary *site = _storage[name]; if (site == nil) @throw [OFInvalidArgumentException exception]; return [site[lengthField] sizeValue]; } - (bool)isSiteLegacy: (OFString *)name { - OFDictionary *site = _storage[name]; + OFDictionary *site = _storage[name]; if (site == nil) @throw [OFInvalidArgumentException exception]; return [site[legacyField] boolValue]; } + +- (OFString *)keyFileForSite: (OFString *)name +{ + OFDictionary *site = _storage[name]; + OFString *keyFile; + + if (site == nil) + @throw [OFInvalidArgumentException exception]; + + keyFile = site[keyFileField]; + + if ([keyFile isEqual: [OFNull null]]) + return nil; + + return keyFile; +} - (void)setSite: (OFString *)site length: (size_t)length legacy: (bool)legacy -{ - void *pool = objc_autoreleasePoolPush(); - - _storage[site] = @{ - lengthField: @(length), - legacyField: @(legacy) - }; - [self _update]; - - objc_autoreleasePoolPop(pool); + keyFile: (OFString *)keyFile +{ + @autoreleasepool { + OFMutableDictionary *siteDictionary = + [OFMutableDictionary dictionary]; + + siteDictionary[lengthField] = @(length); + siteDictionary[legacyField] = @(legacy); + siteDictionary[keyFileField] = keyFile; + + [siteDictionary makeImmutable]; + + _storage[site] = siteDictionary; + + [self _update]; + } } - (void)removeSite: (OFString *)name { [_storage removeObjectForKey: name]; @@ -145,15 +173,13 @@ [self _update]; } - (void)_update { - void *pool = objc_autoreleasePoolPush(); - - [[_storage messagePackRepresentation] writeToFile: _path]; - - [_sites release]; - _sites = [[[_storage allKeys] sortedArray] retain]; - - objc_autoreleasePoolPop(pool); + @autoreleasepool { + [_storage.messagePackRepresentation writeToFile: _path]; + + [_sites release]; + _sites = [_storage.allKeys.sortedArray retain]; + } } @end