Index: LegacyPasswordGenerator.h ================================================================== --- LegacyPasswordGenerator.h +++ LegacyPasswordGenerator.h @@ -24,9 +24,10 @@ @interface LegacyPasswordGenerator: OFObject { size_t _length; OFString *_site; + OFData *_keyfile; const char *_passphrase; unsigned char *_output; } @end Index: LegacyPasswordGenerator.m ================================================================== --- LegacyPasswordGenerator.m +++ LegacyPasswordGenerator.m @@ -21,11 +21,12 @@ */ #import "LegacyPasswordGenerator.h" @implementation LegacyPasswordGenerator -@synthesize site = _site, passphrase = _passphrase, output = _output; +@synthesize site = _site, keyfile = _keyfile, passphrase = _passphrase; +@synthesize output = _output; + (instancetype)generator { return [[[self alloc] init] autorelease]; } @@ -53,10 +54,13 @@ } - (void)derivePassword { OFSHA256Hash *siteHash = [OFSHA256Hash cryptoHash]; + size_t passphraseLength, combinedPassphraseLength; + char *combinedPassphrase; + [siteHash updateWithBuffer: [_site UTF8String] length: [_site UTF8StringLength]]; if (_output != NULL) { of_explicit_memset(_output, 0, _length); @@ -63,13 +67,36 @@ [self freeMemory: _output]; } _output = [self allocMemoryWithSize: _length + 1]; - of_scrypt(8, 524288, 2, [siteHash digest], - [[siteHash class] digestSize], _passphrase, strlen(_passphrase), - _output, _length); + passphraseLength = combinedPassphraseLength = strlen(_passphrase); + if (_keyfile != nil) { + if (SIZE_MAX - combinedPassphraseLength < [_keyfile count]) + @throw [OFOutOfRangeException exception]; + + combinedPassphraseLength += [_keyfile count]; + } + + if ((combinedPassphrase = malloc(combinedPassphraseLength)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: combinedPassphraseLength]; + @try { + memcpy(combinedPassphrase, _passphrase, passphraseLength); + + if (_keyfile != nil) + memcpy(combinedPassphrase + passphraseLength, + [_keyfile items], [_keyfile count]); + + of_scrypt(8, 524288, 2, [siteHash digest], + [[siteHash class] digestSize], combinedPassphrase, + combinedPassphraseLength, _output, _length); + } @finally { + of_explicit_memset(combinedPassphrase, 0, + combinedPassphraseLength); + free(combinedPassphrase); + } /* * This has a bias, however, this is what scrypt-genpass does and the * legacy mode wants to be compatible to scrypt-genpass. */ Index: NewPasswordGenerator.h ================================================================== --- NewPasswordGenerator.h +++ NewPasswordGenerator.h @@ -24,9 +24,10 @@ @interface NewPasswordGenerator: OFObject { size_t _length; OFString *_site; + OFData *_keyfile; const char *_passphrase; unsigned char *_output; } @end Index: NewPasswordGenerator.m ================================================================== --- NewPasswordGenerator.m +++ NewPasswordGenerator.m @@ -21,12 +21,12 @@ */ #import "NewPasswordGenerator.h" @implementation NewPasswordGenerator -@synthesize length = _length, site = _site, passphrase = _passphrase; -@synthesize output = _output; +@synthesize length = _length, site = _site, keyfile = _keyfile; +@synthesize passphrase = _passphrase, output = _output; + (instancetype)generator { return [[[self alloc] init] autorelease]; } @@ -41,10 +41,13 @@ } - (void)derivePassword { OFSHA384Hash *siteHash = [OFSHA384Hash cryptoHash]; + size_t passphraseLength, combinedPassphraseLength; + char *combinedPassphrase; + [siteHash updateWithBuffer: [_site UTF8String] length: [_site UTF8StringLength]]; if (_output != NULL) { of_explicit_memset(_output, 0, _length); @@ -51,17 +54,40 @@ [self freeMemory: _output]; } _output = [self allocMemoryWithSize: _length + 1]; - of_scrypt(8, 524288, 2, [siteHash digest], - [[siteHash class] digestSize], _passphrase, strlen(_passphrase), - _output, _length); + passphraseLength = combinedPassphraseLength = strlen(_passphrase); + if (_keyfile != nil) { + if (SIZE_MAX - combinedPassphraseLength < [_keyfile count]) + @throw [OFOutOfRangeException exception]; + + combinedPassphraseLength += [_keyfile count]; + } + + if ((combinedPassphrase = malloc(combinedPassphraseLength)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: combinedPassphraseLength]; + @try { + memcpy(combinedPassphrase, _passphrase, passphraseLength); + + if (_keyfile != nil) + memcpy(combinedPassphrase + passphraseLength, + [_keyfile items], [_keyfile count]); + + of_scrypt(8, 524288, 2, [siteHash digest], + [[siteHash class] digestSize], combinedPassphrase, + combinedPassphraseLength, _output, _length); + } @finally { + of_explicit_memset(combinedPassphrase, 0, + combinedPassphraseLength); + free(combinedPassphrase); + } for (size_t i = 0; i < _length; i++) _output[i] = "123456789" "abcdefghijkmnopqrstuvwxyz" "ABCDEFGHJKLMNPQRSTUVWXYZ" "#$%-=?"[_output[i] & 0x3F]; } @end Index: PasswordGenerator.h ================================================================== --- PasswordGenerator.h +++ PasswordGenerator.h @@ -21,13 +21,14 @@ */ #import @protocol PasswordGenerator -@property size_t length; -@property (nonatomic, copy) OFString *site; -@property const char *passphrase; -@property (readonly) unsigned char *output; +@property (nonatomic) size_t length; +@property (copy, nonatomic) OFString *site; +@property (retain, nonatomic) OFData *keyfile; +@property (nonatomic) const char *passphrase; +@property (readonly, nonatomic) unsigned char *output; + (instancetype)generator; - (void)derivePassword; @end Index: ScryptPWGen.m ================================================================== --- ScryptPWGen.m +++ ScryptPWGen.m @@ -37,32 +37,36 @@ if (verbose) [output writeString: @"\n" @"Options:\n" @" -h --help Show this help\n" + @" -k --keyfile Use the specified key file\n" @" -l --length Length for the derived password\n" @" -L --legacy Use the legacy algorithm " @"(compatible with scrypt-genpass)\n" @" -r --repeat Repeat input\n"]; } @implementation ScryptPWGen - (void)applicationDidFinishLaunching { - OFString *lengthStr; + OFString *keyfilePath, *lengthString; const of_options_parser_option_t options[] = { { 'h', @"help", 0, NULL, NULL }, - { 'l', @"length", 1, NULL, &lengthStr }, + { '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; - char *passphrase; + OFMutableData *keyfile = nil; OFString *prompt; + const char *promptCString; + char *passphrase; while ((option = [optionsParser nextOption]) != '\0') { switch (option) { case 'h': showHelp(of_stdout, true); @@ -110,35 +114,38 @@ id generator = (_legacy ? [LegacyPasswordGenerator generator] : [NewPasswordGenerator generator]); generator.site = [[optionsParser remainingArguments] firstObject]; - if (lengthStr != nil) { + if (lengthString != nil) { bool invalid = false; @try { - generator.length = (size_t)[lengthStr decimalValue]; + generator.length = (size_t)[lengthString decimalValue]; } @catch (OFInvalidFormatException *e) { invalid = true; } @catch (OFOutOfRangeException *e) { invalid = true; } if (invalid) { [of_stderr writeFormat: @"%@: Invalid length: %@\n", - [OFApplication programName], lengthStr]; + [OFApplication programName], lengthString]; [OFApplication terminateWithStatus: 1]; } } - prompt = [OFString stringWithFormat: @"Passphrase for site \"%@\": ", generator.site]; - passphrase = getpass( - [prompt cStringWithEncoding: [OFLocalization encoding]]); + promptCString = [prompt cStringWithEncoding: [OFLocalization encoding]]; + + if (keyfilePath != nil) + keyfile = [OFMutableData dataWithContentsOfFile: keyfilePath]; + + passphrase = getpass(promptCString); @try { if (_repeat) { char *passphraseCopy = of_strdup(passphrase); if (passphraseCopy == NULL) @@ -164,10 +171,11 @@ strlen(passphraseCopy)); free(passphraseCopy); } } + generator.keyfile = keyfile; generator.passphrase = passphrase; [generator derivePassword]; @try { [of_stdout writeBuffer: generator.output @@ -178,10 +186,13 @@ of_explicit_memset(generator.output, 0, generator.length); } } @finally { of_explicit_memset(passphrase, 0, strlen(passphrase)); + + if (keyfile != nil) + of_explicit_memset([keyfile items], 0, [keyfile count]); } [OFApplication terminate]; } @end