Add support for using a keyfile

This commit is contained in:
Jonathan Schleifer 2017-11-26 16:01:01 +01:00
parent 60b483c499
commit ccc0706caa
No known key found for this signature in database
GPG key ID: 28D65178B37F33E3
6 changed files with 89 additions and 22 deletions

View file

@ -26,6 +26,7 @@
{ {
size_t _length; size_t _length;
OFString *_site; OFString *_site;
OFData *_keyfile;
const char *_passphrase; const char *_passphrase;
unsigned char *_output; unsigned char *_output;
} }

View file

@ -23,7 +23,8 @@
#import "LegacyPasswordGenerator.h" #import "LegacyPasswordGenerator.h"
@implementation LegacyPasswordGenerator @implementation LegacyPasswordGenerator
@synthesize site = _site, passphrase = _passphrase, output = _output; @synthesize site = _site, keyfile = _keyfile, passphrase = _passphrase;
@synthesize output = _output;
+ (instancetype)generator + (instancetype)generator
{ {
@ -55,6 +56,9 @@
- (void)derivePassword - (void)derivePassword
{ {
OFSHA256Hash *siteHash = [OFSHA256Hash cryptoHash]; OFSHA256Hash *siteHash = [OFSHA256Hash cryptoHash];
size_t passphraseLength, combinedPassphraseLength;
char *combinedPassphrase;
[siteHash updateWithBuffer: [_site UTF8String] [siteHash updateWithBuffer: [_site UTF8String]
length: [_site UTF8StringLength]]; length: [_site UTF8StringLength]];
@ -65,9 +69,32 @@
_output = [self allocMemoryWithSize: _length + 1]; _output = [self allocMemoryWithSize: _length + 1];
of_scrypt(8, 524288, 2, [siteHash digest], passphraseLength = combinedPassphraseLength = strlen(_passphrase);
[[siteHash class] digestSize], _passphrase, strlen(_passphrase), if (_keyfile != nil) {
_output, _length); 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 * This has a bias, however, this is what scrypt-genpass does and the

View file

@ -26,6 +26,7 @@
{ {
size_t _length; size_t _length;
OFString *_site; OFString *_site;
OFData *_keyfile;
const char *_passphrase; const char *_passphrase;
unsigned char *_output; unsigned char *_output;
} }

View file

@ -23,8 +23,8 @@
#import "NewPasswordGenerator.h" #import "NewPasswordGenerator.h"
@implementation NewPasswordGenerator @implementation NewPasswordGenerator
@synthesize length = _length, site = _site, passphrase = _passphrase; @synthesize length = _length, site = _site, keyfile = _keyfile;
@synthesize output = _output; @synthesize passphrase = _passphrase, output = _output;
+ (instancetype)generator + (instancetype)generator
{ {
@ -43,6 +43,9 @@
- (void)derivePassword - (void)derivePassword
{ {
OFSHA384Hash *siteHash = [OFSHA384Hash cryptoHash]; OFSHA384Hash *siteHash = [OFSHA384Hash cryptoHash];
size_t passphraseLength, combinedPassphraseLength;
char *combinedPassphrase;
[siteHash updateWithBuffer: [_site UTF8String] [siteHash updateWithBuffer: [_site UTF8String]
length: [_site UTF8StringLength]]; length: [_site UTF8StringLength]];
@ -53,9 +56,32 @@
_output = [self allocMemoryWithSize: _length + 1]; _output = [self allocMemoryWithSize: _length + 1];
of_scrypt(8, 524288, 2, [siteHash digest], passphraseLength = combinedPassphraseLength = strlen(_passphrase);
[[siteHash class] digestSize], _passphrase, strlen(_passphrase), if (_keyfile != nil) {
_output, _length); 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++) for (size_t i = 0; i < _length; i++)
_output[i] = _output[i] =

View file

@ -23,10 +23,11 @@
#import <ObjFW/ObjFW.h> #import <ObjFW/ObjFW.h>
@protocol PasswordGenerator @protocol PasswordGenerator
@property size_t length; @property (nonatomic) size_t length;
@property (nonatomic, copy) OFString *site; @property (copy, nonatomic) OFString *site;
@property const char *passphrase; @property (retain, nonatomic) OFData *keyfile;
@property (readonly) unsigned char *output; @property (nonatomic) const char *passphrase;
@property (readonly, nonatomic) unsigned char *output;
+ (instancetype)generator; + (instancetype)generator;
- (void)derivePassword; - (void)derivePassword;

View file

@ -39,6 +39,7 @@ showHelp(OFStream *output, bool verbose)
@"\n" @"\n"
@"Options:\n" @"Options:\n"
@" -h --help Show this help\n" @" -h --help Show this help\n"
@" -k --keyfile Use the specified key file\n"
@" -l --length Length for the derived password\n" @" -l --length Length for the derived password\n"
@" -L --legacy Use the legacy algorithm " @" -L --legacy Use the legacy algorithm "
@"(compatible with scrypt-genpass)\n" @"(compatible with scrypt-genpass)\n"
@ -48,10 +49,11 @@ showHelp(OFStream *output, bool verbose)
@implementation ScryptPWGen @implementation ScryptPWGen
- (void)applicationDidFinishLaunching - (void)applicationDidFinishLaunching
{ {
OFString *lengthStr; OFString *keyfilePath, *lengthString;
const of_options_parser_option_t options[] = { const of_options_parser_option_t options[] = {
{ 'h', @"help", 0, NULL, NULL }, { '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 }, { 'L', @"legacy", 0, &_legacy, NULL },
{ 'r', @"repeat", 0, &_repeat, NULL }, { 'r', @"repeat", 0, &_repeat, NULL },
{ '\0', nil, 0, NULL, NULL } { '\0', nil, 0, NULL, NULL }
@ -59,8 +61,10 @@ showHelp(OFStream *output, bool verbose)
OFOptionsParser *optionsParser = OFOptionsParser *optionsParser =
[OFOptionsParser parserWithOptions: options]; [OFOptionsParser parserWithOptions: options];
of_unichar_t option; of_unichar_t option;
char *passphrase; OFMutableData *keyfile = nil;
OFString *prompt; OFString *prompt;
const char *promptCString;
char *passphrase;
while ((option = [optionsParser nextOption]) != '\0') { while ((option = [optionsParser nextOption]) != '\0') {
switch (option) { switch (option) {
@ -112,11 +116,11 @@ showHelp(OFStream *output, bool verbose)
: [NewPasswordGenerator generator]); : [NewPasswordGenerator generator]);
generator.site = [[optionsParser remainingArguments] firstObject]; generator.site = [[optionsParser remainingArguments] firstObject];
if (lengthStr != nil) { if (lengthString != nil) {
bool invalid = false; bool invalid = false;
@try { @try {
generator.length = (size_t)[lengthStr decimalValue]; generator.length = (size_t)[lengthString decimalValue];
} @catch (OFInvalidFormatException *e) { } @catch (OFInvalidFormatException *e) {
invalid = true; invalid = true;
} @catch (OFOutOfRangeException *e) { } @catch (OFOutOfRangeException *e) {
@ -126,17 +130,20 @@ showHelp(OFStream *output, bool verbose)
if (invalid) { if (invalid) {
[of_stderr writeFormat: [of_stderr writeFormat:
@"%@: Invalid length: %@\n", @"%@: Invalid length: %@\n",
[OFApplication programName], lengthStr]; [OFApplication programName], lengthString];
[OFApplication terminateWithStatus: 1]; [OFApplication terminateWithStatus: 1];
} }
} }
prompt = [OFString stringWithFormat: @"Passphrase for site \"%@\": ", prompt = [OFString stringWithFormat: @"Passphrase for site \"%@\": ",
generator.site]; generator.site];
passphrase = getpass( promptCString = [prompt cStringWithEncoding: [OFLocalization encoding]];
[prompt cStringWithEncoding: [OFLocalization encoding]]);
if (keyfilePath != nil)
keyfile = [OFMutableData dataWithContentsOfFile: keyfilePath];
passphrase = getpass(promptCString);
@try { @try {
if (_repeat) { if (_repeat) {
char *passphraseCopy = of_strdup(passphrase); char *passphraseCopy = of_strdup(passphrase);
@ -166,6 +173,7 @@ showHelp(OFStream *output, bool verbose)
} }
} }
generator.keyfile = keyfile;
generator.passphrase = passphrase; generator.passphrase = passphrase;
[generator derivePassword]; [generator derivePassword];
@ -180,6 +188,9 @@ showHelp(OFStream *output, bool verbose)
} }
} @finally { } @finally {
of_explicit_memset(passphrase, 0, strlen(passphrase)); of_explicit_memset(passphrase, 0, strlen(passphrase));
if (keyfile != nil)
of_explicit_memset([keyfile items], 0, [keyfile count]);
} }
[OFApplication terminate]; [OFApplication terminate];