1#include "uuid_manager.h"
2
3#include <base/dbg.h>
4#include <base/hash_ctxt.h>
5#include <base/mem.h>
6#include <base/secure.h>
7#include <base/str.h>
8
9#include <engine/shared/packer.h>
10
11#include <algorithm>
12#include <cstdio>
13
14static const CUuid TEEWORLDS_NAMESPACE = {.m_aData: {// "e05ddaaa-c4e6-4cfb-b642-5d48e80c0029"
15 0xe0, 0x5d, 0xda, 0xaa, 0xc4, 0xe6, 0x4c, 0xfb,
16 0xb6, 0x42, 0x5d, 0x48, 0xe8, 0x0c, 0x00, 0x29}};
17
18const CUuid UUID_ZEROED = {.m_aData: {// "00000000-0000-0000-0000-000000000000"
19 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
21
22CUuid RandomUuid()
23{
24 CUuid Result;
25 secure_random_fill(bytes: &Result, length: sizeof(Result));
26
27 // set version 4 (UUID is randomly generated)
28 Result.m_aData[6] &= 0x0f;
29 Result.m_aData[6] |= 0x40;
30
31 // set variant 1 (RFC 4122)
32 Result.m_aData[8] &= 0x3f;
33 Result.m_aData[8] |= 0x80;
34
35 return Result;
36}
37
38CUuid CalculateUuid(const char *pName)
39{
40 MD5_CTX Md5;
41 md5_init(ctxt: &Md5);
42 md5_update(ctxt: &Md5, data: TEEWORLDS_NAMESPACE.m_aData, data_len: sizeof(TEEWORLDS_NAMESPACE.m_aData));
43 // Without terminating NUL.
44 md5_update(ctxt: &Md5, data: (const unsigned char *)pName, data_len: str_length(str: pName));
45 MD5_DIGEST Digest = md5_finish(ctxt: &Md5);
46
47 CUuid Result;
48 for(unsigned i = 0; i < sizeof(Result.m_aData); i++)
49 {
50 Result.m_aData[i] = Digest.data[i];
51 }
52
53 // set version 3 (UUID is generated by MD5 hashing a namespace identifier and a name)
54 Result.m_aData[6] &= 0x0f;
55 Result.m_aData[6] |= 0x30;
56
57 // set variant 1 (RFC 4122)
58 Result.m_aData[8] &= 0x3f;
59 Result.m_aData[8] |= 0x80;
60
61 return Result;
62}
63
64void FormatUuid(CUuid Uuid, char *pBuffer, unsigned BufferLength)
65{
66 unsigned char *p = Uuid.m_aData;
67 str_format(buffer: pBuffer, buffer_size: BufferLength,
68 format: "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
69 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
70 p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
71}
72
73int ParseUuid(CUuid *pUuid, const char *pBuffer)
74{
75 if(str_length(str: pBuffer) + 1 != UUID_MAXSTRSIZE)
76 {
77 return 2;
78 }
79 char aCopy[UUID_MAXSTRSIZE];
80 str_copy(dst&: aCopy, src: pBuffer);
81 // 01234567-9012-4567-9012-456789012345
82 if(aCopy[8] != '-' || aCopy[13] != '-' || aCopy[18] != '-' || aCopy[23] != '-')
83 {
84 return 1;
85 }
86 aCopy[8] = aCopy[13] = aCopy[18] = aCopy[23] = 0;
87 if(static_cast<bool>(str_hex_decode(dst: pUuid->m_aData + 0, dst_size: 4, src: aCopy + 0)) ||
88 str_hex_decode(dst: pUuid->m_aData + 4, dst_size: 2, src: aCopy + 9) ||
89 str_hex_decode(dst: pUuid->m_aData + 6, dst_size: 2, src: aCopy + 14) ||
90 str_hex_decode(dst: pUuid->m_aData + 8, dst_size: 2, src: aCopy + 19) ||
91 str_hex_decode(dst: pUuid->m_aData + 10, dst_size: 6, src: aCopy + 24))
92 {
93 return 1;
94 }
95 return 0;
96}
97
98bool CUuid::operator==(const CUuid &Other) const
99{
100 return mem_comp(a: this, b: &Other, size: sizeof(*this)) == 0;
101}
102
103bool CUuid::operator!=(const CUuid &Other) const
104{
105 return !(*this == Other);
106}
107
108bool CUuid::operator<(const CUuid &Other) const
109{
110 return mem_comp(a: this, b: &Other, size: sizeof(*this)) < 0;
111}
112
113static int GetIndex(int Id)
114{
115 return Id - OFFSET_UUID;
116}
117
118static int GetId(int Index)
119{
120 return Index + OFFSET_UUID;
121}
122
123void CUuidManager::RegisterName(int Id, const char *pName)
124{
125 dbg_assert(GetIndex(Id) == (int)m_vNames.size(), "names must be registered with increasing ID (got=%d want=%d)", GetIndex(Id), (int)m_vNames.size());
126 CName Name;
127 Name.m_pName = pName;
128 Name.m_Uuid = CalculateUuid(pName);
129 dbg_assert(LookupUuid(Name.m_Uuid) == UUID_UNKNOWN, "duplicate uuid %s", Name.m_pName);
130
131 m_vNames.push_back(x: Name);
132
133 CNameIndexed NameIndexed;
134 NameIndexed.m_Uuid = Name.m_Uuid;
135 NameIndexed.m_Id = GetIndex(Id);
136 m_vNamesSorted.insert(position: std::lower_bound(first: m_vNamesSorted.begin(), last: m_vNamesSorted.end(), val: NameIndexed), x: NameIndexed);
137}
138
139CUuid CUuidManager::GetUuid(int Id) const
140{
141 return m_vNames[GetIndex(Id)].m_Uuid;
142}
143
144const char *CUuidManager::GetName(int Id) const
145{
146 return m_vNames[GetIndex(Id)].m_pName;
147}
148
149int CUuidManager::LookupUuid(CUuid Uuid) const
150{
151 CNameIndexed Needle;
152 Needle.m_Uuid = Uuid;
153 Needle.m_Id = 0;
154 auto Range = std::equal_range(first: m_vNamesSorted.begin(), last: m_vNamesSorted.end(), val: Needle);
155 if(std::distance(first: Range.first, last: Range.second) == 1)
156 {
157 return GetId(Index: Range.first->m_Id);
158 }
159 return UUID_UNKNOWN;
160}
161
162int CUuidManager::NumUuids() const
163{
164 return m_vNames.size();
165}
166
167int CUuidManager::UnpackUuid(CUnpacker *pUnpacker) const
168{
169 CUuid Temp;
170 return UnpackUuid(pUnpacker, pOut: &Temp);
171}
172
173int CUuidManager::UnpackUuid(CUnpacker *pUnpacker, CUuid *pOut) const
174{
175 const CUuid *pUuid = (const CUuid *)pUnpacker->GetRaw(Size: sizeof(*pUuid));
176 if(pUuid == nullptr)
177 {
178 return UUID_INVALID;
179 }
180 *pOut = *pUuid;
181 return LookupUuid(Uuid: *pUuid);
182}
183
184void CUuidManager::PackUuid(int Id, CPacker *pPacker) const
185{
186 CUuid Uuid = GetUuid(Id);
187 pPacker->AddRaw(pData: &Uuid, Size: sizeof(Uuid));
188}
189
190void CUuidManager::DebugDump() const
191{
192 for(const auto &Name : m_vNames)
193 {
194 char aBuf[UUID_MAXSTRSIZE];
195 FormatUuid(Uuid: Name.m_Uuid, pBuffer: aBuf, BufferLength: sizeof(aBuf));
196 dbg_msg(sys: "uuid", fmt: "%s %s", aBuf, Name.m_pName);
197 }
198}
199