blob: 85ac21653d73fc3af8486ce366c75d5bb48a07f1 [file] [log] [blame]
Julian Halla7e89b02020-11-23 17:33:31 +01001/*
Julian Hall31d4fdd2022-12-01 16:52:01 +00002 * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.
Julian Halla7e89b02020-11-23 17:33:31 +01003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include "uuid.h"
Julian Hall31d4fdd2022-12-01 16:52:01 +00008#include <assert.h>
Julian Halla7e89b02020-11-23 17:33:31 +01009#include <string.h>
10#include <ctype.h>
Gabor Toth7728fad2024-08-06 13:53:57 +020011#include <compiler.h>
Julian Hall7d7b24c2021-08-13 13:40:38 +010012
13static uint8_t hex_to_nibble(char hex)
14{
15 uint8_t nibble = 0;
16
17 if (hex >= '0' && hex <= '9') {
18 nibble = hex - '0';
19 }
20 else {
21 nibble = ((hex | 0x20) - 'a') + 10;
22 }
23
24 return nibble;
25}
26
Julian Hall1df937d2023-03-21 10:34:36 +000027static char nibble_to_hex(uint8_t nibble)
28{
29 char hex;
30
31 nibble &= 0x0f;
32
33 if (nibble <= 9)
34 hex = '0' + nibble;
35 else
36 hex = 'a' + nibble - 10;
37
38 return hex;
39}
40
Julian Hall7d7b24c2021-08-13 13:40:38 +010041static uint8_t hex_to_byte(const char *hex)
42{
43 /* Takes a validated input and returns the byte value */
44 uint8_t byte = hex_to_nibble(hex[0]) << 4;
45 byte |= (hex_to_nibble(hex[1]) & 0x0f);
46 return byte;
47}
Julian Halla7e89b02020-11-23 17:33:31 +010048
49size_t uuid_is_valid(const char *canonical_form)
50{
Julian Hall7d7b24c2021-08-13 13:40:38 +010051 size_t valid_chars = 0;
Julian Halla7e89b02020-11-23 17:33:31 +010052
Julian Hall1df937d2023-03-21 10:34:36 +000053 /* Note that a valid canonical uuid may be part of a longer string
Julian Hall31d4fdd2022-12-01 16:52:01 +000054 * such as a urn.
55 */
Gabor Toth983264f2024-01-23 09:16:24 +010056 if (!memchr(canonical_form, '\0', UUID_CANONICAL_FORM_LEN)) {
Julian Hall7d7b24c2021-08-13 13:40:38 +010057 size_t i;
58 valid_chars = UUID_CANONICAL_FORM_LEN;
Julian Halla7e89b02020-11-23 17:33:31 +010059
Julian Hall7d7b24c2021-08-13 13:40:38 +010060 for (i = 0; i < UUID_CANONICAL_FORM_LEN; ++i) {
Julian Halla7e89b02020-11-23 17:33:31 +010061
Julian Hall7d7b24c2021-08-13 13:40:38 +010062 if (i == 8 || i == 13 || i == 18 || i == 23) {
63 if (canonical_form[i] != '-') return 0;
64 }
65 else {
Julian Hall31d4fdd2022-12-01 16:52:01 +000066 if (!isxdigit((int)canonical_form[i])) return 0;
Julian Hall7d7b24c2021-08-13 13:40:38 +010067 }
68 }
69 }
Julian Halla7e89b02020-11-23 17:33:31 +010070
Julian Hall7d7b24c2021-08-13 13:40:38 +010071 return valid_chars;
Julian Halla7e89b02020-11-23 17:33:31 +010072}
73
Julian Hall31d4fdd2022-12-01 16:52:01 +000074bool uuid_is_equal(const uint8_t *octets_a, const uint8_t *octets_b)
75{
76 return memcmp(octets_a, octets_b, UUID_OCTETS_LEN) == 0;
77}
78
79bool uuid_is_nil(const uint8_t *octets)
80{
81 return memcmp(uuid_get_nil()->octets, octets, UUID_OCTETS_LEN) == 0;
82}
83
84const struct uuid_octets *uuid_get_nil(void)
85{
86 static const struct uuid_octets nil_uuid = {0};
87
88 return &nil_uuid;
89}
90
Julian Halla7e89b02020-11-23 17:33:31 +010091size_t uuid_parse_to_octets(const char *canonical_form, uint8_t *buf, size_t buf_size)
92{
Julian Hall7d7b24c2021-08-13 13:40:38 +010093 size_t octet_index = 0;
94 const char *pos;
95 size_t valid_chars = uuid_is_valid(canonical_form);
Julian Halla7e89b02020-11-23 17:33:31 +010096
Julian Hall7d7b24c2021-08-13 13:40:38 +010097 if ((buf_size < UUID_OCTETS_LEN) ||
98 (valid_chars != UUID_CANONICAL_FORM_LEN)) {
99 /* Invalid input */
100 return 0;
101 }
Julian Halla7e89b02020-11-23 17:33:31 +0100102
Julian Hall7d7b24c2021-08-13 13:40:38 +0100103 /*
104 * UUID string has been validates as having the following form:
105 * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
106 * 4 2 2 2 6
107 */
108 pos = &canonical_form[0];
109 while (octet_index < 4) {
110 buf[octet_index++] = hex_to_byte(pos);
111 pos += 2;
112 }
Julian Halla7e89b02020-11-23 17:33:31 +0100113
Julian Hall7d7b24c2021-08-13 13:40:38 +0100114 pos = &canonical_form[9];
115 while (octet_index < 6) {
116 buf[octet_index++] = hex_to_byte(pos);
117 pos += 2;
118 }
Julian Halla7e89b02020-11-23 17:33:31 +0100119
Julian Hall7d7b24c2021-08-13 13:40:38 +0100120 pos = &canonical_form[14];
121 while (octet_index < 8) {
122 buf[octet_index++] = hex_to_byte(pos);
123 pos += 2;
124 }
Julian Halla7e89b02020-11-23 17:33:31 +0100125
Julian Hall7d7b24c2021-08-13 13:40:38 +0100126 pos = &canonical_form[19];
127 while (octet_index < 10) {
128 buf[octet_index++] = hex_to_byte(pos);
129 pos += 2;
130 }
Julian Halla7e89b02020-11-23 17:33:31 +0100131
Julian Hall7d7b24c2021-08-13 13:40:38 +0100132 pos = &canonical_form[24];
133 while (octet_index < 16) {
134 buf[octet_index++] = hex_to_byte(pos);
135 pos += 2;
136 }
Julian Halla7e89b02020-11-23 17:33:31 +0100137
Julian Hall7d7b24c2021-08-13 13:40:38 +0100138 return valid_chars;
Julian Halla7e89b02020-11-23 17:33:31 +0100139}
140
141/*
Julian Hall31d4fdd2022-12-01 16:52:01 +0000142 * The byte order is reversed for the integer sections of the UUID. Converts
143 * from standard to GUID octet representations an visa versa.
Julian Halla7e89b02020-11-23 17:33:31 +0100144 */
Julian Hall31d4fdd2022-12-01 16:52:01 +0000145void uuid_reverse_octets(const struct uuid_octets *input_octets,
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100146 uint8_t *buf, size_t buf_size)
147{
148 if (buf_size >= UUID_OCTETS_LEN) {
149 /* Reverse bytes in each section */
Julian Hall31d4fdd2022-12-01 16:52:01 +0000150 buf[0] = input_octets->octets[3];
151 buf[1] = input_octets->octets[2];
152 buf[2] = input_octets->octets[1];
153 buf[3] = input_octets->octets[0];
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100154
Julian Hall31d4fdd2022-12-01 16:52:01 +0000155 buf[4] = input_octets->octets[5];
156 buf[5] = input_octets->octets[4];
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100157
Julian Hall31d4fdd2022-12-01 16:52:01 +0000158 buf[6] = input_octets->octets[7];
159 buf[7] = input_octets->octets[6];
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100160
Julian Hall31d4fdd2022-12-01 16:52:01 +0000161 buf[8] = input_octets->octets[8];
162 buf[9] = input_octets->octets[9];
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100163
Julian Hall31d4fdd2022-12-01 16:52:01 +0000164 buf[10] = input_octets->octets[10];
165 buf[11] = input_octets->octets[11];
166 buf[12] = input_octets->octets[12];
167 buf[13] = input_octets->octets[13];
168 buf[14] = input_octets->octets[14];
169 buf[15] = input_octets->octets[15];
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100170 }
171}
172
Julian Hall31d4fdd2022-12-01 16:52:01 +0000173size_t uuid_parse_to_guid_octets(const char *canonical_form,
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100174 uint8_t *buf, size_t buf_size)
Julian Halla7e89b02020-11-23 17:33:31 +0100175{
Julian Hall7d7b24c2021-08-13 13:40:38 +0100176 size_t valid_chars;
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100177 struct uuid_octets standard_encoding;
Julian Halla7e89b02020-11-23 17:33:31 +0100178
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100179 valid_chars = uuid_parse_to_octets(canonical_form,
180 standard_encoding.octets, sizeof(standard_encoding.octets));
Julian Halla7e89b02020-11-23 17:33:31 +0100181
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100182 if (valid_chars == UUID_CANONICAL_FORM_LEN) {
Julian Halla7e89b02020-11-23 17:33:31 +0100183
Julian Hall7c2ae0c2022-09-29 08:27:07 +0100184 uuid_reverse_octets(&standard_encoding, buf, buf_size);
Julian Hall7d7b24c2021-08-13 13:40:38 +0100185 }
Julian Halla7e89b02020-11-23 17:33:31 +0100186
Julian Hall7d7b24c2021-08-13 13:40:38 +0100187 return valid_chars;
Julian Halla7e89b02020-11-23 17:33:31 +0100188}
Julian Hall31d4fdd2022-12-01 16:52:01 +0000189
190void uuid_octets_from_canonical(struct uuid_octets *uuid_octets,
191 const char *canonical_form)
192{
Gabor Toth7728fad2024-08-06 13:53:57 +0200193 __maybe_unused size_t valid_chars = uuid_parse_to_octets(canonical_form,
Julian Hall31d4fdd2022-12-01 16:52:01 +0000194 uuid_octets->octets, sizeof(uuid_octets->octets));
195
196 /* Input string is assumed to be valid. Should not be used if canonical
197 * string originates from an untrusted source.
198 */
199 assert(valid_chars == UUID_CANONICAL_FORM_LEN);
200}
201
202void uuid_guid_octets_from_canonical(struct uuid_octets *uuid_octets,
203 const char *canonical_form)
204{
Gabor Toth7728fad2024-08-06 13:53:57 +0200205 __maybe_unused size_t valid_chars = uuid_parse_to_guid_octets(canonical_form,
Julian Hall31d4fdd2022-12-01 16:52:01 +0000206 uuid_octets->octets, sizeof(uuid_octets->octets));
207
208 assert(valid_chars == UUID_CANONICAL_FORM_LEN);
209}
Julian Hall1df937d2023-03-21 10:34:36 +0000210
211void uuid_canonical_from_octets(struct uuid_canonical *canonical_form,
212 const struct uuid_octets *uuid_octets)
213{
214 unsigned int octet_index = 0;
215 unsigned int char_index = 0;
216
217 while (octet_index < UUID_OCTETS_LEN) {
218
219 canonical_form->characters[char_index++] =
220 nibble_to_hex(uuid_octets->octets[octet_index] >> 4);
221
222 canonical_form->characters[char_index++] =
223 nibble_to_hex(uuid_octets->octets[octet_index] & 0x0f);
224
225 ++octet_index;
226
227 if ((octet_index == 4) ||
228 (octet_index == 6) ||
229 (octet_index == 8) ||
230 (octet_index == 10))
231 canonical_form->characters[char_index++] = '-';
232 }
233
234 canonical_form->characters[char_index] = '\0';
235}
236
237void uuid_canonical_from_guid_octets(struct uuid_canonical *canonical_form,
238 const struct uuid_octets *uuid_octets)
239{
240 struct uuid_octets reversed_octets;
241
242 uuid_reverse_octets(uuid_octets, reversed_octets.octets, sizeof(reversed_octets.octets));
243 uuid_canonical_from_octets(canonical_form, &reversed_octets);
244}