1 /*
2  * Copyright (c) 2025, SECO Mind Srl
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/sys/uuid.h>
8 
9 #include <ctype.h>
10 #include <string.h>
11 #include <stdio.h>
12 
13 #if defined(CONFIG_UUID_V4)
14 #include <zephyr/random/random.h>
15 #endif
16 
17 #if defined(CONFIG_UUID_V5)
18 #include <mbedtls/md.h>
19 #endif
20 
21 #if defined(CONFIG_UUID_BASE64)
22 #include <zephyr/sys/base64.h>
23 #endif
24 
25 #define UUID_STR_POSITION_FIRST_HYPHEN  (8U)
26 #define UUID_STR_POSITION_SECOND_HYPHEN (13U)
27 #define UUID_STR_POSITION_THIRD_HYPHEN  (18U)
28 #define UUID_STR_POSITION_FOURTH_HYPHEN (23U)
29 
30 #define UUID_POSITION_VERSION (6U)
31 #define UUID_OFFSET_VERSION   (4U)
32 #define UUID_MASK_VERSION     (0xF0U)
33 #define UUID_POSITION_VARIANT (8U)
34 #define UUID_OFFSET_VARIANT   (6U)
35 #define UUID_MASK_VARIANT     (0xC0)
36 
37 #define UUID_V4_VERSION (4U)
38 #define UUID_V4_VARIANT (2U)
39 #define UUID_V5_VERSION (5U)
40 #define UUID_V5_VARIANT (2U)
41 
42 #if defined(CONFIG_UUID_V4) || defined(CONFIG_UUID_V5)
overwrite_uuid_version_and_variant(uint8_t version,uint8_t variant,struct uuid * out)43 static void overwrite_uuid_version_and_variant(uint8_t version, uint8_t variant, struct uuid *out)
44 {
45 	/* Clear the 'ver' and 'var' fields */
46 	out->val[UUID_POSITION_VERSION] &= ~UUID_MASK_VERSION;
47 	out->val[UUID_POSITION_VARIANT] &= ~UUID_MASK_VARIANT;
48 	/* Update the 'ver' and 'var' fields */
49 	out->val[UUID_POSITION_VERSION] |= (uint8_t)(version << UUID_OFFSET_VERSION);
50 	out->val[UUID_POSITION_VARIANT] |= (uint8_t)(variant << UUID_OFFSET_VARIANT);
51 }
52 #endif
53 
should_be_hyphen(unsigned int position)54 static bool should_be_hyphen(unsigned int position)
55 {
56 	switch (position) {
57 	case UUID_STR_POSITION_FIRST_HYPHEN:
58 	case UUID_STR_POSITION_SECOND_HYPHEN:
59 	case UUID_STR_POSITION_THIRD_HYPHEN:
60 	case UUID_STR_POSITION_FOURTH_HYPHEN:
61 		return true;
62 	default:
63 		return false;
64 	}
65 }
66 
67 #if defined(CONFIG_UUID_V4)
uuid_generate_v4(struct uuid * out)68 int uuid_generate_v4(struct uuid *out)
69 {
70 	if (out == NULL) {
71 		return -EINVAL;
72 	}
73 	/* Fill the whole UUID struct with a random number */
74 	sys_rand_get(out->val, UUID_SIZE);
75 	/* Update version and variant */
76 	overwrite_uuid_version_and_variant(UUID_V4_VERSION, UUID_V4_VARIANT, out);
77 	return 0;
78 }
79 #endif
80 
81 #if defined(CONFIG_UUID_V5)
uuid_generate_v5(const struct uuid * ns,const void * data,size_t data_size,struct uuid * out)82 int uuid_generate_v5(const struct uuid *ns, const void *data, size_t data_size,
83 		     struct uuid *out)
84 {
85 	if (out == NULL) {
86 		return -EINVAL;
87 	}
88 	int ret = 0;
89 	int mbedtls_err = 0;
90 	mbedtls_md_context_t ctx = {0};
91 	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
92 	const size_t sha_1_bytes = 20;
93 	uint8_t sha_result[sha_1_bytes];
94 
95 	mbedtls_md_init(&ctx);
96 	mbedtls_err = mbedtls_md_setup(&ctx, md_info, 0);
97 	/* Might return: MBEDTLS_ERR_MD_BAD_INPUT_DATA or MBEDTLS_ERR_MD_ALLOC_FAILED */
98 	switch (mbedtls_err) {
99 	case 0:
100 		break;
101 	case MBEDTLS_ERR_MD_BAD_INPUT_DATA:
102 		ret = -EINVAL;
103 		goto exit;
104 	case MBEDTLS_ERR_MD_ALLOC_FAILED:
105 		ret = -ENOMEM;
106 		goto exit;
107 	default:
108 		ret = -ENOTSUP;
109 		goto exit;
110 	}
111 	mbedtls_err = mbedtls_md_starts(&ctx);
112 	if (mbedtls_err != 0) {
113 		/* Might return MBEDTLS_ERR_MD_BAD_INPUT_DATA */
114 		ret = -EINVAL;
115 		goto exit;
116 	}
117 	mbedtls_err = mbedtls_md_update(&ctx, ns->val, UUID_SIZE);
118 	if (mbedtls_err != 0) {
119 		/* Might return MBEDTLS_ERR_MD_BAD_INPUT_DATA */
120 		ret = -EINVAL;
121 		goto exit;
122 	}
123 	mbedtls_err = mbedtls_md_update(&ctx, data, data_size);
124 	if (mbedtls_err != 0) {
125 		/* Might return MBEDTLS_ERR_MD_BAD_INPUT_DATA */
126 		ret = -EINVAL;
127 		goto exit;
128 	}
129 	mbedtls_err = mbedtls_md_finish(&ctx, sha_result);
130 	if (mbedtls_err != 0) {
131 		/* Might return MBEDTLS_ERR_MD_BAD_INPUT_DATA */
132 		ret = -EINVAL;
133 		goto exit;
134 	}
135 
136 	/* Store the computed SHA1 in the out struct */
137 	for (unsigned int i = 0; i < UUID_SIZE; i++) {
138 		out->val[i] = sha_result[i];
139 	}
140 	/* Update version and variant */
141 	overwrite_uuid_version_and_variant(UUID_V5_VERSION, UUID_V5_VARIANT, out);
142 
143 exit:
144 	mbedtls_md_free(&ctx);
145 	return ret;
146 }
147 #endif
148 
uuid_copy(const struct uuid * data,struct uuid * out)149 int uuid_copy(const struct uuid *data, struct uuid *out)
150 {
151 	if (out == NULL) {
152 		return -EINVAL;
153 	}
154 	memcpy(out->val, data->val, UUID_SIZE);
155 	return 0;
156 }
157 
uuid_from_buffer(const uint8_t data[UUID_SIZE],struct uuid * out)158 int uuid_from_buffer(const uint8_t data[UUID_SIZE], struct uuid *out)
159 {
160 	if ((data == NULL) || (out == NULL)) {
161 		return -EINVAL;
162 	}
163 	memcpy(out->val, data, UUID_SIZE);
164 	return 0;
165 }
166 
uuid_from_string(const char data[UUID_STR_LEN],struct uuid * out)167 int uuid_from_string(const char data[UUID_STR_LEN], struct uuid *out)
168 {
169 	if ((data == NULL) || (strlen(data) + 1 != UUID_STR_LEN) || (out == NULL)) {
170 		return -EINVAL;
171 	}
172 	for (unsigned int i = 0; i < UUID_STR_LEN - 1; i++) {
173 		char char_i = data[i];
174 		/* Check that hyphens are in the right place */
175 		if (should_be_hyphen(i)) {
176 			if (char_i != '-') {
177 				return -EINVAL;
178 			}
179 			continue;
180 		}
181 		/* Check if the given input is hexadecimal */
182 		if (!isxdigit(char_i)) {
183 			return -EINVAL;
184 		}
185 	}
186 
187 	/* Content parsing */
188 	unsigned int data_idx = 0U;
189 	unsigned int out_idx = 0U;
190 
191 	while (data_idx < UUID_STR_LEN - 1) {
192 		if (should_be_hyphen(data_idx)) {
193 			data_idx += 1;
194 			continue;
195 		}
196 
197 		size_t hex2bin_rc =
198 			hex2bin(&data[data_idx], 2, &out->val[out_idx], UUID_SIZE - out_idx);
199 		if (hex2bin_rc != 1) {
200 			return -EINVAL;
201 		}
202 		out_idx++;
203 		data_idx += 2;
204 	}
205 	return 0;
206 }
207 
uuid_to_buffer(const struct uuid * data,uint8_t out[UUID_SIZE])208 int uuid_to_buffer(const struct uuid *data, uint8_t out[UUID_SIZE])
209 {
210 	if (out == NULL) {
211 		return -EINVAL;
212 	}
213 	memcpy(out, data->val, UUID_SIZE);
214 	return 0;
215 }
216 
uuid_to_string(const struct uuid * data,char out[UUID_STR_LEN])217 int uuid_to_string(const struct uuid *data, char out[UUID_STR_LEN])
218 {
219 	if (out == NULL) {
220 		return -EINVAL;
221 	}
222 	snprintf(out, UUID_STR_LEN,
223 		 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
224 		 data->val[0], data->val[1], data->val[2], data->val[3], data->val[4], data->val[5],
225 		 data->val[6], data->val[7], data->val[8], data->val[9], data->val[10],
226 		 data->val[11], data->val[12], data->val[13], data->val[14], data->val[15]);
227 	return 0;
228 }
229 
230 #if defined(CONFIG_UUID_BASE64)
uuid_to_base64(const struct uuid * data,char out[UUID_BASE64_LEN])231 int uuid_to_base64(const struct uuid *data, char out[UUID_BASE64_LEN])
232 {
233 	if (out == NULL) {
234 		return -EINVAL;
235 	}
236 
237 	size_t olen = 0;
238 
239 	base64_encode(out, UUID_BASE64_LEN, &olen, data->val, UUID_SIZE);
240 	return 0;
241 }
242 
uuid_to_base64url(const struct uuid * data,char out[UUID_BASE64URL_LEN])243 int uuid_to_base64url(const struct uuid *data, char out[UUID_BASE64URL_LEN])
244 {
245 	if (out == NULL) {
246 		return -EINVAL;
247 	}
248 
249 	/* Convert UUID to RFC 3548/4648 base 64 notation */
250 	size_t olen = 0;
251 	char uuid_base64[UUID_BASE64_LEN] = {0};
252 
253 	base64_encode(uuid_base64, UUID_BASE64_LEN, &olen, data->val, UUID_SIZE);
254 	/* Convert UUID to RFC 4648 sec. 5 URL and filename safe base 64 notation */
255 	for (unsigned int i = 0; i < UUID_BASE64URL_LEN - 1; i++) {
256 		if (uuid_base64[i] == '+') {
257 			uuid_base64[i] = '-';
258 		}
259 		if (uuid_base64[i] == '/') {
260 			uuid_base64[i] = '_';
261 		}
262 	}
263 	memcpy(out, uuid_base64, UUID_BASE64URL_LEN - 1);
264 	out[UUID_BASE64URL_LEN - 1] = 0;
265 	return 0;
266 }
267 #endif
268