diff options
Diffstat (limited to 'src/autopasswd.c')
| -rw-r--r-- | src/autopasswd.c | 799 |
1 files changed, 359 insertions, 440 deletions
diff --git a/src/autopasswd.c b/src/autopasswd.c index d954024..c82f475 100644 --- a/src/autopasswd.c +++ b/src/autopasswd.c @@ -23,8 +23,7 @@ #include <passphrase.h> #include <argparser.h> - -#include "sha3.h" +#include <libkeccak.h> @@ -46,44 +45,24 @@ /** * The radix 64 characters (66 characters), the two last ones are for padding */ -#ifndef BASE64 -# define BASE64 "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,.-=" -#endif +#define BASE64 "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,.-=" /** * The number of squeezes to do at bump level zero */ -#ifndef KECCAK_SQUEEZES -# define KECCAK_SQUEEZES 300000 -#endif - -/** - * The default output parameter for the Keccak sponge - */ -#ifndef KECCAK_OUTPUT -# define KECCAK_OUTPUT 512 -#endif - -/** - * The default state size parameter for the Keccak sponge - */ -#ifndef KECCAK_STATE_SIZE -# define KECCAK_STATE_SIZE 1600 -#endif +#define DEFAULT_SQUEEZES 300000 /** * The number of addition squeezes to perform per bump level */ -#ifndef BUMP_LEVEL_MULTIPLIER -# define BUMP_LEVEL_MULTIPLIER 5000 -#endif +#define BUMP_LEVEL_MULTIPLIER 5000 /** - * The bitrate parameter for the Keccak sponge when hashing master passphrase + * The rate parameter for the Keccak sponge when hashing master passphrase */ -#ifndef MASTER_PASSPHRASE_KECCAK_BITRATE -# define MASTER_PASSPHRASE_KECCAK_BITRATE 576 +#ifndef MASTER_PASSPHRASE_KECCAK_RATE +# define MASTER_PASSPHRASE_KECCAK_RATE 576 #endif /** @@ -107,99 +86,123 @@ # define MASTER_PASSPHRASE_KECCAK_SQUEEZES 10000 #endif + + +#define USER_ERROR(string) \ + (fprintf(stderr, "%s: %s.\n", execname, string), 1) + +#define ADD(arg, desc, ...) \ + (arg ? args_add_option(args_new_argumented(NULL, arg, 0, __VA_ARGS__, NULL), desc) \ + : args_add_option(args_new_argumentless(NULL, 0, __VA_ARGS__, NULL), desc)) + +#define LAST(arg) \ + (args_opts_get(arg)[args_opts_get_count(arg) - 1]) + +#define t(expr) if ((r = (expr))) goto fail + +#define xfree(s) (free(s), s = NULL) + + + /** - * The hexadecimal alphabet + * `argv[0]` from `main` */ -#ifndef HEXADECA -# define HEXADECA "0123456789abcdef" -#endif +static char* execname; -static char* last_arg(char* arg) +/** + * Convert `libkeccak_generalised_spec_t` to `libkeccak_spec_t` and check for errors + * + * @param gspec See `libkeccak_degeneralise_spec` + * @param spec See `libkeccak_degeneralise_spec` + * @return Zero on success, an appropriate exit value on error + */ +static int make_spec(libkeccak_generalised_spec_t* gspec, libkeccak_spec_t* spec) { - return *(args_opts_get(arg) + (args_opts_get_count(arg) - 1)); + int r; + +#define TEST(CASE, STR) case LIBKECCAK_GENERALISED_SPEC_ERROR_##CASE: return USER_ERROR(STR) + if (r = libkeccak_degeneralise_spec(gspec, spec), r) + switch (r) + { + TEST (STATE_NONPOSITIVE, "the state size must be positive"); + TEST (STATE_TOO_LARGE, "the state size is too large, may not exceed 1600"); + TEST (STATE_MOD_25, "the state size must be a multiple of 25"); + TEST (WORD_NONPOSITIVE, "the word size must be positive"); + TEST (WORD_TOO_LARGE, "the word size is too large, may not exceed 64"); + TEST (STATE_WORD_INCOHERENCY, "the state size must be exactly 25 times the word size"); + TEST (CAPACITY_NONPOSITIVE, "the capacity must be positive"); + TEST (CAPACITY_MOD_8, "the capacity must be a multiple of 8"); + TEST (BITRATE_NONPOSITIVE, "the rate must be positive"); + TEST (BITRATE_MOD_8, "the rate must be a multiple of 8"); + TEST (OUTPUT_NONPOSITIVE, "the output size must be positive"); + default: + return USER_ERROR("unknown error in algorithm parameters"); + } +#undef TEST + +#define TEST(CASE, STR) case LIBKECCAK_SPEC_ERROR_##CASE: return USER_ERROR(STR) + if (r = libkeccak_spec_check(spec), r) + switch (r) + { + TEST (BITRATE_NONPOSITIVE, "the rate size must be positive"); + TEST (BITRATE_MOD_8, "the rate must be a multiple of 8"); + TEST (CAPACITY_NONPOSITIVE, "the capacity must be positive"); + TEST (CAPACITY_MOD_8, "the capacity must be a multiple of 8"); + TEST (OUTPUT_NONPOSITIVE, "the output size must be positive"); + TEST (STATE_TOO_LARGE, "the state size is too large, may not exceed 1600"); + TEST (STATE_MOD_25, "the state size must be a multiple of 25"); + TEST (WORD_NON_2_POTENT, "the word size must be a power of 2"); + TEST (WORD_MOD_8, "the word size must be a multiple of 8"); + default: + return USER_ERROR("unknown error in algorithm parameters"); + } +#undef TEST + + return 0; } /** - * Here we go! + * Parse the command line arguments + * + * @param argc The number of command line argumnets + * @param argv Command line argumnets + * @param gspec Output parameter for the algorithm parameters, must already be initialised + * @param squeezes Output parameter for the number of squeezes to perform, must already have default value + * @param bump_level Output parameter for the bump level, must already be zero + * @param clear_mode Output parameter for the clear mode setting, must already be zero + * @param verbose Output parameter for the verbosity setting, must already be zero + * @return Zero on success, an appropriate exit value on error, however -1 if + * the program should exit with value zero */ -int main(int argc, char** argv) +static int parse_cmdline(int argc, char* argv[], libkeccak_generalised_spec_t* gspec, + long* squeezes, long* bump_level, int* clear_mode, int* verbose) { - size_t site_size = 64; - long bump_level = 0; - int clear_mode = 0; - int verbose_mode = 0; - long keccak_output_ = KECCAK_OUTPUT; - long keccak_state_size_ = KECCAK_STATE_SIZE; - long keccak_capacity_ = keccak_state_size_ - (keccak_output_ << 1); - long keccak_bitrate_ = keccak_state_size_ - keccak_capacity_; - long keccak_squeezes = KECCAK_SQUEEZES; - int output__ = 0; - int state_size__ = 0; - int capacity__ = 0; - int bitrate__ = 0; - int word_size__ = 0; - int squeezes__ = 0; - long output_, keccak_output; - long state_size_, keccak_state_size; - long capacity_, keccak_capacity; - long bitrate_, keccak_bitrate; - long word_size_, keccak_word_size; - long squeezes_; - byte* site; - char* passphrase; - byte* passphrase_hash; - int_fast8_t* digest; - char* base64; - size_t ptr64; - size_t ptr; - char* master_passphrase_hash; - size_t passphrase_n; - size_t site_n; - - - /* Parse command line arguments. */ args_init("Reproducable password generator", "autopasswd [options...]", - "TODO", 0, 1, 0, args_standard_abbreviations); + NULL, NULL, 1, 0, args_standard_abbreviations); - args_add_option(args_new_argumentless(NULL, 0, "-h", "-?", "--help", NULL), - "Display this help message"); - args_add_option(args_new_argumentless(NULL, 0, "+c", "--copyright", "--copying", NULL), - "Display copyright information"); - args_add_option(args_new_argumentless(NULL, 0, "+w", "--warranty", NULL), - "Display warranty disclaimer"); - args_add_option(args_new_argumentless(NULL, 0, "-v", "--verbose", NULL), - "Display extra information"); - args_add_option(args_new_argumented(NULL, "INT", 0, "-b", "--bump-level", NULL), - "Select bump level, can contain + or - to perform accumulated adjustment"); - args_add_option(args_new_argumentless(NULL, 0, "-c", "--clear-mode", NULL), - "Do not hide the output, but rather make it ease to pass into another program\n" - "Use twice to suppress terminal line break"); - args_add_option(args_new_argumented(NULL, "INT", 0, "-O", "--output", NULL), - "Select output parameter for Keccak sponge"); - args_add_option(args_new_argumented(NULL, "INT", 0, "-S", "--state-size", NULL), - "Select state size parameter for Keccak sponge"); - args_add_option(args_new_argumented(NULL, "INT", 0, "-C", "--capacity", NULL), - "Select capacity parameter for Keccak sponge"); - args_add_option(args_new_argumented(NULL, "INT", 0, "-R", "--rate", "--bitrate", NULL), - "Select bitrate parameter for Keccak sponge"); - args_add_option(args_new_argumented(NULL, "INT", 0, "-W", "--word-size", NULL), - "Select word size parameter for Keccak sponge"); - args_add_option(args_new_argumented(NULL, "INT", 0, "-Z", "--squeezes", NULL), - "Select the number squeezes performe on the Keccak sponge at bump level zero"); + ADD(NULL, "Display option summary", "-h", "-?", "--help"); + ADD(NULL, "Display copyright information", "+c", "--copyright", "--copying"); + ADD(NULL, "Display warranty disclaimer", "+w", "--warranty"); + ADD(NULL, "Display extra information", "-v", "--verbose"); + ADD(NULL, "Do not hide the output, but rather make it ease to pass into another program\n" + "Use twice to suppress terminal line break", "-c", "--clear-mode"); + ADD("LEVEL", "Select bump level, can contain + or - to perform accumulated adjustment", + "-b", "--bump-level"); + ADD("RATE", "Select rate parameter for Keccak sponge", "-R", "--bitrate", "--rate"); + ADD("CAPACITY", "Select capacity parameter for Keccak sponge", "-C", "--capacity"); + ADD("SIZE", "Select output parameter for Keccak sponge", "-N", "-O", "--output-size", "--output"); + ADD("SIZE", "Select state size parameter for Keccak sponge", "-S", "-B", "--state-size", "--state"); + ADD("SIZE", "Select word size parameter for Keccak sponge", "-W", "--word-size", "--word"); + ADD("COUNT", "Select the number squeezes performed on the Keccak sponge at bump level zero", + "-Z", "--squeezes"); args_parse(argc, argv); - args_support_alternatives(); - if (args_opts_used("--help")) - { - args_help(); - args_dispose(); - return 0; - } - if (args_opts_used("--copyright")) + if (args_opts_used("-h")) return args_help(0), args_dispose(), -1; + if (args_opts_used("+c")) { printf("autopasswd – Reproducable password generator\n"); printf("\n"); @@ -218,393 +221,309 @@ int main(int argc, char** argv) printf("You should have received a copy of the GNU Affero General Public License\n"); printf("along with this program. If not, see <http://www.gnu.org/licenses/>.\n"); args_dispose(); - return 0; + return -1; } - if (args_opts_used("--warranty")) + if (args_opts_used("+w")) { printf("This program is distributed in the hope that it will be useful,\n"); printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); printf("GNU Affero General Public License for more details.\n"); args_dispose(); - return 0; + return -1; } - if (args_opts_used("--clear-mode")) - { - clear_mode = (int)args_opts_get_count("--clear-mode"); - } - if (args_opts_used("--verbose")) - { - verbose_mode = 1; - } - if (args_opts_used("--bump-level")) + + if (args_opts_used("-R")) gspec->bitrate = atol(LAST("-R")); + if (args_opts_used("-C")) gspec->capacity = atol(LAST("-C")); + if (args_opts_used("-N")) gspec->output = atol(LAST("-N")); + if (args_opts_used("-S")) gspec->state_size = atol(LAST("-S")); + if (args_opts_used("-W")) gspec->word_size = atol(LAST("-W")); + if (args_opts_used("-Z")) *squeezes = atol(LAST("-Z")); + if (args_opts_used("-v")) *verbose = 1; + if (args_opts_used("-c")) *clear_mode = (int)args_opts_get_count("-c"); + if (args_opts_used("-b")) { - size_t n = (size_t)args_opts_get_count("--bump-level"); - char** arr = args_opts_get("--bump-level"); + size_t i, n = (size_t)args_opts_get_count("-b"); + char** arr = args_opts_get("-b"); char* arg; - for (ptr = 0; ptr < n; ptr++) - if ((arg = *(arr + ptr))) + for (i = 0; i < n; i++) + if ((arg = arr[i])) switch (*arg) { - case 0: - break; - case '+': - bump_level += atol(arg + 1); - break; - case '-': - bump_level -= atol(arg + 1); - break; - default: - bump_level = atol(arg); - break; + case 0: break; + case '+': *bump_level += atol(arg + 1); break; + case '-': *bump_level -= atol(arg + 1); break; + default: *bump_level = atol(arg); break; } } - if (args_opts_used("--output")) - { - output__ = 1; - output_ = atol(last_arg("--output")); - } - if (args_opts_used("--state-size")) - { - state_size__ = 1; - state_size_ = atol(last_arg("--state-size")); - } - if (args_opts_used("--capacity")) - { - capacity__ = 1; - capacity_ = atol(last_arg("--capacity")); - } - if (args_opts_used("--bitrate")) - { - bitrate__ = 1; - bitrate_ = atol(last_arg("--bitrate")); - } - if (args_opts_used("--word-size")) - { - word_size__ = 1; - word_size_ = atol(last_arg("--word-size")); - } - if (args_opts_used("--squeezes")) - { - squeezes__ = 1; - squeezes_ = atol(last_arg("--squeezes")); - } args_dispose(); + return 0; +} + + +/** + * Hash, and display, master passphrase so to hint + * the user whether it as typed correctly or not + * (important when creating a passphrase) + * + * @param passphrase The master passphrase + * @return Zero on success, an appropriate exit value on error + */ +static int hash_master_passphrase(const char* passphrase) +{ +#define SQUEEZES MASTER_PASSPHRASE_KECCAK_SQUEEZES + libkeccak_spec_t spec; + libkeccak_state_t state; + char hashsum[MASTER_PASSPHRASE_KECCAK_OUTPUT / 8]; + char hexsum[MASTER_PASSPHRASE_KECCAK_OUTPUT / 4 + 1]; - /* Get Keccak sponge parameters. */ - if (squeezes__) - { - keccak_squeezes = squeezes_; - if (keccak_squeezes == 0) - { - fprintf(stderr, "%s: do you really want your passphrase included in plain text?", *argv); - return 1; - } - else if (keccak_squeezes < 1) - { - fprintf(stderr, "%s: the squeeze count must be positive.", *argv); - return 1; - } - } - if (state_size__) - { - keccak_state_size = state_size_; - if ((keccak_state_size <= 0) || (keccak_state_size > 1600) || (keccak_state_size % 25)) - { - fprintf(stderr, "%s: the state size must be a positive multiple of 25 and is limited to 1600.", *argv); - return 1; - } - } - if (word_size__) - { - keccak_word_size = word_size_; - if ((keccak_word_size <= 0) || (keccak_word_size > 64)) - { - fprintf(stderr, "%s: the word size must be positive and is limited to 64.", *argv); - return 1; - } - if (state_size__ && (keccak_state_size != keccak_word_size * 25)) - { - fprintf(stderr, "%s: the state size must be 25 times of the word size.", *argv); - return 1; - } - else if (state_size__ == 0) - { - state_size__ = 1; - keccak_state_size = keccak_word_size * 25; - } - } - if (capacity__) - { - keccak_capacity = capacity_; - if ((keccak_capacity <= 0) || (keccak_capacity & 7)) - { - fprintf(stderr, "%s: the capacity must be a positive multiple of 8.", *argv); - return 1; - } - } - if (bitrate__) - { - keccak_bitrate = bitrate_; - if ((keccak_bitrate <= 0) || (keccak_bitrate & 7)) - { - fprintf(stderr, "%s: the bitrate must be a positive multiple of 8.", *argv); - return 1; - } - } - if (output__) - { - keccak_output = output_; - if (keccak_output <= 0) - { - fprintf(stderr, "%s: the output size must be positive.", *argv); - return 1; - } - } - if ((bitrate__ | capacity__ | output__) == 0) /* state_size? */ - { - keccak_state_size = state_size__ ? keccak_state_size : keccak_state_size_; - keccak_output = (((keccak_state_size << 5) / 100 + 7) >> 3) << 3; - keccak_bitrate = keccak_output << 1; - keccak_capacity = keccak_state_size - keccak_bitrate; - keccak_output = keccak_output < 8 ? 8 : keccak_output; - } - else if ((bitrate__ | capacity__) == 0) /* !output state_size? */ - { - keccak_bitrate = keccak_bitrate_; - keccak_capacity = keccak_capacity_; - keccak_state_size = state_size__ ? keccak_state_size : (keccak_bitrate + keccak_capacity); - } - else if (bitrate__ == 0) /* !bitrate output? state_size? */ - { - keccak_state_size = state_size__ ? keccak_state_size : keccak_state_size_; - keccak_bitrate = keccak_state_size - keccak_capacity; - keccak_output = output__ ? keccak_output : (keccak_capacity == 8 ? 8 : (keccak_capacity << 1)); - } - else if (capacity__ == 0) /* !bitrate output? state_size? */ - { - keccak_state_size = state_size__ ? keccak_state_size : keccak_state_size_; - keccak_capacity = keccak_state_size - keccak_bitrate; - keccak_output = output__ ? keccak_output : (keccak_capacity == 8 ? 8 : (keccak_capacity << 1)); - } - else /* !bitrate !capacity output? state_size? */ - { - keccak_state_size = state_size__ ? keccak_state_size : (keccak_bitrate + keccak_capacity); - keccak_output = output__ ? keccak_output : (keccak_capacity == 8 ? 8 : (keccak_capacity << 1)); - } - if (keccak_bitrate > keccak_state_size) - { - fprintf(stderr, "%s: the bitrate must not be higher than the state size.", *argv); - return 1; - } - if (keccak_capacity > keccak_state_size) - { - fprintf(stderr, "%s: the capacity must not be higher than the state size.", *argv); - return 1; - } - if (keccak_bitrate + keccak_capacity != keccak_state_size) - { - fprintf(stderr, "%s: the sum of the bitrate and the capacity must equal the state size.", *argv); - return 1; - } - keccak_squeezes += bump_level * BUMP_LEVEL_MULTIPLIER; - if (keccak_squeezes < 1) - { - fprintf(stderr, "%s: bump level is too low.", *argv); - return 1; - } - keccak_word_size = keccak_state_size / 25; + spec.bitrate = MASTER_PASSPHRASE_KECCAK_RATE; + spec.capacity = MASTER_PASSPHRASE_KECCAK_CAPACITY; + spec.output = MASTER_PASSPHRASE_KECCAK_OUTPUT; - /* Display verbose information. */ - if (verbose_mode) - { - fprintf(stderr, "Bump level: %li\n", bump_level); - fprintf(stderr, "Bitrate: %li\n", keccak_bitrate); - fprintf(stderr, "Capacity: %li\n", keccak_capacity); - fprintf(stderr, "Word size: %li\n", keccak_word_size); - fprintf(stderr, "State size: %li\n", keccak_state_size); - fprintf(stderr, "Output size: %li\n", keccak_output); - fprintf(stderr, "Squeezes (excluding bump level): %li\n", - keccak_squeezes - bump_level * BUMP_LEVEL_MULTIPLIER); - fprintf(stderr, "Squeezes (including bump level): %li\n", keccak_squeezes); - } + if (libkeccak_spec_check(&spec) || (SQUEEZES <= 0)) + return USER_ERROR("bad master passhprase hashing parameters, " + "please recompile autopasswd with with proper " + "values on MASTER_PASSPHRASE_KECCAK_RATE, " + "MASTER_PASSPHRASE_KECCAK_CAPACITY, " + "MASTER_PASSPHRASE_KECCAK_OUTPUT and " + "MASTER_PASSPHRASE_KECCAK_SQUEEZES"); + + if (libkeccak_state_initialise(&state, &spec)) + return perror(execname), 2; + + if (libkeccak_digest(&state, passphrase, strlen(passphrase), 0, NULL, SQUEEZES == 1 ? hashsum : NULL)) + return perror(execname), libkeccak_state_destroy(&state), 2; + if (SQUEEZES > 2) libkeccak_fast_squeeze(&state, SQUEEZES - 2); + if (SQUEEZES > 1) libkeccak_squeeze(&state, hashsum); + + libkeccak_state_destroy(&state); + + libkeccak_behex_lower(hexsum, hashsum, sizeof(hashsum) / sizeof(char)); + fprintf(stderr, "%s: master passphrase hash: %s\n", execname, hexsum); + + return 0; +#undef SQUEEZES +} + + +/** + * Ask the user for the site + * + * @param site Output parameter for the site + * @return Zero on success, an appropriate exit value on error + */ +static int get_site(char** site) +{ + size_t size = 64; + size_t ptr = 0; + char* buf = malloc(size * sizeof(char)); + if (buf == NULL) + return perror(execname), 2; - /* Read site. */ - site = malloc(site_size * sizeof(byte)); - if (site == NULL) - { - perror(*argv); - passphrase_disable_echo(); - return 1; - } fprintf(stderr, "%s", SITE_PROMPT_STRING); fflush(stderr); - for (site_n = 0;;) + + for (;;) { int c = getchar(); - if (site_n == site_size) + if (ptr == size) { - site = realloc(site, (site_size <<= 1) * sizeof(byte)); - if (site == NULL) - { - perror(*argv); - passphrase_disable_echo(); - return 1; - } + char* new = realloc(buf, (size <<= 1) * sizeof(char)); + if (new == NULL) + return perror(execname), free(buf), 2; } - if (c == -1) - { - free(site); - passphrase_disable_echo(); - return 0; - } - if (c == '\n') + if ((c < 0) || (c == '\n')) break; - *(site + site_n++) = (byte)c; + buf[ptr++] = (char)c; } - /* Disable echoing. (Should be done as soon as possible after reading site.) */ - passphrase_disable_echo(); - - /* Initialise Keccak sponge. */ - sha3_initialise(MASTER_PASSPHRASE_KECCAK_BITRATE, - MASTER_PASSPHRASE_KECCAK_CAPACITY, - MASTER_PASSPHRASE_KECCAK_OUTPUT); - - /* Read passphrease. */ - fprintf(stderr, "%s", PASSPHRASE_PROMPT_STRING); - fflush(stderr); - passphrase = passphrase_read(); - if (passphrase == NULL) + if (ptr == size) { - perror(*argv); - passphrase_reenable_echo(); - sha3_dispose(); - free(site); - return 1; + char* new = realloc(buf, (size += 1) * sizeof(char)); + if (new == NULL) + return perror(execname), free(buf), 2; } + buf[ptr] = '\0'; - /* Reset terminal settings. */ + *site = buf; + return 0; +} + + +/** + * Ask the user for the master passphrase + * + * @param passphrase Output parameter for the passphrase + * @return Zero on success, an appropriate exit value on error + */ +static int get_master_passphrase(char** passphrase) +{ + passphrase_disable_echo(); + fprintf(stderr, "%s", PASSPHRASE_PROMPT_STRING); + fflush(stderr); + *passphrase = passphrase_read(); + if (*passphrase == NULL) + perror(execname); passphrase_reenable_echo(); + return *passphrase ? 0 : 2; +} + + +/** + * Hash the master password hash and site into a password + * + * @param spec Hashing parameters + * @param squeezes The number of squeezes to perform + * @param passphrase The master passphrase + * @param site The site + * @param hash Output paramter for the raw password + * @return Zero on success, an appropriate exit value on error + */ +static int calculate_raw_password(const libkeccak_spec_t* spec, long squeezes, + const char* passphrase, const char* site, char** hash) +{ + libkeccak_state_t state; + char* hashsum = NULL; - /* Measure passphrase length. */ - passphrase_n = strlen(passphrase); - - /* Translate password to sha3.h friendly format. */ - passphrase_hash = malloc((passphrase_n + 1) * sizeof(byte)); - if (passphrase_hash == NULL) - { - perror(*argv); - memset(passphrase, 0, passphrase_n * sizeof(char)); - free(passphrase); - return 1; - } - else - { - for (ptr = 0; ptr < passphrase_n + 1; ptr++) - *(passphrase_hash + ptr) = (byte)*(passphrase + ptr); - /* Wipe source password, however it is not yet secure to free it. (Should be done as sone as possible.) */ - memset(passphrase, 0, passphrase_n * sizeof(char)); - } - - /* Hash and display master passphrase so hint the user whether it as typed correctly or not. */ - master_passphrase_hash = malloc((MASTER_PASSPHRASE_KECCAK_OUTPUT * 2 + 1) * sizeof(char)); - if (master_passphrase_hash == NULL) - { - perror(*argv); - memset(passphrase_hash, 0, passphrase_n * sizeof(byte)); - free(passphrase_hash); - free(passphrase); - return 1; - } - digest = sha3_digest(passphrase_hash, (long)passphrase_n, MASTER_PASSPHRASE_KECCAK_SQUEEZES == 1); - if (MASTER_PASSPHRASE_KECCAK_SQUEEZES > 2) - sha3_fastSqueeze(MASTER_PASSPHRASE_KECCAK_SQUEEZES - 2); - if (MASTER_PASSPHRASE_KECCAK_SQUEEZES > 1) - digest = sha3_squeeze(); - for (ptr = 0; ptr < (MASTER_PASSPHRASE_KECCAK_OUTPUT + 7) / 8; ptr++) - { - uint8_t v = (uint8_t)*(digest + ptr); - *(master_passphrase_hash + ptr * 2 + 0) = HEXADECA[(v >> 4) & 15]; - *(master_passphrase_hash + ptr * 2 + 1) = HEXADECA[(v >> 0) & 15]; - } - *(master_passphrase_hash + ptr * 2) = 0; - fprintf(stderr, "%s: master passphrase hash: %s\n", *argv, master_passphrase_hash); + if (libkeccak_state_initialise(&state, spec)) + return perror(execname), 2; - /* Reinitialise Keccak sponge. */ - sha3_dispose(); - sha3_initialise(keccak_bitrate, keccak_capacity, keccak_output); + if (hashsum = malloc((size_t)(spec->output / 8) * sizeof(char)), hashsum == NULL) + goto fail; - /* Add passphrase to Keccak sponge input. */ - sha3_update(passphrase_hash, (long)passphrase_n); + if (libkeccak_update(&state, passphrase, strlen(passphrase))) + goto fail; + if (libkeccak_digest(&state, site, strlen(site), 0, NULL, squeezes == 1 ? hashsum : NULL)) + goto fail; + if (squeezes > 2) libkeccak_fast_squeeze(&state, squeezes - 2); + if (squeezes > 1) libkeccak_squeeze(&state, hashsum); - /* Clear passphrase from memory. (Should be done as sone as possible.) */ - memset(passphrase, 0, passphrase_n * sizeof(char)); - free(passphrase_hash); - free(passphrase); + libkeccak_state_destroy(&state); + *hash = hashsum; + return 0; + fail: + perror(execname); + free(hashsum); + libkeccak_state_destroy(&state); + return 2; +} + + +/** + * base64-encode the password + * + * @param raw The password + * @param length The length of the pasword + * @param base64 Output parameter for the base64-encoded password + * @return Zero on success, an appropriate exit value on error + */ +static int encode_base64(const char* raw, size_t length, char** base64) +{ + size_t ptr, ptr64, out_length = ((length + 2) / 3) * 4 + 2; + char* buf = malloc(out_length * sizeof(char)); - /* Add site to Keccak sponge input. */ - free(digest); /* (Should be done after wiping passphrase.) */ - free(master_passphrase_hash); /* (Should be done after wiping passphrase.) */ - digest = sha3_digest(site, (long)site_n, keccak_squeezes == 1); + if (*base64 = buf, buf == NULL) + return perror(execname), 2; - /* Release resources. */ - free(site); + for (ptr = ptr64 = 0; ptr < length; ptr64 += 4) + { + uint8_t a = (uint8_t)(ptr < length ? raw[ptr++] : 0); + uint8_t b = (uint8_t)(ptr < length ? raw[ptr++] : 0); + uint8_t c = (uint8_t)(ptr < length ? raw[ptr++] : 0); + + uint32_t abc = ((uint32_t)a << 16) | ((uint32_t)b << 8) | ((uint32_t)c << 0); + + buf[ptr64 | 0] = BASE64[(abc >> (3 * 6)) & 63]; + buf[ptr64 | 1] = BASE64[(abc >> (2 * 6)) & 63]; + buf[ptr64 | 2] = BASE64[(abc >> (1 * 6)) & 63]; + buf[ptr64 | 3] = BASE64[(abc >> (0 * 6)) & 63]; + } + if (length % 3 == 1) buf[ptr64++] = BASE64[64]; + if (length % 3 == 2) buf[ptr64++] = BASE64[65]; + buf[ptr64++] = '\0'; - /* Squeeze that sponge. */ - if (keccak_squeezes > 2) - sha3_fastSqueeze(keccak_squeezes - 2); - if (keccak_squeezes > 1) - digest = sha3_squeeze(); + return 0; +} + + +/** + * Here we go! + * + * @param argc The number of command line argumnets + * @param argv Command line argumnets + * @return Zero on success, 1 on user error, 2 on system error + */ +int main(int argc, char* argv[]) +{ + int r, verbose = 0, clear_mode = 0; + libkeccak_generalised_spec_t gspec; + libkeccak_spec_t spec; + long squeezes = DEFAULT_SQUEEZES, bump_level = 0; + char* site = NULL; + char* passphrase = NULL; + char* raw_password = NULL; + char* base64 = NULL; - /* Release resources. */ - sha3_dispose(); + libkeccak_generalised_spec_initialise(&gspec); + execname = *argv; - /* Encode with base64 (no invalid character, shorter than hexadecimal.) */ - base64 = malloc((4 * (((((size_t)keccak_output + 7) / 8) + 2) / 3) + 2) * sizeof(char)); - if (base64 == NULL) + t (parse_cmdline(argc, argv, &gspec, &squeezes, &bump_level, &clear_mode, &verbose)); + t (make_spec(&gspec, &spec)); + if (squeezes <= 0) { - perror(*argv); - free(digest); - free(base64); + r = USER_ERROR("the squeeze count most be positive"); + goto fail; } - for (ptr = ptr64 = 0; ptr < (size_t)((keccak_output + 7) / 8); ptr64 += 4) + + squeezes += bump_level * BUMP_LEVEL_MULTIPLIER; + + if (verbose) { - uint8_t a = (uint8_t)(ptr < (size_t)((keccak_output + 7) / 8) ? digest[ptr++] : 0); - uint8_t b = (uint8_t)(ptr < (size_t)((keccak_output + 7) / 8) ? digest[ptr++] : 0); - uint8_t c = (uint8_t)(ptr < (size_t)((keccak_output + 7) / 8) ? digest[ptr++] : 0); - - uint32_t abc = ((uint32_t)a << 16) | ((uint32_t)b << 8) | ((uint32_t)c << 0); - - base64[ptr64 | 0] = BASE64[(abc >> (3 * 6)) & 63]; - base64[ptr64 | 1] = BASE64[(abc >> (2 * 6)) & 63]; - base64[ptr64 | 2] = BASE64[(abc >> (1 * 6)) & 63]; - base64[ptr64 | 3] = BASE64[(abc >> (0 * 6)) & 63]; + fprintf(stderr, "bump level: %li\n", bump_level); + fprintf(stderr, "rate: %li\n", gspec.bitrate); + fprintf(stderr, "capacity: %li\n", gspec.capacity); + fprintf(stderr, "output size: %li\n", gspec.output); + fprintf(stderr, "state size: %li\n", gspec.state_size); + fprintf(stderr, "word size: %li\n", gspec.word_size); + fprintf(stderr, "squeezes after bump level: %li\n", squeezes); + fprintf(stderr, "squeezes before bump level: %li\n", + squeezes - bump_level * BUMP_LEVEL_MULTIPLIER); } - if ((((keccak_output + 7) / 8) % 3) == 1) base64[ptr64++] = BASE64[64]; - if ((((keccak_output + 7) / 8) % 3) == 2) base64[ptr64++] = BASE64[65]; - base64[ptr64++] = 0; - /* Display verbose information. */ - if (verbose_mode) + t (get_site(&site)); + t (get_master_passphrase(&passphrase)); + t (hash_master_passphrase(passphrase)); + + t (calculate_raw_password(&spec, squeezes, passphrase, site, &raw_password)); + passphrase_wipe(passphrase, strlen(passphrase)); + xfree(passphrase); + xfree(site); + + t (encode_base64(raw_password, (size_t)(spec.output / 8), &base64)); + xfree(raw_password); + + if (verbose) { - fprintf(stderr, "Password length (before base64): %li\n", (keccak_output + 7) / 8); - fprintf(stderr, "Password length (after base64): %li\n", strlen(base64)); + fprintf(stderr, "password length before base64: %li\n", spec.output / 8); + fprintf(stderr, "password length after base64: %li\n", strlen(base64)); } - /* Print generated password. */ - if (clear_mode > 1) - printf("%s", base64); - else if (clear_mode) - printf("%s\n", base64); - else - printf("\033[00m>\033[00;30;40m%s\033[00m<\n", base64); + if (clear_mode > 1) printf("%s", base64); + else if (clear_mode) printf("%s\n", base64); + else printf("\033[00m>\033[08;30;40m%s\033[00m<\n", base64); - /* Release resources. */ - free(digest); free(base64); - return 0; + + fail: + if (passphrase) + passphrase_wipe(passphrase, strlen(passphrase)); + free(passphrase); + free(site); + free(raw_password); + free(base64); + return r < 0 ? 0 : r; } |
