grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2] luks2: Fix decoding of digests and salts with escaped chars


From: Patrick Steinhardt
Subject: [PATCH v2] luks2: Fix decoding of digests and salts with escaped chars
Date: Mon, 4 Oct 2021 13:21:49 +0200

It was reported in the #grub IRC channel on Libera that decryption of
LUKS2 partitions fails with errors about invalid digests and/or salts.
In all of these cases, what failed was decoding the Base64
representation of these, where the encoded data contained invalid
characters.

As it turns out, the root cause is that json-c, which is used by
cryptsetup to read and write the JSON header, will escape some
characters by prepending a backslash when writing JSON strings. Most
importantly, this also includes the forward slash, which is part of the
Base64 alphabet and which may optionally be escaped. Because GRUB
doesn't know to unescape such characters, decoding this string will
rightfully fail.

Fix the issue by stripping the escape character for forward slashes.
There is no need to do so for any other escaped character given that
valid Base64 does not contain anything else.

Reported-by: Afdal
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---

Changes compared to v1:

    - `luks2_base64_decode` now takes `decoded` as `grub_uint8_t *`
      instead of as `char *`

    - Adapted the comment explaining why we only unescape forward
      slashes, based on Glenn's feedback.

    - Fixed error handling for the new function. `base64_decode` returns
      a bool, where `true` indicates success. In v1, error handling only
      worked by chance due to accidental double negation. We now
      properly handle errors returned by `base64_decode` and return
      `grub_err_t` errors.

Patrick

 grub-core/disk/luks2.c | 48 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c
index 371a53b83..2a70c66dc 100644
--- a/grub-core/disk/luks2.c
+++ b/grub-core/disk/luks2.c
@@ -383,6 +383,44 @@ luks2_scan (grub_disk_t disk, const char *check_uuid, int 
check_boot)
   return cryptodisk;
 }
 
+static grub_err_t
+luks2_base64_decode (const char *in, size_t inlen, grub_uint8_t *decoded, 
size_t *decodedlen)
+{
+  char *unescaped;
+  bool successful;
+  size_t i, j;
+
+  unescaped = grub_malloc (inlen);
+  if (!unescaped)
+    return GRUB_ERR_OUT_OF_MEMORY;
+
+  grub_memmove (unescaped, in, inlen);
+
+  /*
+   * Characters in JSON strings may be escaped, either via their six-character
+   * "\uXXXX" representation or (at least for a subset of characters) via a
+   * single backslash. In context of the Base64-encoded string we expect here,
+   * the only character that gets escaped by cryptsetup is the forward slash.
+   * So while incomplete, we only strip away the escape character if we see
+   * '\/' in the input.
+   *
+   * See https://datatracker.ietf.org/doc/html/rfc8259#section-7 for more
+   * information on escaping in JSON.
+   */
+  for (i = j = 0; i < inlen; i++) {
+    if (i + 1 < inlen && unescaped[i] == '\\' && unescaped[i + 1] == '/')
+      continue;
+    unescaped[j++] = unescaped[i];
+  }
+
+  successful = base64_decode (unescaped, j, (char *)decoded, decodedlen);
+  grub_free (unescaped);
+  if (!successful)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 luks2_verify_key (grub_luks2_digest_t *d, grub_uint8_t *candidate_key,
                  grub_size_t candidate_key_len)
@@ -394,9 +432,11 @@ luks2_verify_key (grub_luks2_digest_t *d, grub_uint8_t 
*candidate_key,
   gcry_err_code_t gcry_ret;
 
   /* Decode both digest and salt */
-  if (!base64_decode (d->digest, grub_strlen (d->digest), (char *)digest, 
&digestlen))
+  if (luks2_base64_decode (d->digest, grub_strlen (d->digest),
+                          digest, &digestlen) != GRUB_ERR_NONE)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid digest");
-  if (!base64_decode (d->salt, grub_strlen (d->salt), (char *)salt, &saltlen))
+  if (luks2_base64_decode (d->salt, grub_strlen (d->salt),
+                          salt, &saltlen) != GRUB_ERR_NONE)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid digest salt");
 
   /* Configure the hash used for the digest. */
@@ -434,8 +474,8 @@ luks2_decrypt_key (grub_uint8_t *out_key,
   gcry_err_code_t gcry_ret;
   grub_err_t ret;
 
-  if (!base64_decode (k->kdf.salt, grub_strlen (k->kdf.salt),
-                    (char *)salt, &saltlen))
+  if (luks2_base64_decode (k->kdf.salt, grub_strlen (k->kdf.salt),
+                          salt, &saltlen) != GRUB_ERR_NONE)
     {
       ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid keyslot salt");
       goto err;
-- 
2.33.0

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]