From d8e843f707ff6c722de869861518727409cab44a Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Mon, 23 Feb 2026 07:01:05 +0100 Subject: cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 12 +- Makefile | 19 +- autopasswd.c | 531 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ config.mk | 10 +- src/autopasswd.c | 531 ------------------------------------------------------- 5 files changed, 548 insertions(+), 555 deletions(-) create mode 100644 autopasswd.c delete mode 100644 src/autopasswd.c diff --git a/.gitignore b/.gitignore index 24a1673..6a775e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,5 @@ -_/ -bin/ -obj/ -\#*\# -.* -!.git* +*\#* *~ -*.sw[op] -*.bak *.o -*.out -*.gch *.su +/autopasswd diff --git a/Makefile b/Makefile index 55ddd0a..a708475 100644 --- a/Makefile +++ b/Makefile @@ -6,20 +6,18 @@ CONFIGFILE = config.mk include $(CONFIGFILE) -all: bin/autopasswd +all: autopasswd -bin/autopasswd: obj/autopasswd.o - @mkdir -p bin - $(CC) -o $@ obj/autopasswd.o $(LDFLAGS) +.o: + $(CC) -o $@ $< $(LDFLAGS) -obj/%.o: src/%.c - @mkdir -p obj +.c.o: $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) -install: bin/autopasswd +install: autopasswd mkdir -p -- "$(DESTDIR)$(BINDIR)" mkdir -p -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)" - cp -- bin/autopasswd "$(DESTDIR)$(BINDIR)/$(COMMAND)" + cp -- autopasswd "$(DESTDIR)$(BINDIR)/$(COMMAND)" cp -- COPYING LICENSE "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)/" uninstall: @@ -29,6 +27,9 @@ uninstall: -rmdir -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)" clean: - -rm -rf -- bin obj + -rm -f -- *.o *.su autopasswd + +.SUFFIXES: +.SUFFIXES: .o .c .PHONY: all install uninstall clean diff --git a/autopasswd.c b/autopasswd.c new file mode 100644 index 0000000..bf8ac76 --- /dev/null +++ b/autopasswd.c @@ -0,0 +1,531 @@ +/** + * autopasswd – Reproducible password generator + * + * Copyright © 2014 Mattias Andrée (m@maandree.se) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include + +#include +#include +#include + + + +/** + * Prompt string that tells you to enter your master passphrase + */ +#ifndef PASSPHRASE_PROMPT_STRING +# define PASSPHRASE_PROMPT_STRING "[autopasswd] Enter master passphrase: " +# warning: you should personalise PASSPHRASE_PROMPT_STRING. +#endif + +/** + * Prompt string that tells you to enter site + */ +#ifndef SITE_PROMPT_STRING +# define SITE_PROMPT_STRING "[autopasswd] Enter site: " +#endif + +/** + * The radix 64 characters (66 characters), the two last ones are for padding + */ +#define BASE64 "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,.-=" + +/** + * The number of squeezes to do at bump level zero + */ +#define DEFAULT_SQUEEZES 300000 + +/** + * The number of additional squeezes to perform per bump level + */ +#define BUMP_LEVEL_MULTIPLIER 5000 + + +/** + * The rate parameter for the Keccak sponge when hashing master passphrase + */ +#ifndef MASTER_PASSPHRASE_KECCAK_RATE +# define MASTER_PASSPHRASE_KECCAK_RATE 576 +#endif + +/** + * The capacity parameter for the Keccak sponge when hashing master passphrase + */ +#ifndef MASTER_PASSPHRASE_KECCAK_CAPACITY +# define MASTER_PASSPHRASE_KECCAK_CAPACITY 1024 +#endif + +/** + * The output parameter for the Keccak sponge when hashing master passphrase + */ +#ifndef MASTER_PASSPHRASE_KECCAK_OUTPUT +# define MASTER_PASSPHRASE_KECCAK_OUTPUT 32 +#endif + +/** + * The number of times to squeeze the master passphrase + */ +#ifndef MASTER_PASSPHRASE_KECCAK_SQUEEZES +# 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)\ + do { if ((r = (expr))) goto fail; } while (0) + +#define xfree(s)\ + (free(s), s = NULL) + + + +/** + * `argv[0]` from `main` + */ +static char *execname; + + + +/** + * 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) +{ + int r; + +#define TEST(CASE, STR) case LIBKECCAK_GENERALISED_SPEC_ERROR_##CASE: return USER_ERROR(STR) + if ((r = libkeccak_degeneralise_spec(gspec, spec))) { + 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))) { + 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; +} + + +/** + * Parse the command line arguments + * + * @param argc The number of command line arguments + * @param argv Command line arguments + * @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 + */ +static int +parse_cmdline(int argc, char *argv[], libkeccak_generalised_spec_t *gspec, + long *squeezes, long *bump_level, int *clear_mode, int *verbose) +{ + args_init("Reproducible password generator", "autopasswd [options...]", + NULL, NULL, 1, 0, args_standard_abbreviations); + + 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); + + if (args_opts_used("-h")) + return args_help(0), args_dispose(), -1; + if (args_opts_used("+c")) { + printf("autopasswd – Reproducible password generator\n"); + printf("\n"); + printf("Copyright © 2014 Mattias Andrée (m@maandree.se)\n"); + printf("\n"); + printf("This program is free software: you can redistribute it and/or modify\n"); + printf("it under the terms of the GNU Affero General Public License as published by\n"); + printf("the Free Software Foundation, either version 3 of the License, or\n"); + printf("(at your option) any later version.\n"); + printf("\n"); + 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"); + printf("\n"); + printf("You should have received a copy of the GNU Affero General Public License\n"); + printf("along with this program. If not, see .\n"); + args_dispose(); + return -1; + } + 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 -1; + } + + 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 i, n = (size_t)args_opts_get_count("-b"); + char **arr = args_opts_get("-b"); + char *arg; + 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; + } + } + } + } + + 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]; + + spec.bitrate = MASTER_PASSPHRASE_KECCAK_RATE; + spec.capacity = MASTER_PASSPHRASE_KECCAK_CAPACITY; + spec.output = MASTER_PASSPHRASE_KECCAK_OUTPUT; + + if (libkeccak_spec_check(&spec) || (SQUEEZES <= 0)) { + return USER_ERROR("bad master passphrase hashing parameters, " + "please recompile autopasswd 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) + return perror(execname), 2; + + fprintf(stderr, "%s", SITE_PROMPT_STRING); + fflush(stderr); + + for (;;) { + int c = getchar(); + if (ptr == size) { + char *new = realloc(buf, (size <<= 1) * sizeof(char)); + if (!new) + return perror(execname), free(buf), 2; + } + if ((c < 0) || (c == '\n')) + break; + buf[ptr++] = (char)c; + } + + if (ptr == size) { + char *new = realloc(buf, (size += 1) * sizeof(char)); + if (!new) + return perror(execname), free(buf), 2; + } + buf[ptr] = '\0'; + + *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) + 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; + + if (libkeccak_state_initialise(&state, spec)) + return perror(execname), 2; + + if (hashsum = malloc((size_t)(spec->output / 8) * sizeof(char)), !hashsum) + goto fail; + + 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); + + 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)); + + if (*base64 = buf, !buf) + return perror(execname), 2; + + 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'; + + return 0; +} + + +/** + * Here we go! + * + * @param argc The number of command line arguments + * @param argv Command line arguments + * @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; + + libkeccak_generalised_spec_initialise(&gspec); + execname = *argv; + + t (parse_cmdline(argc, argv, &gspec, &squeezes, &bump_level, &clear_mode, &verbose)); + t (make_spec(&gspec, &spec)); + if (squeezes <= 0) { + r = USER_ERROR("the squeeze count most be positive"); + goto fail; + } + + squeezes += bump_level * BUMP_LEVEL_MULTIPLIER; + + if (verbose) { + 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); + } + + 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", spec.output / 8); + fprintf(stderr, "password length after base64: %li\n", strlen(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); + + 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; +} diff --git a/config.mk b/config.mk index 856eeca..b83189c 100644 --- a/config.mk +++ b/config.mk @@ -1,8 +1,8 @@ -PREFIX = /usr -BIN = /bin -BINDIR = $(PREFIX)$(BIN) -DATA = /share -DATADIR = $(PREFIX)$(DATA) +PREFIX = /usr +BIN = /bin +BINDIR = $(PREFIX)$(BIN) +DATA = /share +DATADIR = $(PREFIX)$(DATA) LICENSEDIR = $(DATADIR)/licenses PKGNAME = autopasswd diff --git a/src/autopasswd.c b/src/autopasswd.c deleted file mode 100644 index bf8ac76..0000000 --- a/src/autopasswd.c +++ /dev/null @@ -1,531 +0,0 @@ -/** - * autopasswd – Reproducible password generator - * - * Copyright © 2014 Mattias Andrée (m@maandree.se) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include -#include -#include -#include - -#include -#include -#include - - - -/** - * Prompt string that tells you to enter your master passphrase - */ -#ifndef PASSPHRASE_PROMPT_STRING -# define PASSPHRASE_PROMPT_STRING "[autopasswd] Enter master passphrase: " -# warning: you should personalise PASSPHRASE_PROMPT_STRING. -#endif - -/** - * Prompt string that tells you to enter site - */ -#ifndef SITE_PROMPT_STRING -# define SITE_PROMPT_STRING "[autopasswd] Enter site: " -#endif - -/** - * The radix 64 characters (66 characters), the two last ones are for padding - */ -#define BASE64 "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,.-=" - -/** - * The number of squeezes to do at bump level zero - */ -#define DEFAULT_SQUEEZES 300000 - -/** - * The number of additional squeezes to perform per bump level - */ -#define BUMP_LEVEL_MULTIPLIER 5000 - - -/** - * The rate parameter for the Keccak sponge when hashing master passphrase - */ -#ifndef MASTER_PASSPHRASE_KECCAK_RATE -# define MASTER_PASSPHRASE_KECCAK_RATE 576 -#endif - -/** - * The capacity parameter for the Keccak sponge when hashing master passphrase - */ -#ifndef MASTER_PASSPHRASE_KECCAK_CAPACITY -# define MASTER_PASSPHRASE_KECCAK_CAPACITY 1024 -#endif - -/** - * The output parameter for the Keccak sponge when hashing master passphrase - */ -#ifndef MASTER_PASSPHRASE_KECCAK_OUTPUT -# define MASTER_PASSPHRASE_KECCAK_OUTPUT 32 -#endif - -/** - * The number of times to squeeze the master passphrase - */ -#ifndef MASTER_PASSPHRASE_KECCAK_SQUEEZES -# 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)\ - do { if ((r = (expr))) goto fail; } while (0) - -#define xfree(s)\ - (free(s), s = NULL) - - - -/** - * `argv[0]` from `main` - */ -static char *execname; - - - -/** - * 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) -{ - int r; - -#define TEST(CASE, STR) case LIBKECCAK_GENERALISED_SPEC_ERROR_##CASE: return USER_ERROR(STR) - if ((r = libkeccak_degeneralise_spec(gspec, spec))) { - 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))) { - 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; -} - - -/** - * Parse the command line arguments - * - * @param argc The number of command line arguments - * @param argv Command line arguments - * @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 - */ -static int -parse_cmdline(int argc, char *argv[], libkeccak_generalised_spec_t *gspec, - long *squeezes, long *bump_level, int *clear_mode, int *verbose) -{ - args_init("Reproducible password generator", "autopasswd [options...]", - NULL, NULL, 1, 0, args_standard_abbreviations); - - 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); - - if (args_opts_used("-h")) - return args_help(0), args_dispose(), -1; - if (args_opts_used("+c")) { - printf("autopasswd – Reproducible password generator\n"); - printf("\n"); - printf("Copyright © 2014 Mattias Andrée (m@maandree.se)\n"); - printf("\n"); - printf("This program is free software: you can redistribute it and/or modify\n"); - printf("it under the terms of the GNU Affero General Public License as published by\n"); - printf("the Free Software Foundation, either version 3 of the License, or\n"); - printf("(at your option) any later version.\n"); - printf("\n"); - 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"); - printf("\n"); - printf("You should have received a copy of the GNU Affero General Public License\n"); - printf("along with this program. If not, see .\n"); - args_dispose(); - return -1; - } - 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 -1; - } - - 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 i, n = (size_t)args_opts_get_count("-b"); - char **arr = args_opts_get("-b"); - char *arg; - 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; - } - } - } - } - - 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]; - - spec.bitrate = MASTER_PASSPHRASE_KECCAK_RATE; - spec.capacity = MASTER_PASSPHRASE_KECCAK_CAPACITY; - spec.output = MASTER_PASSPHRASE_KECCAK_OUTPUT; - - if (libkeccak_spec_check(&spec) || (SQUEEZES <= 0)) { - return USER_ERROR("bad master passphrase hashing parameters, " - "please recompile autopasswd 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) - return perror(execname), 2; - - fprintf(stderr, "%s", SITE_PROMPT_STRING); - fflush(stderr); - - for (;;) { - int c = getchar(); - if (ptr == size) { - char *new = realloc(buf, (size <<= 1) * sizeof(char)); - if (!new) - return perror(execname), free(buf), 2; - } - if ((c < 0) || (c == '\n')) - break; - buf[ptr++] = (char)c; - } - - if (ptr == size) { - char *new = realloc(buf, (size += 1) * sizeof(char)); - if (!new) - return perror(execname), free(buf), 2; - } - buf[ptr] = '\0'; - - *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) - 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; - - if (libkeccak_state_initialise(&state, spec)) - return perror(execname), 2; - - if (hashsum = malloc((size_t)(spec->output / 8) * sizeof(char)), !hashsum) - goto fail; - - 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); - - 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)); - - if (*base64 = buf, !buf) - return perror(execname), 2; - - 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'; - - return 0; -} - - -/** - * Here we go! - * - * @param argc The number of command line arguments - * @param argv Command line arguments - * @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; - - libkeccak_generalised_spec_initialise(&gspec); - execname = *argv; - - t (parse_cmdline(argc, argv, &gspec, &squeezes, &bump_level, &clear_mode, &verbose)); - t (make_spec(&gspec, &spec)); - if (squeezes <= 0) { - r = USER_ERROR("the squeeze count most be positive"); - goto fail; - } - - squeezes += bump_level * BUMP_LEVEL_MULTIPLIER; - - if (verbose) { - 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); - } - - 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", spec.output / 8); - fprintf(stderr, "password length after base64: %li\n", strlen(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); - - 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; -} -- cgit v1.2.3-70-g09d2