From ccc0706caa206208b8e78723782c44e82ee49668 Mon Sep 17 00:00:00 2001 From: Jonathan Schleifer Date: Sun, 26 Nov 2017 16:01:01 +0100 Subject: [PATCH] Add support for using a keyfile --- LegacyPasswordGenerator.h | 1 + LegacyPasswordGenerator.m | 35 +++++++++++++++++++++++++++++++---- NewPasswordGenerator.h | 1 + NewPasswordGenerator.m | 36 +++++++++++++++++++++++++++++++----- PasswordGenerator.h | 9 +++++---- ScryptPWGen.m | 29 ++++++++++++++++++++--------- 6 files changed, 89 insertions(+), 22 deletions(-) diff --git a/LegacyPasswordGenerator.h b/LegacyPasswordGenerator.h index 7a102b8..5cfbc22 100644 --- a/LegacyPasswordGenerator.h +++ b/LegacyPasswordGenerator.h @@ -26,6 +26,7 @@ { size_t _length; OFString *_site; + OFData *_keyfile; const char *_passphrase; unsigned char *_output; } diff --git a/LegacyPasswordGenerator.m b/LegacyPasswordGenerator.m index a9d2595..327218c 100644 --- a/LegacyPasswordGenerator.m +++ b/LegacyPasswordGenerator.m @@ -23,7 +23,8 @@ #import "LegacyPasswordGenerator.h" @implementation LegacyPasswordGenerator -@synthesize site = _site, passphrase = _passphrase, output = _output; +@synthesize site = _site, keyfile = _keyfile, passphrase = _passphrase; +@synthesize output = _output; + (instancetype)generator { @@ -55,6 +56,9 @@ - (void)derivePassword { OFSHA256Hash *siteHash = [OFSHA256Hash cryptoHash]; + size_t passphraseLength, combinedPassphraseLength; + char *combinedPassphrase; + [siteHash updateWithBuffer: [_site UTF8String] length: [_site UTF8StringLength]]; @@ -65,9 +69,32 @@ _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 diff --git a/NewPasswordGenerator.h b/NewPasswordGenerator.h index ce97d49..6b89997 100644 --- a/NewPasswordGenerator.h +++ b/NewPasswordGenerator.h @@ -26,6 +26,7 @@ { size_t _length; OFString *_site; + OFData *_keyfile; const char *_passphrase; unsigned char *_output; } diff --git a/NewPasswordGenerator.m b/NewPasswordGenerator.m index 9625fdc..a8a1c14 100644 --- a/NewPasswordGenerator.m +++ b/NewPasswordGenerator.m @@ -23,8 +23,8 @@ #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 { @@ -43,6 +43,9 @@ - (void)derivePassword { OFSHA384Hash *siteHash = [OFSHA384Hash cryptoHash]; + size_t passphraseLength, combinedPassphraseLength; + char *combinedPassphrase; + [siteHash updateWithBuffer: [_site UTF8String] length: [_site UTF8StringLength]]; @@ -53,9 +56,32 @@ _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] = diff --git a/PasswordGenerator.h b/PasswordGenerator.h index c94e53f..e03d57f 100644 --- a/PasswordGenerator.h +++ b/PasswordGenerator.h @@ -23,10 +23,11 @@ #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; diff --git a/ScryptPWGen.m b/ScryptPWGen.m index aa7d5f5..46f730c 100644 --- a/ScryptPWGen.m +++ b/ScryptPWGen.m @@ -39,6 +39,7 @@ showHelp(OFStream *output, bool verbose) @"\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" @@ -48,10 +49,11 @@ showHelp(OFStream *output, bool verbose) @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 } @@ -59,8 +61,10 @@ showHelp(OFStream *output, bool verbose) 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) { @@ -112,11 +116,11 @@ showHelp(OFStream *output, bool verbose) : [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) { @@ -126,17 +130,20 @@ showHelp(OFStream *output, bool verbose) 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); @@ -166,6 +173,7 @@ showHelp(OFStream *output, bool verbose) } } + generator.keyfile = keyfile; generator.passphrase = passphrase; [generator derivePassword]; @@ -180,6 +188,9 @@ showHelp(OFStream *output, bool verbose) } } @finally { of_explicit_memset(passphrase, 0, strlen(passphrase)); + + if (keyfile != nil) + of_explicit_memset([keyfile items], 0, [keyfile count]); } [OFApplication terminate];