Extend uuid component for FWU
This change extends uuid operations to better meet FWU and
GPT based configuration requirements. The change adds more
explicit support for the little-endian octet representation.
Although the little-endian octet order is not what's specified
in RFC4122, it is used by the UEFI specification which covers
disk and partition UUIDs. Because the acronym 'guid' is used
within the UEFI spec, the little-endian representation is
referred to as 'guid' in the names of uuid functions to
distinguish them from functions that assume standard octet
order.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: Ie3d1f3d9fe44ad80c84e541c69fdd2cbfdacd128
diff --git a/components/common/uuid/test/uuid_tests.cpp b/components/common/uuid/test/uuid_tests.cpp
index 03f4f87..9a278a6 100644
--- a/components/common/uuid/test/uuid_tests.cpp
+++ b/components/common/uuid/test/uuid_tests.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -14,100 +14,146 @@
TEST(UuidTests, parseValidUuidLowerCase) {
- /* A valid UUID unsing lower-case */
- const char *uuid_text = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
- CHECK(uuid_is_valid(uuid_text));
+ /* A valid UUID using lower-case */
+ const char *uuid_text = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
+ CHECK(uuid_is_valid(uuid_text));
- const uint8_t expected_bytes[] =
- {0xd9,0xdf,0x52,0xd5,
- 0x16,0xa2,
- 0x4b,0xb2,
- 0x9a,0xa4,
- 0xd2,0x6d,0x3b,0x84,0xe8,0xc0};
+ const uint8_t expected_bytes[] =
+ {0xd9,0xdf,0x52,0xd5,
+ 0x16,0xa2,
+ 0x4b,0xb2,
+ 0x9a,0xa4,
+ 0xd2,0x6d,0x3b,0x84,0xe8,0xc0};
- uint8_t byte_array[UUID_OCTETS_LEN];
- memset(byte_array, 0, sizeof(byte_array));
+ uint8_t byte_array[UUID_OCTETS_LEN];
+ memset(byte_array, 0, sizeof(byte_array));
- CHECK(uuid_parse_to_octets(uuid_text, byte_array, sizeof(byte_array)));
- CHECK(memcmp(byte_array, expected_bytes, UUID_OCTETS_LEN) == 0);
+ CHECK(uuid_parse_to_octets(uuid_text, byte_array, sizeof(byte_array)));
+ CHECK(memcmp(byte_array, expected_bytes, UUID_OCTETS_LEN) == 0);
}
TEST(UuidTests, parseValidUuidMixedCase) {
- /* A valid UUID unsing mixed-case */
- const char *uuid_text = "D9df52d5-16a2-4bB2-9aa4-d26d3b84E8c0";
- CHECK(uuid_is_valid(uuid_text));
+ /* A valid UUID using mixed-case */
+ const char *uuid_text = "D9df52d5-16a2-4bB2-9aa4-d26d3b84E8c0";
+ CHECK(uuid_is_valid(uuid_text));
- const uint8_t expected_bytes[] =
- {0xd9,0xdf,0x52,0xd5,
- 0x16,0xa2,
- 0x4b,0xb2,
- 0x9a,0xa4,
- 0xd2,0x6d,0x3b,0x84,0xe8,0xc0};
+ const uint8_t expected_bytes[] =
+ {0xd9,0xdf,0x52,0xd5,
+ 0x16,0xa2,
+ 0x4b,0xb2,
+ 0x9a,0xa4,
+ 0xd2,0x6d,0x3b,0x84,0xe8,0xc0};
- uint8_t byte_array[UUID_OCTETS_LEN];
- memset(byte_array, 0, sizeof(byte_array));
+ uint8_t byte_array[UUID_OCTETS_LEN];
+ memset(byte_array, 0, sizeof(byte_array));
- CHECK(uuid_parse_to_octets(uuid_text, byte_array, sizeof(byte_array)));
- CHECK(memcmp(byte_array, expected_bytes, UUID_OCTETS_LEN) == 0);
+ CHECK(uuid_parse_to_octets(uuid_text, byte_array, sizeof(byte_array)));
+ CHECK(memcmp(byte_array, expected_bytes, UUID_OCTETS_LEN) == 0);
}
TEST(UuidTests, parseUuidInUrn) {
- /* A valid UUID embedded in a urn */
- const char *urn_text = "urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66:0";
- CHECK(uuid_is_valid(&urn_text[9]));
+ /* A valid UUID embedded in a urn */
+ const char *urn_text = "urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66:0";
+ CHECK(uuid_is_valid(&urn_text[9]));
- const uint8_t expected_bytes[] =
- {0x6e,0x8b,0xc4,0x30,
- 0x9c,0x3a,
- 0x11,0xd9,
- 0x96,0x69,
- 0x08,0x00,0x20,0x0c,0x9a,0x66};
+ const uint8_t expected_bytes[] =
+ {0x6e,0x8b,0xc4,0x30,
+ 0x9c,0x3a,
+ 0x11,0xd9,
+ 0x96,0x69,
+ 0x08,0x00,0x20,0x0c,0x9a,0x66};
- uint8_t byte_array[UUID_OCTETS_LEN];
- memset(byte_array, 0, sizeof(byte_array));
+ uint8_t byte_array[UUID_OCTETS_LEN];
+ memset(byte_array, 0, sizeof(byte_array));
- CHECK(uuid_parse_to_octets(&urn_text[9], byte_array, sizeof(byte_array)));
- CHECK(memcmp(byte_array, expected_bytes, UUID_OCTETS_LEN) == 0);
+ CHECK(uuid_parse_to_octets(&urn_text[9], byte_array, sizeof(byte_array)));
+ CHECK(memcmp(byte_array, expected_bytes, UUID_OCTETS_LEN) == 0);
}
TEST(UuidTests, parseError) {
- /* Invalid digit */
- const char *broken1 = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8X0";
- CHECK(!uuid_is_valid(broken1));
+ /* Invalid digit */
+ const char *broken1 = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8X0";
+ CHECK(!uuid_is_valid(broken1));
- /* Invalid separator */
- const char *broken2 = "d9df52d5-16a2-4bb2-9aa4_d26d3b84e8c0";
- CHECK(!uuid_is_valid(broken2));
+ /* Invalid separator */
+ const char *broken2 = "d9df52d5-16a2-4bb2-9aa4_d26d3b84e8c0";
+ CHECK(!uuid_is_valid(broken2));
- /* Too short */
- const char *broken3 = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c";
- CHECK(!uuid_is_valid(broken3));
+ /* Too short */
+ const char *broken3 = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c";
+ CHECK(!uuid_is_valid(broken3));
- /* Zero length */
- const char *broken4 = "";
- CHECK(!uuid_is_valid(broken4));
+ /* Zero length */
+ const char *broken4 = "";
+ CHECK(!uuid_is_valid(broken4));
}
-TEST(UuidTests, parseValidUuidToReversed) {
+TEST(UuidTests, parseValidUuidToGuidOctets) {
- /* A valid UUID unsing lower-case */
- const char *uuid_text = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
- CHECK(uuid_is_valid(uuid_text));
+ /* A valid UUID using lower-case */
+ const char *uuid_text = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
+ CHECK(uuid_is_valid(uuid_text));
- /* Reversed ouput is expected to be */
- const uint8_t expected_bytes[] =
- {0xd5,0x52,0xdf,0xd9,
- 0xa2,0x16,
- 0xb2,0x4b,
- 0x9a,0xa4,
- 0xd2,0x6d,0x3b,0x84,0xe8,0xc0};
+ /* GUID octet representation is expected to be */
+ const uint8_t expected_bytes[] =
+ {0xd5,0x52,0xdf,0xd9,
+ 0xa2,0x16,
+ 0xb2,0x4b,
+ 0x9a,0xa4,
+ 0xd2,0x6d,0x3b,0x84,0xe8,0xc0};
- uint8_t byte_array[UUID_OCTETS_LEN];
- memset(byte_array, 0, sizeof(byte_array));
+ uint8_t byte_array[UUID_OCTETS_LEN];
+ memset(byte_array, 0, sizeof(byte_array));
- CHECK(uuid_parse_to_octets_reversed(uuid_text, byte_array, sizeof(byte_array)));
- CHECK(memcmp(byte_array, expected_bytes, UUID_OCTETS_LEN) == 0);
+ CHECK(uuid_parse_to_guid_octets(uuid_text, byte_array, sizeof(byte_array)));
+ CHECK(memcmp(byte_array, expected_bytes, UUID_OCTETS_LEN) == 0);
}
+
+TEST(UuidTests, checkOctetsFromCanonical) {
+
+ /* A valid UUID */
+ const char *uuid_text = "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
+ CHECK(uuid_is_valid(uuid_text));
+
+ struct uuid_octets result1;
+
+ memset(&result1, 0x22, sizeof(result1));
+ uuid_octets_from_canonical(&result1, uuid_text);
+
+ struct uuid_octets result2;
+
+ memset(&result2, 0xaa, sizeof(result2));
+ size_t valid_chars = uuid_parse_to_octets(uuid_text,
+ result2.octets, sizeof(result2.octets));
+
+ UNSIGNED_LONGS_EQUAL(UUID_CANONICAL_FORM_LEN, valid_chars);
+ MEMCMP_EQUAL(result2.octets, result1.octets, sizeof(result2.octets));
+}
+
+TEST(UuidTests, checkIsEqualOperation) {
+
+ struct uuid_octets uuid_a;
+ struct uuid_octets uuid_b;
+
+ uuid_guid_octets_from_canonical(&uuid_a, "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0");
+ uuid_guid_octets_from_canonical(&uuid_b, "2435fa44-0951-4ce9-bcf4-1ad08d77cbff");
+
+ CHECK_FALSE(uuid_is_equal(uuid_a.octets, uuid_b.octets));
+ CHECK_TRUE(uuid_is_equal(uuid_a.octets, uuid_a.octets));
+ CHECK_TRUE(uuid_is_equal(uuid_b.octets, uuid_b.octets));
+}
+
+TEST(UuidTests, checkNilUuidOperations) {
+
+ struct uuid_octets uuid;
+
+ uuid_guid_octets_from_canonical(&uuid, "00000000-0000-0000-0000-000000000000");
+ CHECK_TRUE(uuid_is_equal(uuid_get_nil()->octets, uuid.octets));
+ CHECK_TRUE(uuid_is_nil(uuid.octets));
+
+ uuid_guid_octets_from_canonical(&uuid, "00000000-0000-0000-0000-000000000003");
+ CHECK_FALSE(uuid_is_nil(uuid.octets));
+}
\ No newline at end of file
diff --git a/components/common/uuid/uuid.c b/components/common/uuid/uuid.c
index 6f8b2ec..7bf33ed 100644
--- a/components/common/uuid/uuid.c
+++ b/components/common/uuid/uuid.c
@@ -1,10 +1,11 @@
/*
- * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "uuid.h"
+#include <assert.h>
#include <string.h>
#include <ctype.h>
@@ -33,9 +34,13 @@
size_t uuid_is_valid(const char *canonical_form)
{
size_t valid_chars = 0;
- size_t input_len = strlen(canonical_form);
- if (input_len >= UUID_CANONICAL_FORM_LEN) {
+ /* Note that a vaild canonical uuid may be part of a longer string
+ * such as a urn.
+ */
+ size_t input_len = strnlen(canonical_form, UUID_CANONICAL_FORM_LEN);
+
+ if (input_len == UUID_CANONICAL_FORM_LEN) {
size_t i;
valid_chars = UUID_CANONICAL_FORM_LEN;
@@ -46,7 +51,7 @@
if (canonical_form[i] != '-') return 0;
}
else {
- if (!isxdigit(canonical_form[i])) return 0;
+ if (!isxdigit((int)canonical_form[i])) return 0;
}
}
}
@@ -54,6 +59,23 @@
return valid_chars;
}
+bool uuid_is_equal(const uint8_t *octets_a, const uint8_t *octets_b)
+{
+ return memcmp(octets_a, octets_b, UUID_OCTETS_LEN) == 0;
+}
+
+bool uuid_is_nil(const uint8_t *octets)
+{
+ return memcmp(uuid_get_nil()->octets, octets, UUID_OCTETS_LEN) == 0;
+}
+
+const struct uuid_octets *uuid_get_nil(void)
+{
+ static const struct uuid_octets nil_uuid = {0};
+
+ return &nil_uuid;
+}
+
size_t uuid_parse_to_octets(const char *canonical_form, uint8_t *buf, size_t buf_size)
{
size_t octet_index = 0;
@@ -105,39 +127,38 @@
}
/*
- * The byte order is reversed for the first 4 bytes, then 2 bytes, then 2 bytes.
- * This is for compatibility with TF-A and OP-TEE where a binary uuid is represented as
- * an uint32_t, 2x uint16_t, then uint8_t array.
+ * The byte order is reversed for the integer sections of the UUID. Converts
+ * from standard to GUID octet representations an visa versa.
*/
-void uuid_reverse_octets(const struct uuid_octets *standard_encoding,
+void uuid_reverse_octets(const struct uuid_octets *input_octets,
uint8_t *buf, size_t buf_size)
{
if (buf_size >= UUID_OCTETS_LEN) {
/* Reverse bytes in each section */
- buf[0] = standard_encoding->octets[3];
- buf[1] = standard_encoding->octets[2];
- buf[2] = standard_encoding->octets[1];
- buf[3] = standard_encoding->octets[0];
+ buf[0] = input_octets->octets[3];
+ buf[1] = input_octets->octets[2];
+ buf[2] = input_octets->octets[1];
+ buf[3] = input_octets->octets[0];
- buf[4] = standard_encoding->octets[5];
- buf[5] = standard_encoding->octets[4];
+ buf[4] = input_octets->octets[5];
+ buf[5] = input_octets->octets[4];
- buf[6] = standard_encoding->octets[7];
- buf[7] = standard_encoding->octets[6];
+ buf[6] = input_octets->octets[7];
+ buf[7] = input_octets->octets[6];
- buf[8] = standard_encoding->octets[8];
- buf[9] = standard_encoding->octets[9];
+ buf[8] = input_octets->octets[8];
+ buf[9] = input_octets->octets[9];
- buf[10] = standard_encoding->octets[10];
- buf[11] = standard_encoding->octets[11];
- buf[12] = standard_encoding->octets[12];
- buf[13] = standard_encoding->octets[13];
- buf[14] = standard_encoding->octets[14];
- buf[15] = standard_encoding->octets[15];
+ buf[10] = input_octets->octets[10];
+ buf[11] = input_octets->octets[11];
+ buf[12] = input_octets->octets[12];
+ buf[13] = input_octets->octets[13];
+ buf[14] = input_octets->octets[14];
+ buf[15] = input_octets->octets[15];
}
}
-size_t uuid_parse_to_octets_reversed(const char *canonical_form,
+size_t uuid_parse_to_guid_octets(const char *canonical_form,
uint8_t *buf, size_t buf_size)
{
size_t valid_chars;
@@ -153,3 +174,24 @@
return valid_chars;
}
+
+void uuid_octets_from_canonical(struct uuid_octets *uuid_octets,
+ const char *canonical_form)
+{
+ size_t valid_chars = uuid_parse_to_octets(canonical_form,
+ uuid_octets->octets, sizeof(uuid_octets->octets));
+
+ /* Input string is assumed to be valid. Should not be used if canonical
+ * string originates from an untrusted source.
+ */
+ assert(valid_chars == UUID_CANONICAL_FORM_LEN);
+}
+
+void uuid_guid_octets_from_canonical(struct uuid_octets *uuid_octets,
+ const char *canonical_form)
+{
+ size_t valid_chars = uuid_parse_to_guid_octets(canonical_form,
+ uuid_octets->octets, sizeof(uuid_octets->octets));
+
+ assert(valid_chars == UUID_CANONICAL_FORM_LEN);
+}
diff --git a/components/common/uuid/uuid.h b/components/common/uuid/uuid.h
index 02d83b6..33921ee 100644
--- a/components/common/uuid/uuid.h
+++ b/components/common/uuid/uuid.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2022, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -8,10 +8,11 @@
#define COMMON_UUID_H
#include <stddef.h>
+#include <stdbool.h>
#include <stdint.h>
-#define UUID_OCTETS_LEN (16)
-#define UUID_CANONICAL_FORM_LEN (36)
+#define UUID_OCTETS_LEN (16)
+#define UUID_CANONICAL_FORM_LEN (36)
#ifdef __cplusplus
@@ -20,10 +21,19 @@
/*
* Structure for holding an octet representation of a UUID.
+ *
+ * There are two common byte orderings for octet representations of UUIDs.
+ * RFC4122 specifies that integer fields from the UUID data structure
+ * should be presented with most significant bytes first. An alternative
+ * byte ordering, used by UEFI, presents integer values with the reverse
+ * byte order (little Endian). The UEFI specification uses the GUID rather
+ * than UUID acronym. To distinguish between the two octet representations,
+ * functions that assume the little Endian octet encoding include 'guid' in
+ * the function name.
*/
struct uuid_octets
{
- uint8_t octets[UUID_OCTETS_LEN];
+ uint8_t octets[UUID_OCTETS_LEN];
};
/*
@@ -31,7 +41,7 @@
*/
struct uuid_canonical
{
- char characters[UUID_CANONICAL_FORM_LEN + 1];
+ char characters[UUID_CANONICAL_FORM_LEN + 1];
};
/*
@@ -42,28 +52,54 @@
size_t uuid_is_valid(const char *canonical_form);
/*
+ * Check if two octet uuid representations are equal
+ */
+bool uuid_is_equal(const uint8_t *octets_a, const uint8_t *octets_b);
+
+/*
+ * Check if octets represent a nil uuid
+ */
+bool uuid_is_nil(const uint8_t *octets);
+
+/*
+ * Return a const nil uuid
+ */
+const struct uuid_octets *uuid_get_nil(void);
+
+/*
* Parses a uuid string in canonical string form, outputting as an array of bytes
* in the standard big endian byte order. Returns the number of characters parsed
* from the input string. Returns zero if there is a parsing error.
*/
size_t uuid_parse_to_octets(const char *canonical_form,
- uint8_t *buf, size_t buf_size);
+ uint8_t *buf, size_t buf_size);
/*
- * Parses a uuid string in canonical string form but instead of outputting octets
- * in standard byte order, octets from each section of the canonical uuid are
- * reversed.
+ * Parses a uuid string in canonical string form, outputting as an array of bytes
+ * in little endian byte order (GUID order).
*/
-size_t uuid_parse_to_octets_reversed(const char *canonical_form,
- uint8_t *buf, size_t buf_size);
+size_t uuid_parse_to_guid_octets(const char *canonical_form,
+ uint8_t *buf, size_t buf_size);
/*
- * Reverses bytes from the normal big endian binary encoding to the reversed encoding used
- * by tf-a and optee (same byte order as uuid_parse_to_octets_reversed()).
+ * Convert from one octet representation to the other. Works both ways.
*/
-void uuid_reverse_octets(const struct uuid_octets *standard_encoding,
- uint8_t *buf, size_t buf_size);
+void uuid_reverse_octets(const struct uuid_octets *input_octets,
+ uint8_t *buf, size_t buf_size);
+/*
+ * Converts a valid canonical uuid string to the standard octet byte order. Should only
+ * be used if the input canonical string is trusted to be valid. Will assert if it's not.
+ */
+void uuid_octets_from_canonical(struct uuid_octets *uuid_octets,
+ const char *canonical_form);
+
+/*
+ * Converts a valid canonical uuid string to the GUID octet byte order. Should only
+ * be used if the input canonical string is trusted to be valid. Will assert if it's not.
+ */
+void uuid_guid_octets_from_canonical(struct uuid_octets *uuid_octets,
+ const char *canonical_form);
#ifdef __cplusplus
}