// // Created by brett on 15/08/23. // #if !defined(BLT_UUID_H) && defined(__has_include) && __has_include() #define BLT_UUID_H #include #include #include #include #include #include #include #include namespace blt::uuid { // from https://www.rfc-editor.org/rfc/rfc4122#section-4.3 union uuid_t { struct { u_int32_t time_low; u_int16_t time_mid; u_int16_t time_hi_and_version; u_int8_t clock_seq_hi_and_reserved; u_int8_t clock_seq_low; u_int8_t node[6]; } uuid; u_int8_t str[16]; }; /* Name string is a fully-qualified domain name */ constexpr static uuid_t NameSpace_DNS = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */ {0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, {0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}} }; /* Name string is a URL */ constexpr static uuid_t NameSpace_URL = { /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 */ {0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, {0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}} }; /* Name string is an ISO OID */ constexpr static uuid_t NameSpace_OID = { /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 */ {0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, {0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}} }; /* Name string is an X.500 DN (in DER or a text output format) */ constexpr static uuid_t NameSpace_X500 = { /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */ {0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, {0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}} }; class malformed_uuid_exception : public std::runtime_error { public: explicit malformed_uuid_exception(const std::string& error = ""): std::runtime_error(error) {} }; static unsigned char hex2byte(const char* hex) { unsigned short byte = 0; std::istringstream iss(hex); iss >> std::hex >> byte; return byte % 0x100; } static std::string byte2hex(const uint8_t* data, int len) { std::stringstream ss; ss << std::hex; const char dash[] = {0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0}; for (int i = 0; i < len; ++i) { if (dash[i]) ss << std::dec << '-'; ss << std::hex << std::setw(2) << std::setfill('0') << (int) data[i]; } return ss.str(); } static uuid_t genV5(const std::string& objectName, uuid_t namespaceUUID = NameSpace_OID) { auto new_str = std::string(reinterpret_cast(namespaceUUID.str)) + objectName; const auto* c_str = reinterpret_cast(new_str.c_str()); unsigned char hash[SHA_DIGEST_LENGTH]; uuid_t result{}; SHA1(c_str, strlen(new_str.c_str()), hash); memcpy(result.str, hash, 16); //set high-nibble to 5 to indicate type 5 result.str[6] &= 0x0F; result.str[6] |= 0x50; //set upper two bits to "10" result.str[8] &= 0x3F; result.str[8] |= 0x80; return result; } static std::string toString(uuid_t uuid) { return byte2hex(uuid.str, 16); } static uuid_t toUUID(const std::string& str) { if (str.empty()) throw malformed_uuid_exception("expected at least 32 characters!"); if (str.size() > 36) throw malformed_uuid_exception("UUID cannot contain more then 128 bits of information!"); uuid_t uuid{}; std::string data = str; if (data.size() == 36) blt::string::replaceAll(data, "-", ""); if (data.size() == 32) { char cpy[2]; for (size_t i = 0; i < data.size(); i += 2) { cpy[0] = data[i]; cpy[1] = data[i + 1]; uuid.str[i / 2] = hex2byte(cpy); } } else throw malformed_uuid_exception("UUID is expected as a string of bytes xxxxxxxx or in uuid format 8-4-4-4-12"); return uuid; } static uuid_t genV4() { std::random_device rd; std::seed_seq seed{rd(), rd(), rd(), rd()}; std::mt19937_64 gen(seed); std::uniform_int_distribution dis(0, 15); std::uniform_int_distribution<> dis2(8, 11); std::stringstream ss; int i; ss << std::hex; for (i = 0; i < 8; i++) { ss << dis(gen); } ss << "-"; for (i = 0; i < 4; i++) { ss << dis(gen); } ss << "-4"; for (i = 0; i < 3; i++) { ss << dis(gen); } ss << "-"; ss << dis2(gen); for (i = 0; i < 3; i++) { ss << dis(gen); } ss << "-"; for (i = 0; i < 12; i++) { ss << dis(gen); }; return toUUID(ss.str()); } } #endif //BLT_UUID_H