diff mbox series

[v5,02/11] lib: uuid: add UUID v5 support

Message ID 20240719-b4-dynamic-uuid-v5-2-8a83de3fe3dc@linaro.org
State Superseded
Headers show
Series efi: CapsuleUpdate: support for dynamic UUIDs | expand

Commit Message

Caleb Connolly July 19, 2024, 12:43 p.m. UTC
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@linaro.org>
---
 include/uuid.h | 17 +++++++++++++++--
 lib/Kconfig    |  1 +
 lib/uuid.c     | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 67 insertions(+), 9 deletions(-)

Comments

Ilias Apalodimas July 25, 2024, 10:13 a.m. UTC | #1
On Fri, 19 Jul 2024 at 15:43, Caleb Connolly <caleb.connolly@linaro.org> 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@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 2059219a1207..5a48c016d2c5 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);
> +       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);
> +
> +       /* 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);

you need to explicitly convert those to LE, instead of relying to the
native cpu endianess here

cheers
/Ilias
> +}
> +
>  #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);
>
>         memcpy(uuid_bin, uuid, 16);
>  }
>
>
> --
> 2.45.2
>
Caleb Connolly July 31, 2024, 10:16 a.m. UTC | #2
>> +       /* 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);
> 
> you need to explicitly convert those to LE, instead of relying to the
> native cpu endianess here

The EFI spec iirc is not explicit on the binary format for GUIDs on big 
endian systems (and currently doesn't support them at all??). Is LE 
always correct??
> 
> cheers
> /Ilias
>> +}
>> +
>>   #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);
>>
>>          memcpy(uuid_bin, uuid, 16);
>>   }
>>
>>
>> --
>> 2.45.2
>>
Ilias Apalodimas July 31, 2024, 12:01 p.m. UTC | #3
Hi Caleb,

On Wed, 31 Jul 2024 at 13:16, Caleb Connolly <caleb.connolly@linaro.org> wrote:
>
>
> >> +       /* 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);
> >
> > you need to explicitly convert those to LE, instead of relying to the
> > native cpu endianess here
>
> The EFI spec iirc is not explicit on the binary format for GUIDs on big
> endian systems (and currently doesn't support them at all??). Is LE
> always correct??

Appendix A on GUIDs and formats says
"It should also be noted that TimeLow, TimeMid, TimeHighAndVersion
fields in the EFI are encoded as little endian".  So, the native
endianness shouldn't matter

Cheers
/Ilias

> >
> > cheers
> > /Ilias
> >> +}
> >> +
> >>   #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);
> >>
> >>          memcpy(uuid_bin, uuid, 16);
> >>   }
> >>
> >>
> >> --
> >> 2.45.2
> >>
>
> --
> // Caleb (they/them)
diff mbox series

Patch

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 2059219a1207..5a48c016d2c5 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);
+	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);
+
+	/* 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);
 
 	memcpy(uuid_bin, uuid, 16);
 }