// UUID.h // Defines the cUUID class representing a Universally Unique Identifier #include "Globals.h" #include "UUID.h" #include "mbedtls/md5.h" /** UUID normalised in textual form. */ struct sShortUUID { char Data[32]{}; bool IsValid = false; }; /** Returns the given UUID in shortened form with IsValid indicating success. Doesn't check digits are hexadecimal but does check dashes if long form. */ static sShortUUID ShortenUUID(const AString & a_StringUUID) { sShortUUID UUID; switch (a_StringUUID.size()) { case 32: { // Already a short UUID std::memcpy(UUID.Data, a_StringUUID.data(), 32); UUID.IsValid = true; break; } case 36: { // Long UUID, confirm dashed if ( (a_StringUUID[ 8] != '-') || (a_StringUUID[13] != '-') || (a_StringUUID[18] != '-') || (a_StringUUID[23] != '-') ) { break; } // Copy everying but the dashes from the string std::memcpy(UUID.Data, a_StringUUID.data(), 8); std::memcpy(UUID.Data + 8, a_StringUUID.data() + 9, 4); std::memcpy(UUID.Data + 12, a_StringUUID.data() + 14, 4); std::memcpy(UUID.Data + 16, a_StringUUID.data() + 19, 4); std::memcpy(UUID.Data + 20, a_StringUUID.data() + 24, 12); UUID.IsValid = true; } default: break; } return UUID; } /** Returns the integer value of the hex digit or 0xff if invalid. */ static Byte FromHexDigit(char a_Hex) { if (('0' <= a_Hex) && (a_Hex <= '9')) { return static_cast(a_Hex - '0'); } if (('a' <= a_Hex) && (a_Hex <= 'f')) { return static_cast(10 + (a_Hex - 'a')); } if (('A' <= a_Hex) && (a_Hex <= 'F')) { return static_cast(10 + (a_Hex - 'A')); } return 0xff; } /** From a number in the range [0, 16), returns the corresponding hex digit in lowercase. */ static char ToHexDigit(UInt8 a_Nibble) { ASSERT((a_Nibble & 0xf0) == 0); return static_cast( (a_Nibble < 10) ? ('0' + a_Nibble) : ('a' + (a_Nibble - 10)) ); } //////////////////////////////////////////////////////////////////////////////// // cUUID: bool cUUID::FromString(const AString & a_StringUUID) { sShortUUID Norm = ShortenUUID(a_StringUUID); if (!Norm.IsValid) { return false; } std::array ParsedUUID{{0}}; for (size_t i = 0; i != m_UUID.size(); ++i) { Byte HighNibble = FromHexDigit(Norm.Data[2 * i ]); Byte LowNibble = FromHexDigit(Norm.Data[2 * i + 1]); if ((HighNibble > 0x0f) || (LowNibble > 0x0f)) { // Invalid hex digit return false; } ParsedUUID[i] = static_cast((HighNibble << 4) | LowNibble); } // Parsed successfully m_UUID = ParsedUUID; return true; } AString cUUID::ToShortString() const { AString ShortString(32, '\0'); for (size_t i = 0; i != m_UUID.size(); ++i) { Byte HighNibble = (m_UUID[i] >> 4) & 0x0f; Byte LowNibble = m_UUID[i] & 0x0f; ShortString[2 * i ] = ToHexDigit(HighNibble); ShortString[2 * i + 1] = ToHexDigit(LowNibble); } return ShortString; } AString cUUID::ToLongString() const { AString LongString = ToShortString(); LongString.reserve(36); // Convert to long form by inserting the dashes auto First = LongString.begin(); LongString.insert(First + 8, '-'); LongString.insert(First + 13, '-'); LongString.insert(First + 18, '-'); LongString.insert(First + 23, '-'); return LongString; } UInt8 cUUID::Version() const { return static_cast((m_UUID[6] >> 4) & 0x0f); } UInt8 cUUID::Variant() const { const Byte VariantBits = static_cast((m_UUID[8] >> 5) & 0x07); /* Variant bits format: bits | variant | Description -----|---------|---------------------- 0xx | 0 | Obsolete 10x | 1 | Standard UUID 110 | 2 | Microsoft Legacy GUID 111 | 3 | Reserved */ if ((VariantBits & 0x04) == 0) { return 0; } else if ((VariantBits & 0x02) == 0) { return 1; } else if ((VariantBits & 0x01) == 0) { return 2; } else { return 3; } } std::array cUUID::ToRaw() const { std::array Raw(m_UUID); if (Variant() == 2) { // Convert to microsoft mixed-endian format // First 3 components are host-endian, last 2 are network auto First = reinterpret_cast(Raw.data()); *First = ntohl(*First); auto Second = reinterpret_cast(&Raw[4]); *Second = ntohs(*Second); auto Third = Second + 1; *Third = ntohs(*Third); } return Raw; } void cUUID::FromRaw(const std::array & a_Raw) { m_UUID = a_Raw; if (Variant() != 2) { // Standard big-endian formats return; } // Convert from microsoft mixed-endian format // First 3 components are host-endian, last 2 are network auto First = reinterpret_cast(m_UUID.data()); *First = htonl(*First); auto Second = reinterpret_cast(&m_UUID[4]); *Second = htons(*Second); auto Third = Second + 1; *Third = htons(*Third); } cUUID cUUID::GenerateVersion3(const AString & a_Name) { cUUID UUID; // Generate an md5 checksum, and use it as base for the ID: const Byte * ByteString = reinterpret_cast(a_Name.data()); mbedtls_md5(ByteString, a_Name.length(), UUID.m_UUID.data()); // Insert version number UUID.m_UUID[6] = (UUID.m_UUID[6] & 0x0f) | 0x30; // Insert variant number UUID.m_UUID[8] = (UUID.m_UUID[8] & 0x3f) | 0x80; return UUID; }