/*========================================================================= Program: GDCM (Grassroots DICOM). A DICOM library Copyright (c) 2006-2011 Mathieu Malaterre All rights reserved. See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "gdcmUIDGenerator.h" #include "gdcmTrace.h" #include "gdcmSystem.h" #include #include // FIXME... #if defined(_WIN32) || defined(__CYGWIN__) #define HAVE_UUIDCREATE #else #define HAVE_UUID_GENERATE #endif #ifdef HAVE_UUID_GENERATE #include "gdcm_uuid.h" #endif #ifdef GDCM_HAVE_RPC_H #include #endif namespace gdcm { /* * This is just plain bad luck. GDCM UID root is 26 byte long * And all implementation of the DCE UUID (Theodore Y. Ts'o) * are based on a uint128_t (unsigned char [16]). Which * means that converted to a base 10 number they require at * least 39 bytes to fit in memory, since the highest possible * number is 256**16 - 1 = 340282366920938463463374607431768211455 * Unfortunately root + '.' + suffix should be at most 64 bytes * * So to get a full UUID implementation as per RFC 4122 * http://www.ietf.org/rfc/rfc4122.txt we need a shorter * root... * */ const char UIDGenerator::GDCM_UID[] = "1.2.826.0.1.3680043.2.1143"; std::string UIDGenerator::Root = GetGDCMUID(); // The following contains the *encoded* hardware address (not the raw as in ipconfig/ifconfig) std::string UIDGenerator::EncodedHardwareAddress; // = System::GetHardwareAddress(); const char *UIDGenerator::GetRoot() { return Root.c_str(); } void UIDGenerator::SetRoot(const char * root) { assert( IsValid( root ) ); Root = root; } const char *UIDGenerator::GetGDCMUID() { return GDCM_UID; } /* * http://www.isthe.com/chongo/tech/comp/fnv/ */ #define FNV1_64_INIT ((uint64_t)0xcbf29ce484222325ULL) struct fnv_hash { static uint64_t hash(const char* pBuffer, size_t nByteLen) { uint64_t nHashVal = FNV1_64_INIT, nMagicPrime = 0x00000100000001b3ULL; const unsigned char* pFirst = ( const unsigned char* )( pBuffer ), * pLast = pFirst + nByteLen; while( pFirst < pLast ) { nHashVal ^= *pFirst++; nHashVal *= nMagicPrime; } return nHashVal; } }; /* Implementation note: You cannot set a root of more than 26 bytes (which should already enough for most people). Since implementation is only playing with the first 8bits of the upper */ const char* UIDGenerator::Generate() { Unique = GetRoot(); // We choose here a value of 26 so that we can still have 37 bytes free to // set the suffix part which is sufficient to store a 2^(128-8+1)-1 number if( Unique.empty() || Unique.size() > 62 ) // 62 is simply the highest possible limit { // I cannot go any further... return nullptr; } unsigned char uuid[16]; bool r = UIDGenerator::GenerateUUID(uuid); // This should only happen in some obscure cases. Since the creation of UUID failed // I should try to go any further and make sure the user's computer crash and burn // right away if( !r ) return nullptr; char randbytesbuf[64]; size_t len = System::EncodeBytes(randbytesbuf, uuid, sizeof(uuid)); assert( len < 64 ); // programmer error Unique += "."; // This dot is compulsory to separate root from suffix if( Unique.size() + len > 64 ) { int idx = 0; bool found = false; std::bitset<8> x; while( !found && idx < 16 ) /* 16 is insane ... oh well */ { // too bad ! randbytesbuf is too long, let's try to truncate the high bits a little x = uuid[idx]; unsigned int i = 0; while( ( Unique.size() + len > 64 ) && i < 8 ) { x[7-i] = false; uuid[idx] = (unsigned char)x.to_ulong(); len = System::EncodeBytes(randbytesbuf, uuid, sizeof(uuid)); ++i; } if( ( Unique.size() + len > 64 ) && i == 8 ) { // too bad only reducing the 8 bits from uuid[idx] was not enough, // let's set to zero the following bits... idx++; } else { // cool we found enough to stop found = true; } } if( !found ) { // Technically this could only happen when root has a length >= 64 ... is it // even remotely possible ? gdcmWarningMacro( "Root is too long for current implementation" ); return nullptr; } } // can now safely use randbytesbuf as is, no need to truncate any more: Unique += randbytesbuf; assert( IsValid( Unique.c_str() ) ); return Unique.c_str(); } /* return true on success */ bool UIDGenerator::GenerateUUID(unsigned char *uuid_data) { #if defined(HAVE_UUID_GENERATE) uuid_t g; uuid_generate(g); memcpy(uuid_data, g, sizeof(uuid_t)); #elif defined(HAVE_UUID_CREATE) uint32_t rv; uuid_t g; uuid_create(&g, &rv); if (rv != uuid_s_ok) return false; memcpy(uuid_data, &g, sizeof(uuid_t)); #elif defined(HAVE_UUIDCREATE) if (FAILED(UuidCreate((UUID *)uuid_data))) { return false; } #else #error should not happen #endif return true; } bool UIDGenerator::IsValid(const char *uid_) { /* 9.1 UID ENCODING RULES The DICOM UID encoding rules are defined as follows: - Each component of a UID is a number and shall consist of one or more digits. The first digit of each component shall not be zero unless the component is a single digit. Note: Registration authorities may distribute components with non-significant leading zeroes. The leading zeroes should be ignored when being encoded (ie. 00029 would be encoded 29). - Each component numeric value shall be encoded using the characters 0-9 of the Basic G0 Set of the International Reference Version of ISO 646:1990 (the DICOM default character repertoire). - Components shall be separated by the character "." (2EH). - If ending on an odd byte boundary, except when used for network negotiation (See PS 3.8), one trailing NULL (00H), as a padding character, shall follow the last component in order to align the UID on an even byte boundary. - UID's, shall not exceed 64 total characters, including the digits of each component, separators between components, and the NULL (00H) padding character if needed. */ /* * FIXME: This is not clear in the standard, but I believe a trailing '.' is not allowed since * this is considered as a separator for components */ if( !uid_ ) return false; std::string uid = uid_; if( uid.size() > 64 || uid.empty() ) { return false; } if( uid[0] == '.' || uid[uid.size()-1] == '.' ) // important to do that first { return false; } if( uid.size() < 3 ) return false; if( uid[0] == '0' && uid[1] != '.' ) return false; std::string::size_type i = 0; for(; i < uid.size(); ++i) { if( uid[i] == '.' ) // if test is true we are guarantee that next char is valid (see previous check) { // check that next character is neither '0' (except single number) not '.' if( uid[i+1] == '.' ) { return false; } else if( uid[i+1] == '0' ) // character is guarantee to exist since '.' is not last char { // Need to check first if we are not at the end of string if( i+2 != uid.size() && uid[i+2] != '.' ) { return false; } } } else if ( !isdigit( (unsigned char)uid[i] ) ) { return false; } } // no error found ! return true; } } // end namespace gdcm