This commit introduces a new command, password_argon2, which allows verifying a user-provided password against a stored Argon2 hash.
Signed-off-by: Gary Lin <[email protected]> --- grub-core/Makefile.core.def | 5 + grub-core/commands/password_argon2.c | 247 +++++++++++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 grub-core/commands/password_argon2.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index fa4bc54aa..f8593b33d 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1087,6 +1087,11 @@ module = { common = commands/password.c; }; +module = { + name = password_argon2; + common = commands/password_argon2.c; +}; + module = { name = password_pbkdf2; common = commands/password_pbkdf2.c; diff --git a/grub-core/commands/password_argon2.c b/grub-core/commands/password_argon2.c new file mode 100644 index 000000000..a245a84b4 --- /dev/null +++ b/grub-core/commands/password_argon2.c @@ -0,0 +1,247 @@ +/* + * 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 <grub/auth.h> +#include <grub/crypto.h> +#include <grub/list.h> +#include <grub/mm.h> +#include <grub/misc.h> +#include <grub/env.h> +#include <grub/normal.h> +#include <grub/dl.h> +#include <grub/i18n.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +struct argon2_password +{ + grub_uint64_t iterations; + grub_uint64_t memory; + grub_uint64_t parallelism; + grub_uint8_t *salt; + grub_size_t saltlen; + grub_uint8_t *expected; + grub_size_t buflen; +}; + +static grub_err_t +check_password (const char *user, const char *entered, void *pin) +{ + grub_uint8_t *buf; + grub_uint64_t param[4]; + struct argon2_password *pass = pin; + gcry_err_code_t err; + grub_err_t ret; + + param[0] = pass->buflen; + param[1] = pass->iterations; + param[2] = pass->memory; + param[3] = pass->parallelism; + + buf = grub_malloc (pass->buflen); + if (!buf) + return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY); + + err = grub_crypto_argon2 (GRUB_GCRY_KDF_ARGON2ID, param, 4, + entered, grub_strlen (entered), + pass->salt, pass->saltlen, + NULL, 0, NULL, 0, + pass->buflen, buf); + + if (err) + ret = grub_crypto_gcry_error (err); + else if (grub_crypto_memcmp (buf, pass->expected, pass->buflen) != 0) + ret = GRUB_ACCESS_DENIED; + else + { + grub_auth_authenticate (user); + ret = GRUB_ERR_NONE; + } + + grub_free (buf); + return ret; +} + +static inline int +hex2val (char hex) +{ + if ('0' <= hex && hex <= '9') + return hex - '0'; + if ('a' <= hex && hex <= 'f') + return hex - 'a' + 10; + if ('A' <= hex && hex <= 'F') + return hex - 'A' + 10; + return -1; +} + +static grub_err_t +grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_err_t err; + const char *ptr, *ptr2; + grub_uint8_t *ptro; + struct argon2_password *pass; + + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); + + /* Expected format: grub.argon2.<iterations>.<memory>.<parallelism>.<salt>.<hash> */ + + if (grub_memcmp (args[1], "grub.argon2.", + sizeof ("grub.argon2.") - 1) != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid Argon2 password")); + + ptr = args[1] + sizeof ("grub.argon2.") - 1; + + pass = grub_malloc (sizeof (*pass)); + if (!pass) + return grub_errno; + + pass->iterations = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + { + grub_free (pass); + return grub_errno; + } + if (*ptr != '.') + { + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid Argon2 password")); + } + ptr++; + + pass->memory = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + { + grub_free (pass); + return grub_errno; + } + if (*ptr != '.') + { + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid Argon2 password")); + } + ptr++; + + pass->parallelism = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + { + grub_free (pass); + return grub_errno; + } + if (*ptr != '.') + { + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid Argon2 password")); + } + ptr++; + + ptr2 = grub_strchr (ptr, '.'); + if (!ptr2 || ((ptr2 - ptr) & 1) || grub_strlen (ptr2 + 1) & 1) + { + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid Argon2 password")); + } + + pass->saltlen = (ptr2 - ptr) >> 1; + pass->buflen = grub_strlen (ptr2 + 1) >> 1; + ptro = pass->salt = grub_malloc (pass->saltlen); + if (!ptro) + { + grub_free (pass); + return grub_errno; + } + while (ptr < ptr2) + { + int hex1, hex2; + hex1 = hex2val (*ptr); + ptr++; + hex2 = hex2val (*ptr); + ptr++; + if (hex1 < 0 || hex2 < 0) + { + grub_free (pass->salt); + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, + /* TRANSLATORS: it means that the string which + was supposed to be a password hash doesn't + have a correct format, not to password + mismatch. */ + N_("invalid Argon2 password")); + } + + *ptro = (hex1 << 4) | hex2; + ptro++; + } + + ptro = pass->expected = grub_malloc (pass->buflen); + if (!ptro) + { + grub_free (pass->salt); + grub_free (pass); + return grub_errno; + } + ptr = ptr2 + 1; + ptr2 += grub_strlen (ptr2); + while (ptr < ptr2) + { + int hex1, hex2; + hex1 = hex2val (*ptr); + ptr++; + hex2 = hex2val (*ptr); + ptr++; + if (hex1 < 0 || hex2 < 0) + { + grub_free (pass->expected); + grub_free (pass->salt); + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid Argon2 password")); + } + + *ptro = (hex1 << 4) | hex2; + ptro++; + } + + err = grub_auth_register_authentication (args[0], check_password, pass); + if (err) + { + grub_free (pass); + return err; + } + grub_dl_ref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(password_argon2) +{ + my_mod = mod; + cmd = grub_register_command ("password_argon2", grub_cmd_password, + N_("USER ARGON2_PASSWORD"), + N_("Set user password (Argon2). ")); +} + +GRUB_MOD_FINI(password_argon2) +{ + grub_unregister_command (cmd); +} -- 2.51.0 _______________________________________________ Grub-devel mailing list [email protected] https://lists.gnu.org/mailman/listinfo/grub-devel
