//==================================================================// /* AtomicParsley - uuid.cpp AtomicParsley is GPL software; you can freely distribute, redistribute, modify & use under the terms of the GNU General Public License; either version 2 or its successor. AtomicParsley is distributed under the GPL "AS IS", without any warranty; without the implied warranty of merchantability or fitness for either an expressed or implied particular purpose. Please see the included GNU General Public License (GPL) for your rights and further details; see the file COPYING. If you cannot, write to the Free Software Foundation, 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org Copyright (C) 2006-2007 puck_lock with contributions from others; see the CREDITS file */ //==================================================================// //==================================================================// /* Much of AP_Create_UUID_ver5_sha1_name was derived from http://www.ietf.org/rfc/rfc4122.txt which I don't believe conflicts with or restricts the GPL. And this page: http://home.famkruithof.net/guid-uuid-namebased.html tells me I'm not on crack when I try to calculate the uuids myself. Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. Copyright (c) 1998 Microsoft. To anyone who acknowledges that this file is provided "AS IS" without any express or implied warranty: permission to use, copy, modify, and distribute this file for any purpose is hereby granted without fee, provided that the above copyright notices and this notice appears in all source code copies, and that none of the names of Open Software Foundation, Inc., Hewlett-Packard Company, Microsoft, or Digital Equipment Corporation be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Neither Open Software Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment Corporation makes any representations about the suitability of this software for any purpose. */ //==================================================================// #include "AtomicParsley.h" /*---------------------- print_hash hash - the string array of the sha1 hash prints out the hex representation of the 16 byte hash - this relates to sha1, but its here to keep the sha1 files as close to original as possible ----------------------*/ void print_hash(char hash[]) { for (int i = 0; i < 20; i++) { fprintf(stdout, "%02x", (uint8_t)hash[i]); } fprintf(stdout, "\n"); return; } /*---------------------- Swap_Char in_str - the string to have the swap operation performed on str_len - the amount of bytes to swap in the string Make a pointer to the start & end of the string, as well as a temporary string to hold the swapped byte. As the start increments up, the end decrements down. Copy the byte at each advancing start position. Copy the byte of the diminishing end string into the start byte, then advance the start byte. Finaly, set each byte of the decrementing end pointer to the temp string byte. ----------------------*/ void Swap_Char(char *in_str, uint8_t str_len) { char *start_str, *end_str, temp_str; start_str = in_str; end_str = start_str + str_len; while (start_str < --end_str) { temp_str = *start_str; *start_str++ = *end_str; *end_str = temp_str; } return; } /*---------------------- APar_endian_uuid_bin_str_conversion raw_uuid - a binary string representation of a uuid As a string representation of a uuid, there is a 32-bit & 2 16-bit numbers in the uuid. These members need to be swapped on big endian systems. ----------------------*/ void APar_endian_uuid_bin_str_conversion(char *raw_uuid) { #if defined(__ppc__) || defined(__ppc64__) return; // we are *naturally* network byte ordered - simplicity #else Swap_Char(raw_uuid, 4); Swap_Char(raw_uuid + 4, 2); Swap_Char(raw_uuid + 4 + 2, 2); return; #endif } /*---------------------- APar_print_uuid uuid - a uuid structure containing the uuid Print out a full string representation of a uuid ----------------------*/ void APar_print_uuid(ap_uuid_t *uuid, bool new_line) { fprintf(stdout, "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid->time_low, uuid->time_mid, uuid->time_hi_and_version, uuid->clock_seq_hi_and_reserved, uuid->clock_seq_low, uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], uuid->node[4], uuid->node[5]); if (new_line) fprintf(stdout, "\n"); return; } /*---------------------- APar_sprintf_uuid uuid - a uuid structure containing the uuid destination - the end result uuid will be placed here Put a binary representation of a uuid to a human-readable ordered uuid string ----------------------*/ void APar_sprintf_uuid(ap_uuid_t *uuid, char *destination) { sprintf(destination, "%08" PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid->time_low, uuid->time_mid, uuid->time_hi_and_version, uuid->clock_seq_hi_and_reserved, uuid->clock_seq_low, uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], uuid->node[4], uuid->node[5]); return; } /*---------------------- APar_uuid_scanf in_formed_uuid - pointer to a string (or a place in a string) where to place a binary (hex representation) string uuid of 16 bytes raw_uuid - the string that contains a string representation of a uuid (from cli input for example). This string isn't 16 bytes - its 36 Skip past a hyphen, make any upper case characters lower (ahh, that hex 'Q') to do a manual scanf on the string. Add its hex representation as a number for 1/2 of the bits (a single byte is 2 hex characters), shift it over to the upper bits, and repeat adding the lower bits. Repeat until done. ----------------------*/ uint8_t APar_uuid_scanf(char *in_formed_uuid, const char *raw_uuid_in) { char *uuid_str, *end_uuid_str, *uuid_byte; uint8_t uuid_pos, uuid_len; uint8_t keeprap = 0; #if defined(_WIN32) && !defined(__CYGWIN__) char *raw_uuid = _strdup(raw_uuid_in); #else char *raw_uuid = strdup(raw_uuid_in); #endif uuid_len = strlen( raw_uuid); // it will be like "55534d54-21d2-4fce-bb88-695cfac9c740" uuid_str = raw_uuid; uuid_pos = 0; end_uuid_str = uuid_str + uuid_len; while (uuid_str < end_uuid_str) { uuid_byte = &in_formed_uuid[uuid_pos]; if (uuid_str[0] == '-') uuid_str++; if (uuid_str[0] >= 'A' && uuid_str[0] <= 90) uuid_str[0] += 32; if (uuid_str[1] >= 'A' && uuid_str[1] <= 90) uuid_str[0] += 32; for (int i = 0; i <= 1; i++) { switch (uuid_str[i]) { case '0': { keeprap = 0; break; } case '1': { keeprap = 1; break; } case '2': { keeprap = 2; break; } case '3': { keeprap = 3; break; } case '4': { keeprap = 4; break; } case '5': { keeprap = 5; break; } case '6': { keeprap = 6; break; } case '7': { keeprap = 7; break; } case '8': { keeprap = 8; break; } case '9': { keeprap = 9; break; } case 'a': { keeprap = 10; break; } case 'b': { keeprap = 11; break; } case 'c': { keeprap = 12; break; } case 'd': { keeprap = 13; break; } case 'e': { keeprap = 14; break; } case 'f': { keeprap = 15; break; } } if (i == 0) { *uuid_byte = keeprap << 4; } else { *uuid_byte |= keeprap; //(keeprap & 0xF0); } } uuid_str += 2; uuid_pos++; } APar_endian_uuid_bin_str_conversion(in_formed_uuid); free(raw_uuid); return uuid_pos; } /*---------------------- APar_extract_uuid_version uuid - a uuid structure containing the uuid binary_uuid_str - a binary string rep of a uuid (without dashes, just the hex) Test the 6th byte in a str and push the bits to get the version or take the 3rd member of a uuid (uint16_t) and shift bits by 12 ----------------------*/ uint8_t APar_extract_uuid_version(ap_uuid_t *uuid, char *binary_uuid_str) { uint8_t uuid_ver = 0; if (binary_uuid_str != NULL) { uuid_ver = (binary_uuid_str[6] >> 4); } else if (uuid != NULL) { uuid_ver = (uuid->time_hi_and_version >> 12); } return uuid_ver; } /*---------------------- AP_Create_UUID_ver5_sha1_name uuid - pointer to the final version 5 sha1 hash bashed uuid desired_namespace - the input uuid used as a basis for the hash; a ver5 uuid is a namespace/name uuid. This is the namespace portion name - this is the name portion used to make a v5 sha1 hash namelen - length of name (currently a strlen() value) This will create a version 5 sha1 based uuid of a name in a namespace. The desired_namespace has its endian members swapped to newtwork byte ordering. The sha1 hash algorithm is fed the reordered netord_namespace uuid to create a hash; the name is then added to the hash to create a hash of the name in the namespace. The final hash is then copied into the out_uuid, and the endian members of the ap_uuid_t are swapped to endian ordering. ----------------------*/ void AP_Create_UUID_ver5_sha1_name(ap_uuid_t *out_uuid, ap_uuid_t desired_namespace, const char *name, int namelen) { sha1_ctx sha_state; char hash[20]; ap_uuid_t networkorderd_namespace; // swap the endian members of uuid to network byte order for hash uniformity // across platforms (the NULL or AP.sf.net uuid) networkorderd_namespace = desired_namespace; networkorderd_namespace.time_low = SWAP32(networkorderd_namespace.time_low); networkorderd_namespace.time_mid = SWAP16(networkorderd_namespace.time_mid); networkorderd_namespace.time_hi_and_version = SWAP16(networkorderd_namespace.time_hi_and_version); // make a hash of the input desired_namespace (as netord_ns); add the name // (the AP.sf.net namespace as string or the atom name) sha1_init_ctx(&sha_state); sha1_process_bytes((char *)&networkorderd_namespace, sizeof networkorderd_namespace, &sha_state); sha1_process_bytes(name, namelen, &sha_state); sha1_finish_ctx(&sha_state, hash); // quasi uuid sha1hash is network byte ordered. swap the endian members of the // uuid (uint32_t & uint16_t) to local byte order this creates additional // requirements later that have to be APar_endian_uuid_bin_str_conversion() // swapped, but leave this for now for coherence memcpy(out_uuid, hash, sizeof *out_uuid); out_uuid->time_low = SWAP32(out_uuid->time_low); out_uuid->time_mid = SWAP16(out_uuid->time_mid); out_uuid->time_hi_and_version = SWAP16(out_uuid->time_hi_and_version); out_uuid->time_hi_and_version &= 0x0FFF; // mask hash octes 6&7 out_uuid->time_hi_and_version |= (5 << 12); // set bits 12-15 to the version (5 for sha1 namespace/name // hash uuids) out_uuid->clock_seq_hi_and_reserved &= 0x3F; // mask hash octet 8 out_uuid->clock_seq_hi_and_reserved |= 0x80; // Set the two most significant bits (bits 6 and 7) of the // clock_seq_hi_and_reserved to zero and one, respectively. return; } /*---------------------- AP_Create_UUID_ver3_random out_uuid - pointer to the final version 3 randomly generated uuid Use a high quality random number generator (still requiring a seed) to generate a random uuid. In 2.5 million creations, all have been unique (at least on Mac OS X with sranddev providing the initial seed). ----------------------*/ void AP_Create_UUID_ver3_random(ap_uuid_t *out_uuid) { uint32_t rand1 = 0; out_uuid->time_low = xor4096i(); rand1 = xor4096i(); out_uuid->time_mid = (rand1 >> 16) & 0xFFFF; out_uuid->node[0] = (rand1 >> 8) & 0xFF; out_uuid->node[1] = (rand1 >> 0) & 0xFF; rand1 = xor4096i(); out_uuid->node[2] = (rand1 >> 24) & 0xFF; out_uuid->node[3] = (rand1 >> 16) & 0xFF; out_uuid->node[4] = (rand1 >> 8) & 0xFF; out_uuid->node[5] = (rand1 >> 0) & 0xFF; rand1 = xor4096i(); out_uuid->time_hi_and_version = (rand1 >> 16) & 0xFFFF; out_uuid->clock_seq_low = (rand1 >> 8) & 0xFF; out_uuid->clock_seq_hi_and_reserved = (rand1 >> 0) & 0x3F; out_uuid->clock_seq_hi_and_reserved |= 0x40; // bits 6 & 7 must be 0 & 1 respectively out_uuid->time_hi_and_version &= 0x0FFF; out_uuid->time_hi_and_version |= (3 << 12); // set bits 12-15 to the version (3 for peusdo-random/random) return; } /*---------------------- APar_generate_uuid_from_atomname atom_name - the 4 character atom name to create a uuid for uuid_binary_str - the destination for the created uuid (as a hex string) This will create a 16-byte universal unique identifier for any atom name in the AtomicParsley namespace. 1. Make a namespace for all uuids to derive from (here its AP.sf.net) 2. Make a sha1 hash of the namespace string; use that hash as the basis for a v5 uuid into a NULL/blank uuid to create an AP namespace uuid 3. Make a sha2 hash of the atom_name string; use that hash as the basis for a v5 uuid into an AtomicParsley namespace uuid to create the final uuid. 4. copy the uuid structure into a binary string representation ----------------------*/ void APar_generate_uuid_from_atomname(char *atom_name, char *uuid_binary_str) { ap_uuid_t blank_namespace = {0x00000000, 0x0000, 0x0000, 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; ap_uuid_t APar_namespace_uuid; ap_uuid_t AP_atom_uuid; AP_Create_UUID_ver5_sha1_name( &APar_namespace_uuid, blank_namespace, "AtomicParsley.sf.net", 20); AP_Create_UUID_ver5_sha1_name( &AP_atom_uuid, APar_namespace_uuid, atom_name, 4); memset(uuid_binary_str, 0, 20); memcpy(uuid_binary_str, &AP_atom_uuid, sizeof(AP_atom_uuid)); return; } void APar_generate_random_uuid(char *uuid_binary_str) { ap_uuid_t rand_uuid = {0x00000000, 0x0000, 0x0000, 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; AP_Create_UUID_ver3_random(&rand_uuid); memcpy(uuid_binary_str, &rand_uuid, sizeof(rand_uuid)); return; } void APar_generate_test_uuid() { ap_uuid_t blank_ns = {0x00000000, 0x0000, 0x0000, 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; ap_uuid_t APar_ns_uuid; ap_uuid_t APar_test_uuid; AP_Create_UUID_ver5_sha1_name( &APar_ns_uuid, blank_ns, "AtomicParsley.sf.net", 20); APar_print_uuid( &APar_ns_uuid); // should be aa80eaf3-1f72-5575-9faa-de9388dc2a90 fprintf(stdout, "uuid for 'cprt' in AP namespace: "); AP_Create_UUID_ver5_sha1_name(&APar_test_uuid, APar_ns_uuid, "cprt", 4); APar_print_uuid( &APar_test_uuid); //'cprt' should be 4bd39a57-e2c8-5655-a4fb-7a19620ef151 // uuid_t domain_ns_uuid = { 0x6ba7b810, 0x9dad, 0x11d1, // 0x80, // 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; // // 6ba7b810-9dad-11d1-80b4-00c04fd430c8 a blank representation of a blank // domain return; }