Re: [PATCH v6 1/1] use crc32 instead of md5 for hibernation e820 integrity check

From: Eric Biggers
Date: Mon Apr 12 2021 - 13:45:36 EST


On Mon, Apr 12, 2021 at 10:09:32AM -0400, Chris von Recklinghausen wrote:
> Suspend fails on a system in fips mode because md5 is used for the e820
> integrity check and is not available. Use crc32 instead.
>
> This patch changes the integrity check algorithm from md5 to crc32.
>
> The purpose of the integrity check is to detect possible differences
> between the memory map used at the time when the hibernation image is
> about to be loaded into memory and the memory map used at the image
> creation time, because it is generally unsafe to load the image if the
> current memory map doesn't match the one used when it was created. so
> it is not intended as a cryptographic integrity check.

This still doesn't actually explain why a non-cryptographic checksum is
sufficient. "Detection of possible differences" could very well require
cryptographic authentication; it depends on whether malicious changes need to be
detected or not.

>
> Fixes: 62a03defeabd ("PM / hibernate: Verify the consistent of e820 memory map
> by md5 digest")
>
> Signed-off-by: Chris von Recklinghausen <crecklin@xxxxxxxxxx>
> ---
> v1 -> v2
> bump up RESTORE_MAGIC
> v2 -> v3
> move embelishment from cover letter to commit comments (no code change)
> v3 -> v4
> add note to comments that md5 isn't used for encryption here.
> v4 -> v5
> reword comment per Simo's suggestion
> v5 -> v6
> use wording from Eric Biggers, use crc32_le instead of crc32 from crypto
> framework (crc32_le is in the core API and removes need for #defines)
>
> arch/x86/power/hibernate.c | 76 +++++++++++---------------------------
> 1 file changed, 22 insertions(+), 54 deletions(-)
>
> diff --git a/arch/x86/power/hibernate.c b/arch/x86/power/hibernate.c
> index cd3914fc9f3d..f39e507e34ca 100644
> --- a/arch/x86/power/hibernate.c
> +++ b/arch/x86/power/hibernate.c
> @@ -13,6 +13,8 @@
> #include <linux/kdebug.h>
> #include <linux/cpu.h>
> #include <linux/pgtable.h>
> +#include <linux/types.h>
> +#include <linux/crc32.h>
>
> #include <crypto/hash.h>

crypto/hash.h is no longer needed.

>
> @@ -55,94 +57,60 @@ int pfn_is_nosave(unsigned long pfn)
> }
>
>
> -#define MD5_DIGEST_SIZE 16
> +#define CRC32_DIGEST_SIZE (sizeof (u32))
>
> struct restore_data_record {
> unsigned long jump_address;
> unsigned long jump_address_phys;
> unsigned long cr3;
> unsigned long magic;
> - u8 e820_digest[MD5_DIGEST_SIZE];
> + u8 e820_digest[CRC32_DIGEST_SIZE];
> };

It would be simpler to just make this field 'unsigned long'. Then there would
be no need to deal with memcpy().

> -#if IS_BUILTIN(CONFIG_CRYPTO_MD5)
> /**
> - * get_e820_md5 - calculate md5 according to given e820 table
> + * get_e820_crc32 - calculate crc32 according to given e820 table

Calling this "compute_e820_crc32()" would avoid confusion with retrieving the
previously-stored crc32 value.

> + ret = crc32_le(0, (unsigned char const *)table, size);

It would be better to do ~crc32_le(~0, ...) (i.e., invert at beginning and end)
to match the recommended usage of CRC-32. Unfortunately the library function
doesn't do the inversions by default.

> static int hibernation_e820_save(void *buf)
> {
> - return get_e820_md5(e820_table_firmware, buf);
> + return get_e820_crc32(e820_table_firmware, buf);
> }

This should be inlined into arch_hibernation_header_save(). Also note that it
can no longer fail.

>
> static bool hibernation_e820_mismatch(void *buf)
> {

This should be inlined into arch_hibernation_header_restore().

> int ret;
> - u8 result[MD5_DIGEST_SIZE];
> + u8 result[CRC32_DIGEST_SIZE];
>
> - memset(result, 0, MD5_DIGEST_SIZE);
> + memset(result, 0, CRC32_DIGEST_SIZE);
> /* If there is no digest in suspend kernel, let it go. */
> - if (!memcmp(result, buf, MD5_DIGEST_SIZE))
> + if (!memcmp(result, buf, CRC32_DIGEST_SIZE))
> return false;

There's no need to handle the "no digest" case anymore, right? Since crc32_le()
is always available.

- Eric