[PATCH v4 02/10] lib: uuid: add UUID v5 support

Heinrich Schuchardt xypron.glpk at gmx.de
Fri Jul 5 08:54:46 CEST 2024


On 7/2/24 15:30, Caleb Connolly wrote:
> Add support for generating version 5 UUIDs, these are determistic and work
> by hashing a "namespace" UUID together with some unique data. One intended
> usecase is to allow for dynamically generate payload UUIDs for UEFI
> capsule updates, so that supported boards can have their own UUIDs
> without needing to hardcode them.
>
> In addition, move the common bit twiddling code from gen_ran_uuid into a
> separate function and rewrite it not to use clrsetbits (which is not
> available when building as part of host tools).
>
> Tests for this are added in an upcoming patch.
>
> Signed-off-by: Caleb Connolly <caleb.connolly at linaro.org>
> ---
>   include/uuid.h | 17 +++++++++++++++--
>   lib/Kconfig    |  1 +
>   lib/uuid.c     | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
>   3 files changed, 67 insertions(+), 9 deletions(-)
>
> diff --git a/include/uuid.h b/include/uuid.h
> index f5a941250f48..1f4fa103b5e9 100644
> --- a/include/uuid.h
> +++ b/include/uuid.h
> @@ -10,8 +10,9 @@
>   #ifndef __UUID_H__
>   #define __UUID_H__
>
>   #include <linux/bitops.h>
> +#include <linux/kconfig.h>
>
>   /*
>    * UUID - Universally Unique IDentifier - 128 bits unique number.
>    *        There are 5 versions and one variant of UUID defined by RFC4122
> @@ -45,10 +46,10 @@
>    * where x is a hexadecimal character. Fields are separated by '-'s.
>    * When converting to a binary UUID, le means the field should be converted
>    * to little endian and be means it should be converted to big endian.
>    *
> - * UUID is also used as GUID (Globally Unique Identifier) with the same binary
> - * format but it differs in string format like below.
> + * UUID is also used as GUID (Globally Unique Identifier) with the same format
> + * but with some fields stored in little endian.
>    *
>    * GUID:
>    * 0        9    14   19   24
>    * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
> @@ -142,8 +143,20 @@ void gen_rand_uuid(unsigned char *uuid_bin);
>    * @param          - uuid output type: UUID - 0, GUID - 1
>    */
>   void gen_rand_uuid_str(char *uuid_str, int str_format);
>
> +struct efi_guid;
> +
> +/**
> + * gen_v5_guid() - generate little endian v5 GUID from namespace and other seed data.
> + *
> + * @namespace:   pointer to UUID namespace salt
> + * @guid:        pointer to allocated GUID output
> + * @...:         NULL terminated list of seed data as pairs of pointers
> + *               to data and their lengths
> + */
> +void gen_v5_guid(const struct uuid *namespace, struct efi_guid *guid, ...);
> +
>   /**
>    * uuid_str_to_le_bin() - Convert string UUID to little endian binary data.
>    * @uuid_str:	pointer to UUID string
>    * @uuid_bin:	pointer to allocated array for little endian output [16B]
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 189e6eb31aa1..9aa882d5f882 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -72,8 +72,9 @@ config HAVE_PRIVATE_LIBGCC
>   	bool
>
>   config LIB_UUID
>   	bool
> +	select SHA1
>
>   config RANDOM_UUID
>   	bool "GPT Random UUID generation"
>   	select LIB_UUID
> diff --git a/lib/uuid.c b/lib/uuid.c
> index dfa2320ba267..7d0a8273d157 100644
> --- a/lib/uuid.c
> +++ b/lib/uuid.c
> @@ -21,8 +21,9 @@
>   #include <part_efi.h>
>   #include <malloc.h>
>   #include <dm/uclass.h>
>   #include <rng.h>
> +#include <u-boot/sha1.h>
>
>   int uuid_str_valid(const char *uuid)
>   {
>   	int i, valid;
> @@ -368,8 +369,57 @@ void uuid_bin_to_str(const unsigned char *uuid_bin, char *uuid_str,
>   		}
>   	}
>   }
>
> +static void configure_uuid(struct uuid *uuid, unsigned char version)
> +{
> +	uint16_t tmp;
> +
> +	/* Configure variant/version bits */
> +	tmp = be16_to_cpu(uuid->time_hi_and_version);
> +	tmp = (tmp & ~UUID_VERSION_MASK) | (version << UUID_VERSION_SHIFT);

As you wrote above time_hi is stored low-endian in a GUID and big endian
in UUID. So the version is in the byte 6 for a UUID and in byte 7 in a GUID.

E.g. on my hard disk I find:

Partition unique GUID: 003EF8CE-F7E9-467F-B80D-22ED2E6E93D7
CE F8 3E 00  E9 F7 7F 46  B8 0D 22 ED  2E 6E 93 D7

Where 4 is the version.

> +	uuid->time_hi_and_version = cpu_to_be16(tmp);
> +
> +	uuid->clock_seq_hi_and_reserved &= ~UUID_VARIANT_MASK;
> +	uuid->clock_seq_hi_and_reserved |= (UUID_VARIANT << UUID_VARIANT_SHIFT);
> +}
> +
> +void gen_v5_guid(const struct uuid *namespace, struct efi_guid *guid, ...)
> +{
> +	sha1_context ctx;
> +	va_list args;
> +	const uint8_t *data;
> +	uint32_t *tmp32;
> +	uint16_t *tmp16;
> +	uint8_t hash[SHA1_SUM_LEN];
> +
> +	sha1_starts(&ctx);
> +	/* Hash the namespace UUID as salt */
> +	sha1_update(&ctx, (unsigned char *)namespace, UUID_BIN_LEN);
> +	va_start(args, guid);
> +
> +	while ((data = va_arg(args, const uint8_t *))) {
> +		unsigned int len = va_arg(args, size_t);
> +		sha1_update(&ctx, data, len);
> +	}
> +
> +	va_end(args);
> +	sha1_finish(&ctx, hash);
> +
> +	/* Truncate the hash into output UUID, it is already big endian */
> +	memcpy(guid, hash, sizeof(*guid));
> +
> +	configure_uuid((struct uuid *)guid, 5);


Please, provide a test that in the resulting GUID output we find the
variant and version information at the expected places:

The high nibble of the byte with index 7 should be 5.

The high nibble of the byte with index 8 should 8, 9, C, or D.

> +
> +	/* Make little endian */
> +	tmp32 = (uint32_t *)&guid->b[0];
> +	*tmp32 = be32_to_cpu(*tmp32);
> +	tmp16 = (uint16_t *)&guid->b[4];
> +	*tmp16 = be16_to_cpu(*tmp16);
> +	tmp16 = (uint16_t *)&guid->b[6];
> +	*tmp16 = be16_to_cpu(*tmp16);
> +}
> +
>   #if defined(CONFIG_RANDOM_UUID) || defined(CONFIG_CMD_UUID)
>   void gen_rand_uuid(unsigned char *uuid_bin)
>   {
>   	u32 ptr[4];
> @@ -394,15 +444,9 @@ void gen_rand_uuid(unsigned char *uuid_bin)
>   	/* Set all fields randomly */
>   	for (i = 0; i < 4; i++)
>   		ptr[i] = rand();
>
> -	clrsetbits_be16(&uuid->time_hi_and_version,
> -			UUID_VERSION_MASK,
> -			UUID_VERSION << UUID_VERSION_SHIFT);
> -
> -	clrsetbits_8(&uuid->clock_seq_hi_and_reserved,
> -		     UUID_VARIANT_MASK,
> -		     UUID_VARIANT << UUID_VARIANT_SHIFT);
> +	configure_uuid(uuid, UUID_VERSION);


Please, provide a test that in the resulting UUID output we find the
variant and version information at the expected places:

The high nibble of the byte with index 6 should be 4.

The high nibble of the byte with index 8 should 8, 9, C, or D.

Best regards

Heinrich

>
>   	memcpy(uuid_bin, uuid, 16);
>   }
>
>



More information about the U-Boot mailing list