This commit adds grub-mkpasswd-argon2, a new user-space utility used to generate Argon2 password hashes compatible with the password_argon2 command.
The utility generates a random salt and computes the hash based on the user's input password. It allows customizing the Argon2 parameters via command-line arguments. The default parameters are configured as follows: - Iterations (-i): 5 - Memory (-m): 131072 KiB - Parallelism (-p): 1 - Salt length (-s): 16 bytes - Hash length (-l): 32 bytes Signed-off-by: Gary Lin <[email protected]> --- Makefile.util.def | 18 +++ util/grub-mkpasswd-argon2.c | 231 ++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 util/grub-mkpasswd-argon2.c diff --git a/Makefile.util.def b/Makefile.util.def index 313dd0a68..68c832f71 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -293,6 +293,24 @@ program = { ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; +program = { + name = grub-mkpasswd-argon2; + mansection = 1; + + common = util/grub-mkpasswd-argon2.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/random.c; + common = grub-core/osdep/init.c; + + /* Link libgrubkern.a twice to resolve a circular dependency with libgrubgcry.a */ + ldadd = libgrubmods.a; + ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + program = { name = grub-mkpasswd-pbkdf2; mansection = 1; diff --git a/util/grub-mkpasswd-argon2.c b/util/grub-mkpasswd-argon2.c new file mode 100644 index 000000000..c3c401d77 --- /dev/null +++ b/util/grub-mkpasswd-argon2.c @@ -0,0 +1,231 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <grub/types.h> +#include <grub/crypto.h> +#include <grub/auth.h> +#include <grub/emu/misc.h> +#include <grub/util/misc.h> +#include <grub/i18n.h> +#include <grub/misc.h> + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define _GNU_SOURCE 1 + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#include <argp.h> +#pragma GCC diagnostic error "-Wmissing-prototypes" +#pragma GCC diagnostic error "-Wmissing-declarations" + + +#include "progname.h" + +static struct argp_option options[] = { + {"iterations", 'i', N_("NUM"), 0, N_("Number of iterations to perform"), 0}, + {"memory", 'm', N_("NUM"), 0, N_("Amount of memory (in kibibytes) to use"), 0}, + {"parallelism", 'p', N_("NUM"), 0, N_("Degree of parallelism (i.e. number of threads)"), 0}, + {"buflen", 'l', N_("NUM"), 0, N_("Length of generated hash"), 0}, + {"salt", 's', N_("NUM"), 0, N_("Length of salt"), 0}, + { 0, 0, 0, 0, 0, 0 } +}; + +struct arguments +{ + grub_uint64_t iterations; + grub_uint64_t memory; + grub_uint64_t parallelism; + unsigned int buflen; + unsigned int saltlen; +}; + +static error_t +argp_parser (int key, char *arg, struct argp_state *state) +{ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; + + switch (key) + { + case 'i': + arguments->iterations = strtoul (arg, NULL, 0); + break; + + case 'm': + arguments->memory = strtoul (arg, NULL, 0); + break; + + case 'p': + arguments->parallelism = strtoul (arg, NULL, 0); + break; + + case 'l': + arguments->buflen = strtoul (arg, NULL, 0); + break; + + case 's': + arguments->saltlen = strtoul (arg, NULL, 0); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + options, argp_parser, N_("[OPTIONS]"), + N_("Generate ARGON2 password hash."), + NULL, NULL, NULL +}; + + +static void +hexify (char *hex, grub_uint8_t *bin, grub_size_t n) +{ + while (n--) + { + if (((*bin & 0xf0) >> 4) < 10) + *hex = ((*bin & 0xf0) >> 4) + '0'; + else + *hex = ((*bin & 0xf0) >> 4) + 'A' - 10; + hex++; + + if ((*bin & 0xf) < 10) + *hex = (*bin & 0xf) + '0'; + else + *hex = (*bin & 0xf) + 'A' - 10; + hex++; + bin++; + } + *hex = 0; +} + +int +main (int argc, char *argv[]) +{ + struct arguments arguments = { + .iterations = 5, + .memory = 131072, + .parallelism = 1, + .buflen = 32, + .saltlen = 16 + }; + grub_uint64_t param[4]; + char *result, *ptr; + gcry_err_code_t gcry_err; + grub_uint8_t *buf, *salt; + char pass1[GRUB_AUTH_MAX_PASSLEN]; + char pass2[GRUB_AUTH_MAX_PASSLEN]; + + grub_util_host_init (&argc, &argv); + + /* Check for options. */ + if (argp_parse (&argp, argc, argv, 0, 0, &arguments) != 0) + { + fprintf (stderr, "%s", _("Error in parsing command line arguments\n")); + exit(1); + } + + printf ("%s", _("Enter password: ")); + if (!grub_password_get (pass1, GRUB_AUTH_MAX_PASSLEN)) + grub_util_error ("%s", _("failure to read password")); + printf ("%s", _("Reenter password: ")); + if (!grub_password_get (pass2, GRUB_AUTH_MAX_PASSLEN)) + grub_util_error ("%s", _("failure to read password")); + + if (strcmp (pass1, pass2) != 0) + { + memset (pass1, 0, sizeof (pass1)); + memset (pass2, 0, sizeof (pass2)); + grub_util_error ("%s", _("passwords don't match")); + } + memset (pass2, 0, sizeof (pass2)); + + buf = xmalloc (arguments.buflen); + salt = xmalloc (arguments.saltlen); + + if (grub_get_random (salt, arguments.saltlen)) + { + memset (pass1, 0, sizeof (pass1)); + free (buf); + free (salt); + grub_util_error ("%s", _("couldn't retrieve random data for salt")); + } + + param[0] = arguments.buflen; + param[1] = arguments.iterations; + param[2] = arguments.memory; + param[3] = arguments.parallelism; + gcry_err = grub_crypto_argon2 (GRUB_GCRY_KDF_ARGON2ID, param, 4, + pass1, grub_strlen (pass1), + salt, arguments.saltlen, + NULL, 0, NULL, 0, + arguments.buflen, buf); + memset (pass1, 0, sizeof (pass1)); + + if (gcry_err) + { + memset (buf, 0, arguments.buflen); + free (buf); + memset (salt, 0, arguments.saltlen); + free (salt); + grub_util_error (_("cryptographic error number %d"), gcry_err); + } + + /* + * Output format: grub.argon2.<iterations>.<memory>.<parallelism>.<salt>.<hash> + * + * Reserve 20 digits for each argument: iterations, memory, and parallelism + */ + result = xmalloc (sizeof ("grub.argon2.X.X.X.S.S") + (20 * 3) + + arguments.buflen * 2 + arguments.saltlen * 2); + ptr = result; + memcpy (ptr, "grub.argon2.", sizeof ("grub.argon2.") - 1); + ptr += sizeof ("grub.argon2.") - 1; + + grub_snprintf (ptr, 20, "%ld", arguments.iterations); + ptr += strlen (ptr); + *ptr++ = '.'; + grub_snprintf (ptr, 20, "%ld", arguments.memory); + ptr += strlen (ptr); + *ptr++ = '.'; + grub_snprintf (ptr, 20, "%ld", arguments.parallelism); + ptr += strlen (ptr); + *ptr++ = '.'; + hexify (ptr, salt, arguments.saltlen); + ptr += arguments.saltlen * 2; + *ptr++ = '.'; + hexify (ptr, buf, arguments.buflen); + ptr += arguments.buflen * 2; + *ptr = '\0'; + + printf (_("ARGON2 hash of your password is %s\n"), result); + memset (buf, 0, arguments.buflen); + free (buf); + memset (salt, 0, arguments.saltlen); + free (salt); + + return 0; +} -- 2.51.0 _______________________________________________ Grub-devel mailing list [email protected] https://lists.gnu.org/mailman/listinfo/grub-devel
