From c7e13d717c38e528ae241bf5facb51bca63f323c Mon Sep 17 00:00:00 2001 From: Nathan Wagner Date: Wed, 21 Mar 2012 03:17:39 -0700 Subject: [PATCH] Initial commit --- Makefile | 35 +++ hash.h | 77 ++++++ internal.c | 460 ++++++++++++++++++++++++++++++++++++ md5.c | 294 +++++++++++++++++++++++ pduuid.h | 40 ++++ postgres/Makefile | 21 ++ postgres/README | 125 ++++++++++ postgres/pduuid--1.0.sql | 130 ++++++++++ postgres/pduuid.control | 4 + postgres/purepguuid.sql | 132 +++++++++++ postgres/uninstall-uuid.sql | 51 ++++ postgres/uuid.c | 437 ++++++++++++++++++++++++++++++++++ rng.c | 139 +++++++++++ sha1.c | 216 +++++++++++++++++ tomcrypt_macros.h | 426 +++++++++++++++++++++++++++++++++ uuidgen.c | 67 ++++++ 16 files changed, 2654 insertions(+) create mode 100644 Makefile create mode 100644 hash.h create mode 100644 internal.c create mode 100644 md5.c create mode 100644 pduuid.h create mode 100644 postgres/Makefile create mode 100644 postgres/README create mode 100644 postgres/pduuid--1.0.sql create mode 100644 postgres/pduuid.control create mode 100644 postgres/purepguuid.sql create mode 100644 postgres/uninstall-uuid.sql create mode 100644 postgres/uuid.c create mode 100644 rng.c create mode 100644 sha1.c create mode 100644 tomcrypt_macros.h create mode 100644 uuidgen.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1b1c6bb --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +# Makefile for uuid library +# written by nathan wagner and placed in the public domain + +OBJ = md5.o sha1.o rng.o internal.o +CC=gcc + +# set the following for windows +#LDFLAGS += -liphlpapi + +all: uuidgen + +windows: libpduuid.a uuidgen.o + gcc $(CFLAGS) -Wall -L. -o $@ uuidgen.o -lpduuid $(LDFLAGS) -liphlpapi + +libpduuid.a: $(OBJ) + ar rcuv $@ $+ + ranlib $@ + +md5.o: md5.c + gcc $(CFLAGS) -Wall -fPIC -DLTC_SMALL_CODE -c -o $@ $+ + +sha1.o: sha1.c + gcc $(CFLAGS) -Wall -fPIC -DLTC_SMALL_CODE -c -o $@ $+ + +rng.o: rng.c + gcc $(CFLAGS) -Wall -fPIC -c -o $@ $+ + +internal.o: internal.c + gcc $(CFLAGS) -Wall -fPIC -c -o $@ $+ + +uuidgen: libpduuid.a uuidgen.o + gcc $(CFLAGS) -Wall -L. -o $@ uuidgen.o -lpduuid $(LDFLAGS) + +clean: + rm -f *.o *.a uuidgen postgres/*.o postgres/*.so diff --git a/hash.h b/hash.h new file mode 100644 index 0000000..a55397f --- /dev/null +++ b/hash.h @@ -0,0 +1,77 @@ +#include +#include + +typedef uint64_t ulong64; +typedef uint32_t ulong32; + +#define CRYPT_OK 0 +#define CRYPT_INVALID_ARG 1 +#define CRYPT_NOP 2 +#define CRYPT_FAIL_TESTVECTOR 3 +#define XMEMCMP memcmp +#define LTC_ARGCHK(x) + +struct sha1_state { + ulong64 length; + ulong32 state[5], curlen; + unsigned char buf[64]; +}; + +struct md5_state { + ulong64 length; + ulong32 state[4], curlen; + unsigned char buf[64]; +}; + +typedef union Hash_state { + struct sha1_state sha1; + struct md5_state md5; + void *data; +} hash_state; + +int sha1_init(hash_state * md); +int sha1_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int sha1_done(hash_state * md, unsigned char *hash); +int sha1_test(void); + +int md5_init(hash_state * md); +int md5_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int md5_done(hash_state * md, unsigned char *hash); +int md5_test(void); + +/* a simple macro for making hash "process" functions */ +#define HASH_PROCESS(func_name, compress_name, state_var, block_size) \ +int func_name (hash_state * md, const unsigned char *in, unsigned long inlen) \ +{ \ + unsigned long n; \ + int err; \ + LTC_ARGCHK(md != NULL); \ + LTC_ARGCHK(in != NULL); \ + if (md-> state_var .curlen > sizeof(md-> state_var .buf)) { \ + return CRYPT_INVALID_ARG; \ + } \ + while (inlen > 0) { \ + if (md-> state_var .curlen == 0 && inlen >= block_size) { \ + if ((err = compress_name (md, (unsigned char *)in)) != CRYPT_OK) { \ + return err; \ + } \ + md-> state_var .length += block_size * 8; \ + in += block_size; \ + inlen -= block_size; \ + } else { \ + n = MIN(inlen, (block_size - md-> state_var .curlen)); \ + memcpy(md-> state_var .buf + md-> state_var.curlen, in, (size_t)n); \ + md-> state_var .curlen += n; \ + in += n; \ + inlen -= n; \ + if (md-> state_var .curlen == block_size) { \ + if ((err = compress_name (md, md-> state_var .buf)) != CRYPT_OK) { \ + return err; \ + } \ + md-> state_var .length += 8*block_size; \ + md-> state_var .curlen = 0; \ + } \ + } \ + } \ + return CRYPT_OK; \ +} diff --git a/internal.c b/internal.c new file mode 100644 index 0000000..0fe6962 --- /dev/null +++ b/internal.c @@ -0,0 +1,460 @@ +/* + * internal functions for uuid library + * + * written by nathan wagner and placed in the public domain + */ +#include +#include +#include + +#include "hash.h" + +#include "pduuid.h" + +#ifdef __linux__ +#include +#include +#include +#include +#include +#endif + +#ifdef __APPLE__ +#include +#include +#include +#include +#endif + +#ifdef WIN32 +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#include +#endif + +static void set_time_low(pd_uuid_t *u, uint32_t low) { + u->data[0] = (low >> 24) & 0xff; + u->data[1] = (low >> 16) & 0xff; + u->data[2] = (low >> 8) & 0xff; + u->data[3] = low & 0xff; +} + +static void set_time_mid(pd_uuid_t *u, uint16_t mid) { + u->data[4] = (mid >> 8) & 0xff; + u->data[5] = mid & 0xff; +} + +static void set_time_hi(pd_uuid_t *u, uint16_t hi) { + u->data[7] = hi & 0xff; + u->data[6] &= 0xf0; + u->data[6] |= (hi >> 8) & 0xf; +} + +static void set_version(pd_uuid_t *u, uint8_t version) { + u->data[6] &= 0xf; + u->data[6] |= (version & 0xf) << 4; +} + +static void set_clock_seq_low(pd_uuid_t *u, uint8_t low) { + u->data[9] = low & 0xff; +} + +static void set_clock_seq_hi(pd_uuid_t *u, uint8_t hi) { + u->data[8] &= 0xc0; + u->data[8] |= hi & 0x3f; +} + +static void set_reserved(pd_uuid_t *u, uint8_t reserved) { + u->data[8] &= 0x3f; + u->data[8] |= (reserved & 0x3) << 6; +} + +#if 0 +static void set_node(pd_uuid_t *u, uint8_t data[6]) { + u->data[10] = data[0]; + u->data[11] = data[1]; + u->data[12] = data[2]; + u->data[13] = data[3]; + u->data[14] = data[4]; + u->data[15] = data[5]; +} +#endif + +static void set_node64(pd_uuid_t *u, uint64_t node) { + u->data[10] = (node >> 40) & 0xff; + u->data[11] = (node >> 32) & 0xff; + u->data[12] = (node >> 24) & 0xff; + u->data[13] = (node >> 16) & 0xff; + u->data[14] = (node >> 8) & 0xff; + u->data[15] = (node >> 0) & 0xff; +} + +static void set_timestamp(pd_uuid_t *u, uint64_t ts) { + set_time_low(u, ts & 0xffffffff); + set_time_mid(u, (ts >> 32) & 0xffff); + set_time_hi(u, (ts >> 48) & 0xfff); +} + +static void set_clockseq(pd_uuid_t *u, uint16_t seq) { + set_clock_seq_low(u, seq & 0xff); + set_clock_seq_hi(u, (seq >> 8) & 0xff); +} + +static void format_uuid(pd_uuid_t *u, struct pd_uuid_state *s, int version) { + set_timestamp(u, s->timestamp); + set_version(u, version); + set_clockseq(u, s->clock_sequence); + set_reserved(u, 0x2); + set_node64(u, s->node); +} + +static int obtain_global_lock(void *data) { + /* no-op */ + return 0; +} + +static int release_global_lock(void *data) { + /* no-op */ + return 0; +} + +#define GREGORIAN 122192928000000000ULL +static uint64_t current_time(void) { + uint64_t now; + struct timeval tv; + + /* TODO is this BSD specific? */ + gettimeofday(&tv, 0); + + now = (tv.tv_sec * 10000000ULL + tv.tv_usec * 10ULL) + GREGORIAN; + return now; +} + +static void random_bytes(void *buf, size_t n) { + unsigned char *s = buf; + int i = 0; + + while (i < n) { + i += pd_uuid_rng_get_bytes(s+i, n-i); + } +} + +static uint64_t random_mc_mac(struct pd_uuid_state *s) { + uint64_t node = 0; + + if (s && s->random_bytes) { + s->random_bytes(&node, sizeof node, s->rng_state); + } else { + random_bytes(&node, sizeof node); + } + node |= 0x800000000000ULL; /* rfc 4.5 */ + return node; +} + +/* + * TODO would probably make more sense to use a destination array + * rather than returning an integer + */ +static uint64_t current_node(struct pd_uuid_state *st) { + uint64_t node = 0; + +#ifdef __linux__ + struct ifconf conf; + struct ifreq *req; + struct ifreq interfaces[4]; + int s, i; + unsigned char *data; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s != -1) { + conf.ifc_len = sizeof interfaces; + conf.ifc_req = interfaces; + if (ioctl(s, SIOCGIFCONF, &conf) == 0) { + for (i=0; i < 4; i++) { + req = &conf.ifc_req[i]; + if (ioctl(s, SIOCGIFFLAGS, req) != 0) { + continue; + } + if (req->ifr_flags & IFF_LOOPBACK) { + continue; + } + if (req->ifr_flags & IFF_NOARP) { + continue; + } + if (ioctl(s, SIOCGIFHWADDR, req) == 0) { + int j; + data = (unsigned char *)req->ifr_hwaddr.sa_data; + node = data[0]; + node = node << 8; node += data[1]; + node = node << 8; node += data[2]; + node = node << 8; node += data[3]; + node = node << 8; node += data[4]; + node = node << 8; node += data[5]; + return node; + } + } + } + } +#endif + + /* this is any BSD based system i think */ +#ifdef __APPLE__ + struct ifaddrs *addrs; + struct ifaddrs *a; + struct sockaddr_dl *dl; + unsigned char *data; + if (getifaddrs(&addrs) == 0) { + for (a = addrs; a; a = a->ifa_next) { + + if (a->ifa_addr && a->ifa_addr->sa_family == AF_LINK) { + dl = (struct sockaddr_dl *)a->ifa_addr; + data = (unsigned char *)(dl->sdl_data + dl->sdl_nlen); + if (dl->sdl_alen != 6) continue; + + node = data[0]; + node = node << 8; node += data[1]; + node = node << 8; node += data[2]; + node = node << 8; node += data[3]; + node = node << 8; node += data[4]; + node = node << 8; node += data[5]; + freeifaddrs(addrs); + return node; + } + } + freeifaddrs(addrs); + } +#endif + +#if defined(WIN32) || defined(__CYGWIN__) + IP_ADAPTER_INFO addrs[4]; + DWORD size; + int rc; + unsigned char *data; + + size = sizeof addrs; + rc = GetAdaptersInfo(addrs, &size); + if (rc == ERROR_SUCCESS) { + PIP_ADAPTER_INFO info; + for (info = addrs; info; info = info->Next) { + if (info->Type != MIB_IF_TYPE_ETHERNET) { + continue; + } + data = info->Address; + node = data[0]; + node = node << 8; node += data[1]; + node = node << 8; node += data[2]; + node = node << 8; node += data[3]; + node = node << 8; node += data[4]; + node = node << 8; node += data[5]; + } + return node; + } +#endif + + /* if we get here, use a random multicast address */ + node = random_mc_mac(st); + return node; +} + +static uint16_t random_clock_sequence(struct pd_uuid_state *s) { + uint16_t seq; + + if (s && s->random_bytes) { + s->random_bytes(&seq, sizeof seq, s->rng_state); + } else { + random_bytes(&seq, sizeof seq); + } + return seq; +} + +static int read_state(struct pd_uuid_state *s) { + s->available = 0; + s->node = 0LL; + s->clock_sequence = 0; + s->timestamp = 0LL; + + return 0; +} + +static int save_state(struct pd_uuid_state *s) { + /* no op */ + return 0; +} + +static unsigned long get_bytes(void *buf, unsigned long n, void *state) { + unsigned char *s = buf; + int i = 0; + + while (i < n) { + i += pd_uuid_rng_get_bytes(s+i, n-i); + } + return i; +} + +int pd_uuid_init_state(struct pd_uuid_state *s) { + if (!s) return 0; + + s->get_lock = obtain_global_lock; + s->release_lock = release_global_lock; + s->lock_data = 0; + + s->read_state = read_state; + s->save_state = save_state; + + s->random_bytes = get_bytes; + s->rng_state = 0; + + s->available = 0; + + return 1; +} + +int pd_uuid_make_v1(struct pd_uuid_state *s, pd_uuid_t *uuid) { + struct pd_uuid_state ls; + uint64_t now; + uint64_t node; + int err; + + if (!s) { + s = &ls; + pd_uuid_init_state(s); + } + + if (s->get_lock) { + if ((err = s->get_lock(s->lock_data)) != 0) { + /* TODO set uuid to nil ? */ + /* be cute and have an "error" uuid? */ + return 0; + } + } + + if (s->read_state) { + if ((err = s->read_state(s)) != 0) { + return 0; + } + } + + now = current_time(); + node = current_node(s); + + if (!s->available || s->node != node) { + s->clock_sequence = random_clock_sequence(s); + } + s->node = node; + + if (s->available && s->timestamp > now) { + s->clock_sequence++; + } else { + s->timestamp = now; + } + + if (s->save_state) { + if ((err = s->save_state(s)) != 0) { + return 0; + } + } + + if (s->release_lock) { + if ((err = s->release_lock(s->lock_data)) != 0) { + /* TODO set uuid to nil ? */ + /* be cute and have an "error" uuid? */ + return 0; + } + } + + format_uuid(uuid, s, 1); + + return 1; +} + +int pd_uuid_make_v1mc(struct pd_uuid_state *ps, pd_uuid_t *uuid) { + struct pd_uuid_state s; + uint64_t now; + uint64_t node; + + obtain_global_lock(0); + read_state(&s); + now = current_time(); + node = random_mc_mac(&s); + if (!s.available) { + s.clock_sequence = random_clock_sequence(&s); + } + + if (s.available && s.timestamp > now) { + s.clock_sequence++; + } else { + s.timestamp = now; + } + + save_state(&s); + release_global_lock(0); + s.node = node; + + format_uuid(uuid, &s, 1); + + return 1; +} + +int pd_uuid_make_v4(struct pd_uuid_state *s, pd_uuid_t *uuid) { + random_bytes(uuid, sizeof *uuid); + set_version(uuid, 4); + set_reserved(uuid, 0x2); + return 1; +} + +int pd_set_uuid_hash(struct pd_uuid *s, void *hash, int version) { + int i; + unsigned char *h = hash; + + for (i=0;i<16;i++) { + s->data[i] = h[i]; + } + set_version(s, version); + set_reserved(s, 0x2); + return 1; +} + +int pd_uuid_make_v3(struct pd_uuid_state *s, pd_uuid_t *uuid, pd_uuid_t *ns, void *data, int len) { + hash_state hs; + unsigned char hash[16]; + + md5_init(&hs); + md5_process(&hs, ns->data, 16); + md5_process(&hs, data, len); + md5_done(&hs, hash); + pd_set_uuid_hash(uuid, hash, 3); + return 1; +} + +int pd_uuid_make_v5(struct pd_uuid_state *s, pd_uuid_t *uuid, pd_uuid_t *ns, void *data, int len) { + hash_state hs; + unsigned char hash[20]; + + sha1_init(&hs); + sha1_process(&hs, ns->data, sizeof *ns); + sha1_process(&hs, data, len); + sha1_done(&hs, hash); + pd_set_uuid_hash(uuid, hash, 5); + return 1; +} + +int pd_uuid_set_string(pd_uuid_t *uuid, char *s) { + unsigned int byte; + int i; + + for (i=0;i<16;i++) { + if (*s == 0) return 0; + if (*s == '-') s++; + + if (sscanf(s, "%02x", &byte) != 1) { + return 0; + } + s += 2; + uuid->data[i] = byte & 0xff; + } + return 1; +} diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..7a7652a --- /dev/null +++ b/md5.c @@ -0,0 +1,294 @@ +/* + * md5 implementation + * adapted from libtomcrypt by Tom St. Denis + * by nathan wagner and released into the public domain + */ + +#define ENDIAN_NEUTRAL 1 +#define LTC_SMALL_CODE 1 + +#include "tomcrypt_macros.h" +#include "hash.h" + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define G(x,y,z) (y ^ (z & (y ^ x))) +#define H(x,y,z) (x^y^z) +#define I(x,y,z) (y^(x|(~z))) + +#ifdef LTC_SMALL_CODE + +#define FF(a,b,c,d,M,s,t) \ + a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ + a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ + a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ + a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b; + +static const unsigned char Worder[64] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, + 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, + 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, + 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 +}; + +static const unsigned char Rorder[64] = { + 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, + 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, + 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, + 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 +}; + +static const ulong32 Korder[64] = { +0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL, +0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL, +0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL, +0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL, +0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL, +0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL, +0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL, +0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL +}; + +#else + +#define FF(a,b,c,d,M,s,t) \ + a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ + a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ + a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ + a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b; + + +#endif + +static int md5_compress(hash_state *md, unsigned char *buf) { + ulong32 i, W[16], a, b, c, d; +#ifdef LTC_SMALL_CODE + ulong32 t; +#endif + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32L(W[i], buf + (4*i)); + } + + /* copy state */ + a = md->md5.state[0]; + b = md->md5.state[1]; + c = md->md5.state[2]; + d = md->md5.state[3]; + +#ifdef LTC_SMALL_CODE + for (i = 0; i < 16; ++i) { + FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 32; ++i) { + GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 48; ++i) { + HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 64; ++i) { + II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + +#else + FF(a,b,c,d,W[0],7,0xd76aa478UL) + FF(d,a,b,c,W[1],12,0xe8c7b756UL) + FF(c,d,a,b,W[2],17,0x242070dbUL) + FF(b,c,d,a,W[3],22,0xc1bdceeeUL) + FF(a,b,c,d,W[4],7,0xf57c0fafUL) + FF(d,a,b,c,W[5],12,0x4787c62aUL) + FF(c,d,a,b,W[6],17,0xa8304613UL) + FF(b,c,d,a,W[7],22,0xfd469501UL) + FF(a,b,c,d,W[8],7,0x698098d8UL) + FF(d,a,b,c,W[9],12,0x8b44f7afUL) + FF(c,d,a,b,W[10],17,0xffff5bb1UL) + FF(b,c,d,a,W[11],22,0x895cd7beUL) + FF(a,b,c,d,W[12],7,0x6b901122UL) + FF(d,a,b,c,W[13],12,0xfd987193UL) + FF(c,d,a,b,W[14],17,0xa679438eUL) + FF(b,c,d,a,W[15],22,0x49b40821UL) + GG(a,b,c,d,W[1],5,0xf61e2562UL) + GG(d,a,b,c,W[6],9,0xc040b340UL) + GG(c,d,a,b,W[11],14,0x265e5a51UL) + GG(b,c,d,a,W[0],20,0xe9b6c7aaUL) + GG(a,b,c,d,W[5],5,0xd62f105dUL) + GG(d,a,b,c,W[10],9,0x02441453UL) + GG(c,d,a,b,W[15],14,0xd8a1e681UL) + GG(b,c,d,a,W[4],20,0xe7d3fbc8UL) + GG(a,b,c,d,W[9],5,0x21e1cde6UL) + GG(d,a,b,c,W[14],9,0xc33707d6UL) + GG(c,d,a,b,W[3],14,0xf4d50d87UL) + GG(b,c,d,a,W[8],20,0x455a14edUL) + GG(a,b,c,d,W[13],5,0xa9e3e905UL) + GG(d,a,b,c,W[2],9,0xfcefa3f8UL) + GG(c,d,a,b,W[7],14,0x676f02d9UL) + GG(b,c,d,a,W[12],20,0x8d2a4c8aUL) + HH(a,b,c,d,W[5],4,0xfffa3942UL) + HH(d,a,b,c,W[8],11,0x8771f681UL) + HH(c,d,a,b,W[11],16,0x6d9d6122UL) + HH(b,c,d,a,W[14],23,0xfde5380cUL) + HH(a,b,c,d,W[1],4,0xa4beea44UL) + HH(d,a,b,c,W[4],11,0x4bdecfa9UL) + HH(c,d,a,b,W[7],16,0xf6bb4b60UL) + HH(b,c,d,a,W[10],23,0xbebfbc70UL) + HH(a,b,c,d,W[13],4,0x289b7ec6UL) + HH(d,a,b,c,W[0],11,0xeaa127faUL) + HH(c,d,a,b,W[3],16,0xd4ef3085UL) + HH(b,c,d,a,W[6],23,0x04881d05UL) + HH(a,b,c,d,W[9],4,0xd9d4d039UL) + HH(d,a,b,c,W[12],11,0xe6db99e5UL) + HH(c,d,a,b,W[15],16,0x1fa27cf8UL) + HH(b,c,d,a,W[2],23,0xc4ac5665UL) + II(a,b,c,d,W[0],6,0xf4292244UL) + II(d,a,b,c,W[7],10,0x432aff97UL) + II(c,d,a,b,W[14],15,0xab9423a7UL) + II(b,c,d,a,W[5],21,0xfc93a039UL) + II(a,b,c,d,W[12],6,0x655b59c3UL) + II(d,a,b,c,W[3],10,0x8f0ccc92UL) + II(c,d,a,b,W[10],15,0xffeff47dUL) + II(b,c,d,a,W[1],21,0x85845dd1UL) + II(a,b,c,d,W[8],6,0x6fa87e4fUL) + II(d,a,b,c,W[15],10,0xfe2ce6e0UL) + II(c,d,a,b,W[6],15,0xa3014314UL) + II(b,c,d,a,W[13],21,0x4e0811a1UL) + II(a,b,c,d,W[4],6,0xf7537e82UL) + II(d,a,b,c,W[11],10,0xbd3af235UL) + II(c,d,a,b,W[2],15,0x2ad7d2bbUL) + II(b,c,d,a,W[9],21,0xeb86d391UL) +#endif + + md->md5.state[0] = md->md5.state[0] + a; + md->md5.state[1] = md->md5.state[1] + b; + md->md5.state[2] = md->md5.state[2] + c; + md->md5.state[3] = md->md5.state[3] + d; + + return CRYPT_OK; +} + +int md5_init(hash_state * md) { + md->md5.state[0] = 0x67452301UL; + md->md5.state[1] = 0xefcdab89UL; + md->md5.state[2] = 0x98badcfeUL; + md->md5.state[3] = 0x10325476UL; + md->md5.curlen = 0; + md->md5.length = 0; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(md5_process, md5_compress, md5, 64) + +int md5_done(hash_state * md, unsigned char *out) { + int i; + + if (md->md5.curlen >= sizeof(md->md5.buf)) { + return CRYPT_INVALID_ARG; + } + + /* increase the length of the message */ + md->md5.length += md->md5.curlen * 8; + + /* append the '1' bit */ + md->md5.buf[md->md5.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->md5.curlen > 56) { + while (md->md5.curlen < 64) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + md5_compress(md, md->md5.buf); + md->md5.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->md5.curlen < 56) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64L(md->md5.length, md->md5.buf+56); + md5_compress(md, md->md5.buf); + + /* copy output */ + for (i = 0; i < 4; i++) { + STORE32L(md->md5.state[i], out+(4*i)); + } + return CRYPT_OK; +} + +int md5_test(void) +{ + #if 0 + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[16]; + } tests[] = { + { "", + { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }, + { "a", + {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, + 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } }, + { "abc", + { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, + 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }, + { "message digest", + { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, + 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } }, + { "abcdefghijklmnopqrstuvwxyz", + { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, + 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, + 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } }, + { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, + 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } }, + { NULL, { 0 } } + }; + + int i; + unsigned char tmp[16]; + hash_state md; + + for (i = 0; tests[i].msg != NULL; i++) { + md5_init(&md); + md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + md5_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 16) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} diff --git a/pduuid.h b/pduuid.h new file mode 100644 index 0000000..77cbcc1 --- /dev/null +++ b/pduuid.h @@ -0,0 +1,40 @@ +#ifndef PD_UUID_H_ +#define PD_UUID_H_ 1 + +#include + +struct pd_uuid_state { + uint64_t timestamp, node; + uint16_t clock_sequence; + int available; + int (*get_lock)(void *); + int (*release_lock)(void *); + void *lock_data; + int (*read_state)(struct pd_uuid_state *s); + int (*save_state)(struct pd_uuid_state *s); + /* TODO hook for a random number generator? */ + unsigned long (*random_bytes)(void *out, unsigned long outlen, + void *prng_state); + void *rng_state; +}; + +struct pd_uuid { + uint8_t data[16]; +}; +typedef struct pd_uuid pd_uuid_t; + +int pd_uuid_init_state(struct pd_uuid_state *s); +int pd_uuid_make_v1(struct pd_uuid_state *s, pd_uuid_t *uuid); +int pd_uuid_make_v1mc(struct pd_uuid_state *s, pd_uuid_t *uuid); +int pd_uuid_make_v4(struct pd_uuid_state *s, pd_uuid_t *uuid); +int pd_uuid_make_v3(struct pd_uuid_state *s, pd_uuid_t *uuid, pd_uuid_t *ns, void *data, int len); +int pd_uuid_make_v5(struct pd_uuid_state *s, pd_uuid_t *uuid, pd_uuid_t *ns, void *data, int len); + +int pd_set_uuid_hash(pd_uuid_t *s, void *hash, int version); + +int pd_uuid_set_string(pd_uuid_t *uuid, char *s); + +unsigned long pd_uuid_rng_get_bytes(unsigned char *out, unsigned long outlen); + + +#endif diff --git a/postgres/Makefile b/postgres/Makefile new file mode 100644 index 0000000..1c24262 --- /dev/null +++ b/postgres/Makefile @@ -0,0 +1,21 @@ +# makefile for postgres uuid type support functions +# written by nathan wagner and placed in the public domain + +EXTENSION=pduuid +MODULES= pduuid +DATA= pduuid--1.0.sql +#DOCS= README.uuid_ossp +#SHLIB_LINK= -Lpduuid -lpduuid +MODULE_big= pduuid +OBJS= uuid.o ../internal.o ../rng.o ../md5.o ../sha1.o + +PG_CPPFLAGS := -I.. +PG_CONFIG= pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +#include c:/Program\ Files/PostgreSQL/8.4/lib/pgxs/src/makefiles/pgxs.mk + +# uncomment the following on mac +ifeq ($(PORTNAME), darwin) +LDFLAGS_SL += -flat_namespace -undefined suppress +endif diff --git a/postgres/README b/postgres/README new file mode 100644 index 0000000..9d0fd91 --- /dev/null +++ b/postgres/README @@ -0,0 +1,125 @@ +This software package is an addon module for postgres. It supplies +support functions for the built-in uuid data type. It was built and +tested against postgres version 8.4.0. This file and all supplied files +are written by Nathan Wagner and placed in the public domain. Bug +reports to nw@hydaspes.if.org. + +Credit: the hash functions are stripped down implementations from +libtomcrypt by Tom St Denis, also in the public domain. + +Functions supplied are uuid generation functions, conforming (with +one exception) to RFC 4122. The exception lies in the multicast +bit + +Features: + +UUID generation functions for version 1, 3, 4, and 5 UUIDS. + +RFC 4122 compliance, with the exception of the multicast bit +for randomly generated mac addresses. This module sets the correct +bit (the most significant bit), rather than the one specified in the RFC +(the least significant one). + +No external library dependency (other than the standard C library). +The uuid generation has been written from scratch and is included +with the source. + +The entire package is in the public domain, so there are no licensing +issues. + +Compiles with the usual postgres addon infrastructure. +make +make install +psql -f uuid.sql +psql -f uninstall-uuid.sql + +A pure postgres implementation is also supplied of just the generation +functions in purepguuid.sql. + +Generation Functions: + +uuid_v1() +returns a version 1 uuid. if the mac address can't be determined, +uses a random one. + +uuid_v1mc() +returns a version 1 uuid using a random mac address + +uuid_v3(uuid, text) +returns a version 3 uuid using the supplied namespace uuid and the +given input string. This is an md5 based uuid. + +uuid_v4() +returns a version 4 uuid. This is a random uuid. The odds of collision +are vanishingly low and it is safe to use for regular production uuid +generation. + +uuid_v3(uuid, text) +returns a version 5 uuid using the supplied namespace uuid and the +given input string. This is an sha1 based uuid. + +uuid_url(text) +Returns a version 5 uuid using the URL namespace. + +uuid_dns(text) +Returns a version 5 uuid using the DNS namespace. + +uuid_oid(text) +Returns a version 5 uuid using the DNS namespace. + +uuid_x500(text) +Returns a version 5 uuid using the DNS namespace. + +UUID Lookup Functions: + +uuid_recent() +returns the most recent uuid generated by this backend. Returns +the nil uuid if no uuids have been generated yet. + +uuid_nil() +Returns the nil uuid (all bits zero). + +uuid_ns_dns() +Returns the DNS namespace uuid. +6ba7b810-9dad-11d1-80b4-00c04fd430c8 + +uuid_ns_url() +Returns the URL namespace uuid. +6ba7b811-9dad-11d1-80b4-00c04fd430c8 + +uuid_ns_oid() +Returns the OID namespace uuid. +6ba7b812-9dad-11d1-80b4-00c04fd430c8 + +uuid_ns_x500() +Returns the X500 namespace uuid. +6ba7b813-9dad-11d1-80b4-00c04fd430c8 + +Casting Support: + +Cast functions and casts are created for uuids to and from data types +where this is reasonable: + +Bytea: treats the UUID as an array of bytes. This is always in +network byte order for the subfields of a uuid, where applicable. + +Numeric: treats the UUID as a 128 bit number and converts it to a numeric. + +Bit(128) and bit varying: treats the uuid as a 128 bit vector. + +Field extraction functions: + +uuid_version(uuid) +Returns an integer corresponding to the 4 bit version number. + +uuid_macaddr(uuid) +Returns a macaddr type corresponding to the mac address part of the +uuid. This function can be called on any uuid, not just version 1 uuids. + +uuid_timestamp(uuid) +Returns a timestamp corresponding to the timestamp field in the uuid. You +probably want to use uuid_timestamptz(). + +uuid_timestamptz(uuid) +Returns a timestamp with time zonecorresponding to the timestamp field in the +uuid. diff --git a/postgres/pduuid--1.0.sql b/postgres/pduuid--1.0.sql new file mode 100644 index 0000000..68c68eb --- /dev/null +++ b/postgres/pduuid--1.0.sql @@ -0,0 +1,130 @@ +-- uuid support function install script +-- written by nathan wagner and placed in the public domain + +create function uuid_v1() returns uuid as 'pduuid', 'uuid_gen_v1' +language C strict; + +create function uuid_v1mc() returns uuid as 'pduuid', 'uuid_gen_v1_mc' +language C strict; + +create function uuid_v3(uuid, text) returns uuid +as 'pduuid', 'uuid_gen_v3' +language C strict; + +create function uuid_v4() returns uuid as 'pduuid', 'uuid_gen_v4' +language C strict; + +create function uuid_v5(uuid, text) returns uuid +as 'pduuid', 'uuid_gen_v5' +language C strict; + +create function uuid_recent() returns uuid as 'pduuid', 'uuid_recent' +language C strict; + +create function uuid_nil() returns uuid as 'pduuid', 'uuid_nil' +language C strict; + +create function uuid_ns_dns() returns uuid as 'pduuid', 'uuid_dns' +language C strict; + +create function uuid_ns_oid() returns uuid as 'pduuid', 'uuid_oid' +language C strict; + +create function uuid_ns_url() returns uuid as 'pduuid', 'uuid_url' +language C strict; + +create function uuid_ns_x500() returns uuid as 'pduuid', 'uuid_x500' +language C strict; + +create function uuid_url(text) returns uuid as $$ +select uuid_v5(uuid_ns_url(), $1); +$$ +language 'sql' strict; + +create function uuid_dns(text) returns uuid as $$ +select uuid_v5(uuid_ns_dns(), $1); +$$ +language 'sql' strict; + +create function uuid_oid(text) returns uuid as $$ +select uuid_v5(uuid_ns_oid(), $1); +$$ +language 'sql' strict; + +create function uuid_x500(text) returns uuid as $$ +select uuid_v5(uuid_ns_x500(), $1); +$$ +language 'sql' strict; + +-- Casts + +-- Bytea + +create function uuid_bytea_cast(uuid) returns bytea as +'uuid', 'uuid_cast_bytea' +language C strict; + +create function uuid_bytea_uncast(bytea) returns uuid as +'uuid', 'uuid_cast_from_bytea' +language C strict; + +create cast (uuid as bytea) with function uuid_bytea_cast(uuid); + +create cast (bytea as uuid) with function uuid_bytea_uncast(bytea); + +-- Numeric + +create function uuid_numeric_cast(uuid) returns numeric as +'uuid', 'uuid_cast_numeric' +language C strict; + +create function uuid_numeric_uncast(numeric) returns uuid as +'uuid', 'uuid_cast_from_numeric' +language C strict; + +create cast (uuid as numeric) with function uuid_numeric_cast(uuid); + +create cast (numeric as uuid) with function uuid_numeric_uncast(numeric); + +-- Bit and bit varying + +create function uuid_bit_cast(uuid) returns bit(128) as +'uuid', 'uuid_cast_bit' +language C strict; + +create function uuid_bit_uncast(bit(128)) returns uuid as +'uuid', 'uuid_cast_from_bit' +language C strict; + +create cast (uuid as bit(128)) with function uuid_bit_cast(uuid); + +create cast (uuid as bit varying) with function uuid_bit_cast(uuid); + +create cast (bit(128) as uuid) with function uuid_bit_uncast(bit(128)); + +create cast (bit varying as uuid) with function uuid_bit_uncast(bit(128)); + +-- Field extraction functions + +create function uuid_version(uuid) returns integer as +'uuid', 'uuid_extract_version' +language C strict; + +create function uuid_macaddr(uuid) returns macaddr as +'uuid', 'uuid_extract_macaddr' +language C strict; + +create function uuid_timestamp(uuid) returns timestamp as +'uuid', 'uuid_extract_timestamp' +language C strict; + +create function uuid_timestamptz(uuid) +returns timestamp with time zone as +'uuid', 'uuid_extract_timestamp' +language C strict; + +-- TODO +-- cast to timestamp / extract timestamp +-- cast to macaddr / extract macaddr +-- extract variant +-- extract clock sequence diff --git a/postgres/pduuid.control b/postgres/pduuid.control new file mode 100644 index 0000000..b49e728 --- /dev/null +++ b/postgres/pduuid.control @@ -0,0 +1,4 @@ +relocatable = true +comment = 'Public Domain uuid support functions' +default_version = '1.0' + diff --git a/postgres/purepguuid.sql b/postgres/purepguuid.sql new file mode 100644 index 0000000..9e87119 --- /dev/null +++ b/postgres/purepguuid.sql @@ -0,0 +1,132 @@ +create or replace function uuid_bytespp(howmany integer) returns bytea as $$ +declare + bytes bytea; +begin + bytes = rpad('', howmany); + + for i in 0 .. howmany-1 loop + bytes := set_byte(bytes, i, (random()*256)::integer); + end loop; + + return bytes; +end; +$$ language 'plpgsql'; + +create or replace function uuid_v1pp() returns uuid as $$ +declare + bytes bytea; + byte integer; + str text; + id uuid; + tslow bytea; + tsmid bytea; + tshigh bytea; + tsi bigint; +begin + -- 122192928000000000 + + select extract(epoch from now() - '1582-10-15 00:00:00 UTC'::timestamp with time zone)::bigint * 10000000 + + extract(microseconds from now() - '1582-10-15 00:00:00 UTC'::timestamp with time zone)::integer % 1000000 * 10 + into tsi + ; + + tslow := decode(lpad(to_hex(tsi & x'ffffffff'::bigint), 8, '0'), 'hex'); + tsmid := decode(lpad(to_hex((tsi>>32) & x'ffff'::bigint), 4, '0'), 'hex'); + tshigh := decode(lpad(to_hex((tsi>>48) & x'ffff'::bigint), 4, '0'), 'hex'); + + -- we don't store any state, so the clock sequence is random, + -- and we don't have any way to get the macaddr, so that's + -- random too. though we set the multicast bit (the real one, + -- not the one in the rfc) + bytes := tslow || tsmid || tshigh || uuid_bytespp(8); + + byte = get_byte(bytes, 6); + byte = (byte & 15) | 16; + bytes = set_byte(bytes, 6, byte); + + byte = get_byte(bytes, 8); + byte = (byte & 63) | 128; + bytes = set_byte(bytes, 8, byte); + + byte = get_byte(bytes, 10); + byte = (byte & 127) | 128; + bytes = set_byte(bytes, 10, byte); + + str = encode(bytes, 'hex'); + id = str::uuid; + return id; +end; +$$ language 'plpgsql'; + +create or replace function uuid_v4pp() returns uuid as $$ +declare + bytes bytea; + byte integer; + str text; + id uuid; +begin + bytes = uuid_bytespp(16); + + byte = get_byte(bytes, 6); + byte = (byte & 15) | 64; + bytes = set_byte(bytes, 6, byte); + + byte = get_byte(bytes, 8); + byte = (byte & 63) | 128; + bytes = set_byte(bytes, 8, byte); + + str = encode(bytes, 'hex'); + id = str::uuid; + return id; +end; +$$ language 'plpgsql'; + +create or replace function uuid_v3pp(ns uuid, content bytea) returns uuid as $$ +declare + bytes bytea; + nsbytes bytea; + byte integer; + str text; + id uuid; +begin + nsbytes = decode(regexp_replace(ns::text, '-', '', 'g'), 'hex'); + bytes = decode(md5(nsbytes || content), 'hex'); + + byte = get_byte(bytes, 6); + byte = (byte & 15) | 48; + bytes = set_byte(bytes, 6, byte); + + byte = get_byte(bytes, 8); + byte = (byte & 63) | 128; + bytes = set_byte(bytes, 8, byte); + + str = encode(bytes, 'hex'); + id = str::uuid; + return id; +end; +$$ language 'plpgsql'; + +create or replace function uuid_v5pp(ns uuid, content bytea) returns uuid as $$ +declare + bytes bytea; + nsbytes bytea; + byte integer; + str text; + id uuid; +begin + nsbytes = decode(regexp_replace(ns::text, '-', '', 'g'), 'hex'); + bytes = substring(decode(sha1(nsbytes || content), 'hex') from 1 for 16); + + byte = get_byte(bytes, 6); + byte = (byte & 15) | 80; + bytes = set_byte(bytes, 6, byte); + + byte = get_byte(bytes, 8); + byte = (byte & 63) | 128; + bytes = set_byte(bytes, 8, byte); + + str = encode(bytes, 'hex'); + id = str::uuid; + return id; +end; +$$ language 'plpgsql'; diff --git a/postgres/uninstall-uuid.sql b/postgres/uninstall-uuid.sql new file mode 100644 index 0000000..7258b8e --- /dev/null +++ b/postgres/uninstall-uuid.sql @@ -0,0 +1,51 @@ +-- uuid support function uninstall script +-- written by nathan wagner and placed in the public domain + +drop function uuid_v1(); +drop function uuid_v1mc(); +drop function uuid_v3(uuid, text); +drop function uuid_v4(); +drop function uuid_v5(uuid, text); +drop function uuid_recent(); +drop function uuid_nil(); +drop function uuid_ns_dns(); +drop function uuid_ns_oid(); +drop function uuid_ns_url(); +drop function uuid_ns_x500(); +drop function uuid_url(text); +drop function uuid_dns(text); +drop function uuid_oid(text); +drop function uuid_x500(text); + +-- Casts + +-- Bytea + +drop cast if exists (uuid as bytea); +drop cast if exists (bytea as uuid); +drop function uuid_bytea_cast(uuid); +drop function uuid_bytea_uncast(bytea); + +-- Numeric + +drop cast if exists (uuid as numeric); +drop cast if exists (numeric as uuid); +drop function uuid_numeric_cast(uuid); +drop function uuid_numeric_uncast(numeric); + +-- Bit and bit varying + +drop cast if exists (uuid as bit(128)); +drop cast if exists (uuid as bit varying); +drop cast if exists (bit(128) as uuid); +drop cast if exists (bit varying as uuid); + +drop function uuid_bit_cast(uuid); +drop function uuid_bit_uncast(bit(128)); + +-- Field extraction functions + +drop function uuid_version(uuid); +drop function uuid_macaddr(uuid); +drop function uuid_timestamp(uuid); +drop function uuid_timestamptz(uuid); diff --git a/postgres/uuid.c b/postgres/uuid.c new file mode 100644 index 0000000..eb55399 --- /dev/null +++ b/postgres/uuid.c @@ -0,0 +1,437 @@ +/* + * uuid type support functions for Postgres 8.4 + * + * written by Nathan Wagner and placed in the public domain + */ +#include "postgres.h" +#include "fmgr.h" + +#include "utils/builtins.h" +#include "utils/uuid.h" +#include "utils/numeric.h" +#include "utils/inet.h" +#include "utils/timestamp.h" +#include "utils/datetime.h" +#include "utils/varbit.h" + +#include "pduuid.h" + +#define UUID_LEN_BIN 16 + +PG_MODULE_MAGIC; + +void _PG_init(void); +void _PG_fini(void); + +Datum uuid_nil(PG_FUNCTION_ARGS); +Datum uuid_dns(PG_FUNCTION_ARGS); +Datum uuid_url(PG_FUNCTION_ARGS); +Datum uuid_oid(PG_FUNCTION_ARGS); +Datum uuid_x500(PG_FUNCTION_ARGS); +Datum uuid_recent(PG_FUNCTION_ARGS); +Datum uuid_gen_v1(PG_FUNCTION_ARGS); +Datum uuid_gen_v1_mc(PG_FUNCTION_ARGS); +Datum uuid_gen_v3(PG_FUNCTION_ARGS); +Datum uuid_gen_v4(PG_FUNCTION_ARGS); +Datum uuid_gen_v5(PG_FUNCTION_ARGS); +Datum uuid_cast_bit(PG_FUNCTION_ARGS); +Datum uuid_cast_from_bit(PG_FUNCTION_ARGS); +Datum uuid_cast_numeric(PG_FUNCTION_ARGS); +Datum uuid_cast_from_numeric(PG_FUNCTION_ARGS); +Datum uuid_cast_bytea(PG_FUNCTION_ARGS); +Datum uuid_cast_from_bytea(PG_FUNCTION_ARGS); +Datum uuid_extract_version(PG_FUNCTION_ARGS); +Datum uuid_extract_macaddr(PG_FUNCTION_ARGS); +Datum uuid_extract_timestamp(PG_FUNCTION_ARGS); + +Datum uuid_ns_dns(PG_FUNCTION_ARGS); +Datum uuid_ns_url(PG_FUNCTION_ARGS); +Datum uuid_ns_oid(PG_FUNCTION_ARGS); +Datum uuid_ns_x500(PG_FUNCTION_ARGS); + +static pd_uuid_t i_gid; +static pd_uuid_t i_ns_uuid; +static pd_uuid_t *gid = NULL; +static pd_uuid_t *ns_uuid = NULL; +static struct pd_uuid_state us; + +void _PG_init(void) { + gid = &i_gid; + memset(gid, 0, 16); + ns_uuid = &i_ns_uuid; + memset(ns_uuid, 0, 16); + + pd_uuid_init_state(&us); + + return; +} + +#if 0 +void _PG_fini(void) { + if (gid) { + uuid_destroy(gid); + } + if (ns_uuid) { + uuid_destroy(ns_uuid); + } + return; +} +#endif + +PG_FUNCTION_INFO_V1(uuid_nil); +Datum uuid_nil(PG_FUNCTION_ARGS) { + pg_uuid_t *uuid; + + uuid = palloc(UUID_LEN_BIN); + memset(uuid, 0, 16); + PG_RETURN_UUID_P(uuid); +} + +PG_FUNCTION_INFO_V1(uuid_dns); +Datum uuid_dns(PG_FUNCTION_ARGS) { + pg_uuid_t *uuid; + unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, + 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; + + uuid = palloc(UUID_LEN_BIN); + memcpy(uuid, bytes, 16); + PG_RETURN_UUID_P(uuid); +} + +PG_FUNCTION_INFO_V1(uuid_url); +Datum uuid_url(PG_FUNCTION_ARGS) { + pg_uuid_t *uuid; + unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, + 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; + + uuid = palloc(UUID_LEN_BIN); + memcpy(uuid, bytes, 16); + PG_RETURN_UUID_P(uuid); +} + +PG_FUNCTION_INFO_V1(uuid_oid); +Datum uuid_oid(PG_FUNCTION_ARGS) { + pg_uuid_t *uuid; + unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, + 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; + + uuid = palloc(UUID_LEN_BIN); + memcpy(uuid, bytes, 16); + PG_RETURN_UUID_P(uuid); +} + +PG_FUNCTION_INFO_V1(uuid_x500); +Datum uuid_x500(PG_FUNCTION_ARGS) { + pg_uuid_t *uuid; + unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x13, 0x9d, 0xad, 0x11, + 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; + + uuid = palloc(UUID_LEN_BIN); + memcpy(uuid, bytes, 16); + PG_RETURN_UUID_P(uuid); +} + +static pg_uuid_t *export(pd_uuid_t *u) { + pg_uuid_t *pgid; + + pgid = palloc(UUID_LEN_BIN); + memcpy(pgid, u, 16); + + return pgid; +} + +static void import(pd_uuid_t *u, pg_uuid_t *pgid) { + memcpy(u, pgid, 16); +} + +PG_FUNCTION_INFO_V1(uuid_recent); + +Datum uuid_recent(PG_FUNCTION_ARGS) { + pg_uuid_t *pgid; + + pgid = export(gid); + PG_RETURN_UUID_P(pgid); +} + +PG_FUNCTION_INFO_V1(uuid_gen_v1); + +Datum uuid_gen_v1(PG_FUNCTION_ARGS) { + pg_uuid_t *pgid; + + if (!pd_uuid_make_v1(&us, gid)) { + elog(ERROR, "can't make uuid"); + } + + pgid = export(gid); + PG_RETURN_UUID_P(pgid); +} + +PG_FUNCTION_INFO_V1(uuid_gen_v1_mc); + +Datum uuid_gen_v1_mc(PG_FUNCTION_ARGS) { + pg_uuid_t *pgid; + + if (!pd_uuid_make_v1mc(&us, gid)) { + elog(ERROR, "can't make uuid"); + } + + pgid = export(gid); + PG_RETURN_UUID_P(pgid); +} + +PG_FUNCTION_INFO_V1(uuid_gen_v3); + +Datum uuid_gen_v3(PG_FUNCTION_ARGS) { + pg_uuid_t *pgid; + pg_uuid_t *ns; + text *t; + char *string; + + ns = PG_GETARG_UUID_P(0); + t = PG_GETARG_TEXT_P(1); + + import(ns_uuid, ns); + + string = text_to_cstring(t); + + if (!pd_uuid_make_v3(&us, gid, ns_uuid, string, strlen(string))) { + elog(ERROR, "can't make uuid"); + } + pfree(string); + + pgid = export(gid); + PG_RETURN_UUID_P(pgid); +} + +PG_FUNCTION_INFO_V1(uuid_gen_v4); + +Datum uuid_gen_v4(PG_FUNCTION_ARGS) { + pg_uuid_t *pgid; + + if (!pd_uuid_make_v4(&us, gid)) { + elog(ERROR, "can't make uuid"); + } + + pgid = export(gid); + PG_RETURN_UUID_P(pgid); +} + +PG_FUNCTION_INFO_V1(uuid_gen_v5); + +Datum uuid_gen_v5(PG_FUNCTION_ARGS) { + pg_uuid_t *pgid; + pg_uuid_t *ns; + text *t; + char *string; + + if (!gid || !ns_uuid) { + elog(ERROR, "UUID library failed to initialize"); + } + + ns = PG_GETARG_UUID_P(0); + t = PG_GETARG_TEXT_P(1); + + import(ns_uuid, ns); + + string = text_to_cstring(t); + + if (!pd_uuid_make_v5(&us, gid,ns_uuid, string, strlen(string))) { + elog(ERROR, "can't make uuid"); + } + pfree(string); + + pgid = export(gid); + PG_RETURN_UUID_P(pgid); +} + +PG_FUNCTION_INFO_V1(uuid_cast_bit); +Datum uuid_cast_bit(PG_FUNCTION_ARGS) { + VarBit *bit; + + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + + bit = palloc(VARBITTOTALLEN(128)); + bit->bit_len = 128; + bit->vl_len_ = VARBITTOTALLEN(128); + memcpy(VARBITS(bit), uuid, 16); + PG_RETURN_VARBIT_P(bit); +} + +PG_FUNCTION_INFO_V1(uuid_cast_from_bit); +Datum uuid_cast_from_bit(PG_FUNCTION_ARGS) { + pg_uuid_t *uuid; + + VarBit *bit = PG_GETARG_VARBIT_P(0); + + if (bit->bit_len != 128) { + /* Throw error? */ + elog(ERROR, "can't cast from bit(%d) to uuid, must cast from bit(128)", bit->bit_len); + } + + uuid = palloc(UUID_LEN_BIN); + memcpy(uuid, VARBITS(bit), 16); + PG_RETURN_UUID_P(uuid); +} + +PG_FUNCTION_INFO_V1(uuid_cast_bytea); +Datum uuid_cast_bytea(PG_FUNCTION_ARGS) { + bytea *bit; + + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + + bit = palloc(VARHDRSZ + 16); + SET_VARSIZE(bit, VARHDRSZ + 16); + memcpy(VARDATA(bit), uuid, 16); + PG_RETURN_BYTEA_P(bit); +} + +PG_FUNCTION_INFO_V1(uuid_cast_from_bytea); +Datum uuid_cast_from_bytea(PG_FUNCTION_ARGS) { + pg_uuid_t *uuid; + + bytea *bit = PG_GETARG_BYTEA_P(0); + + if (VARSIZE_ANY_EXHDR(bit) != 16) { + elog(ERROR, "invalid length of bytea for cast to uuid, need 16, attempted %d", VARSIZE_ANY_EXHDR(bit)); + } + + uuid = palloc(UUID_LEN_BIN); + memcpy(uuid, VARDATA(bit), 16); + PG_RETURN_UUID_P(uuid); +} + +/* TODO construct the uuid directly from the numeric */ +/* TODO have a static c256 numeric */ + +PG_FUNCTION_INFO_V1(uuid_cast_from_numeric); +Datum uuid_cast_from_numeric(PG_FUNCTION_ARGS) { + int i; + unsigned char *data; + int32 byte; + + Numeric c256; + Numeric input; + Numeric result; + pg_uuid_t *uuid; + + input = PG_GETARG_NUMERIC(0); + uuid = palloc(UUID_LEN_BIN); + data = (unsigned char *)uuid; + + c256 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(256))); + + for (i=15;i>=0;i--) { + result = DatumGetNumeric(DirectFunctionCall2(numeric_mod, NumericGetDatum(input), NumericGetDatum(c256))); + + input = DatumGetNumeric(DirectFunctionCall2(numeric_div_trunc, NumericGetDatum(input), NumericGetDatum(c256))); + + byte = DatumGetInt32(DirectFunctionCall1(numeric_int4, NumericGetDatum(result))); + data[i] = byte & 0xff; + } + + /* TODO will this leak if I don't do this? Segfault if i do? */ + pfree(c256); + + PG_RETURN_UUID_P(uuid); +} + +/* + * TODO construct the numeric directly + */ +PG_FUNCTION_INFO_V1(uuid_cast_numeric); +Datum uuid_cast_numeric(PG_FUNCTION_ARGS) { + int i; + unsigned char *data; + int32 byte; + + Numeric result; + Numeric c256; + + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + + data = (unsigned char *)uuid; + + result = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(0))); + + c256 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(256))); + + for (i=0;i<16;i++) { + byte = (int32) data[i]; + + result = DatumGetNumeric(DirectFunctionCall2(numeric_mul, NumericGetDatum(result), NumericGetDatum(c256))); + + result = DatumGetNumeric( + DirectFunctionCall2(numeric_add, + NumericGetDatum(result), + DirectFunctionCall1(int4_numeric, Int32GetDatum(byte)))); + + } + + pfree(c256); + + PG_RETURN_NUMERIC(result); +} + +PG_FUNCTION_INFO_V1(uuid_extract_version); +Datum uuid_extract_version(PG_FUNCTION_ARGS) { + int32 version; + unsigned char *data; + + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + data = (unsigned char *)uuid; + + version = data[6] >> 4; + PG_RETURN_INT32(version); +} + +PG_FUNCTION_INFO_V1(uuid_extract_macaddr); +Datum uuid_extract_macaddr(PG_FUNCTION_ARGS) { + macaddr *mac; + unsigned char *data; + + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + data = (unsigned char *)uuid; + + mac = palloc(sizeof *mac); + mac->a = data[10]; + mac->b = data[11]; + mac->c = data[12]; + mac->d = data[13]; + mac->e = data[14]; + mac->f = data[15]; + + PG_RETURN_MACADDR_P(mac); +} + +/* number of 100 nanosecond intervals from 1585-10-15 to 1970-01-01 */ +#define GREGORIAN 122192928000000000ULL + +PG_FUNCTION_INFO_V1(uuid_extract_timestamp); +Datum uuid_extract_timestamp(PG_FUNCTION_ARGS) { + Timestamp ts; + unsigned char *data; + + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + data = (unsigned char *)uuid; + + ts = (data[6] & 0xf); + ts *= 256; ts += (data[7] & 0xff); + ts *= 256; ts += (data[4] & 0xff); + ts *= 256; ts += (data[5] & 0xff); + ts *= 256; ts += (data[0] & 0xff); + ts *= 256; ts += (data[1] & 0xff); + ts *= 256; ts += (data[2] & 0xff); + ts *= 256; ts += (data[3] & 0xff); + + /* TODO could probably combine some of this */ + ts -= GREGORIAN; /* adjust by offset from gregorian to unix epoch */ + ts /= 10; /* convert from 100 ns to us */ + + ts -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * 1000000LL); + +#ifdef HAVE_INT64_TIMESTAMP + PG_RETURN_TIMESTAMP(ts); +#else + PG_RETURN_TIMESTAMP(ts/1000000UL); +#endif +} diff --git a/rng.c b/rng.c new file mode 100644 index 0000000..89eed89 --- /dev/null +++ b/rng.c @@ -0,0 +1,139 @@ +/* + * Adaped from libtomcrypt by Tom St Denis. + * + * by Nathan Wagner, and released into the public domain + */ + +#include + +#define LTC_ARGCHK(x) +#define TRY_URANDOM_FIRST 1 + +#ifndef WIN32 +#define LTC_DEVRANDOM 1 +#endif + +#ifdef LTC_DEVRANDOM +/* on *NIX read /dev/random */ +static unsigned long rng_nix(unsigned char *buf, unsigned long len) { +#ifdef LTC_NO_FILE + return 0; +#else + FILE *f; + unsigned long x; +#ifdef TRY_URANDOM_FIRST + f = fopen("/dev/urandom", "rb"); + if (f == NULL) +#endif + f = fopen("/dev/random", "rb"); + + if (f == NULL) { + return 0; + } + /* disable buffering */ + if (setvbuf(f, NULL, _IONBF, 0) != 0) { + fclose(f); + return 0; + } + x = (unsigned long) fread(buf, 1, (size_t) len, f); + fclose(f); + return x; +#endif +} + +#endif + +/* TODO remove this. It's too slow really (I think) */ +/* on ANSI C platforms with 100 < CLOCKS_PER_SEC < 10000 */ +#if defined(CLOCKS_PER_SEC) && !defined(WINCE) + +#define ANSI_RNG + +static unsigned long rng_ansic(unsigned char *buf, unsigned long len) { + clock_t t1; + int l, acc, bits, a, b; + + if (XCLOCKS_PER_SEC < 100 || XCLOCKS_PER_SEC > 10000) { + return 0; + } + l = len; + bits = 8; + acc = a = b = 0; + while (len--) { + while (bits--) { + do { + t1 = XCLOCK(); + while (t1 == XCLOCK()) + a ^= 1; + t1 = XCLOCK(); + while (t1 == XCLOCK()) + b ^= 1; + } while (a == b); + acc = (acc << 1) | a; + } + *buf++ = acc; + acc = 0; + bits = 8; + } + acc = bits = a = b = 0; + return l; +} + +#endif + +/* Try the Microsoft CSP */ +#if defined(WIN32) || defined(WINCE) +#warning using WIN32 +#define _WIN32_WINNT 0x0400 +#ifdef WINCE +#define UNDER_CE +#define ARM +#endif +#include +#include + +static unsigned long rng_win32(unsigned char *buf, unsigned long len) { + HCRYPTPROV hProv = 0; + + if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, + (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) && + !CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET)) + return 0; + + if (CryptGenRandom(hProv, len, buf) == TRUE) { + CryptReleaseContext(hProv, 0); + return len; + } else { + CryptReleaseContext(hProv, 0); + return 0; + } +} + +#endif + +unsigned long pd_uuid_rng_get_bytes(unsigned char *out, unsigned long outlen) { + unsigned long x; + + LTC_ARGCHK(out != NULL); + +#if defined(LTC_DEVRANDOM) + x = rng_nix(out, outlen); + if (x != 0) { + return x; + } +#endif +#ifdef WIN32 + x = rng_win32(out, outlen); + if (x != 0) { + return x; + } +#endif +#ifdef ANSI_RNG + x = rng_ansic(out, outlen); + if (x != 0) { + return x; + } +#endif + return 0; +} diff --git a/sha1.c b/sha1.c new file mode 100644 index 0000000..0e45979 --- /dev/null +++ b/sha1.c @@ -0,0 +1,216 @@ +/* + * Adapted from libtomcrypt by Tom St Denis + * + * by Nathan Wagner and released into the public domain + */ + +#define ENDIAN_NEUTRAL 1 +#define LTC_SMALL_CODE 1 + +#include "tomcrypt_macros.h" +#include "hash.h" + +#define F0(x,y,z) (z ^ (x & (y ^ z))) +#define F1(x,y,z) (x ^ y ^ z) +#define F2(x,y,z) ((x & y) | (z & (x | y))) +#define F3(x,y,z) (x ^ y ^ z) + +static int sha1_compress(hash_state *md, unsigned char *buf) +{ + ulong32 a,b,c,d,e,W[80],i; +#ifdef LTC_SMALL_CODE + ulong32 t; +#endif + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4*i)); + } + + /* copy state */ + a = md->sha1.state[0]; + b = md->sha1.state[1]; + c = md->sha1.state[2]; + d = md->sha1.state[3]; + e = md->sha1.state[4]; + + /* expand it */ + for (i = 16; i < 80; i++) { + W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); + } + + /* compress */ + /* round one */ + #define FF0(a,b,c,d,e,i) e = (ROL(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROL(b, 30); + #define FF1(a,b,c,d,e,i) e = (ROL(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROL(b, 30); + #define FF2(a,b,c,d,e,i) e = (ROL(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROL(b, 30); + #define FF3(a,b,c,d,e,i) e = (ROL(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROL(b, 30); + +#ifdef LTC_SMALL_CODE + + for (i = 0; i < 20; ) { + FF0(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; + } + + for (; i < 40; ) { + FF1(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; + } + + for (; i < 60; ) { + FF2(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; + } + + for (; i < 80; ) { + FF3(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; + } + +#else + + for (i = 0; i < 20; ) { + FF0(a,b,c,d,e,i++); + FF0(e,a,b,c,d,i++); + FF0(d,e,a,b,c,i++); + FF0(c,d,e,a,b,i++); + FF0(b,c,d,e,a,i++); + } + + /* round two */ + for (; i < 40; ) { + FF1(a,b,c,d,e,i++); + FF1(e,a,b,c,d,i++); + FF1(d,e,a,b,c,i++); + FF1(c,d,e,a,b,i++); + FF1(b,c,d,e,a,i++); + } + + /* round three */ + for (; i < 60; ) { + FF2(a,b,c,d,e,i++); + FF2(e,a,b,c,d,i++); + FF2(d,e,a,b,c,i++); + FF2(c,d,e,a,b,i++); + FF2(b,c,d,e,a,i++); + } + + /* round four */ + for (; i < 80; ) { + FF3(a,b,c,d,e,i++); + FF3(e,a,b,c,d,i++); + FF3(d,e,a,b,c,i++); + FF3(c,d,e,a,b,i++); + FF3(b,c,d,e,a,i++); + } +#endif + + #undef FF0 + #undef FF1 + #undef FF2 + #undef FF3 + + /* store */ + md->sha1.state[0] = md->sha1.state[0] + a; + md->sha1.state[1] = md->sha1.state[1] + b; + md->sha1.state[2] = md->sha1.state[2] + c; + md->sha1.state[3] = md->sha1.state[3] + d; + md->sha1.state[4] = md->sha1.state[4] + e; + + return CRYPT_OK; +} + +int sha1_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + md->sha1.state[0] = 0x67452301UL; + md->sha1.state[1] = 0xefcdab89UL; + md->sha1.state[2] = 0x98badcfeUL; + md->sha1.state[3] = 0x10325476UL; + md->sha1.state[4] = 0xc3d2e1f0UL; + md->sha1.curlen = 0; + md->sha1.length = 0; + return CRYPT_OK; +} + +HASH_PROCESS(sha1_process, sha1_compress, sha1, 64) + +int sha1_done(hash_state * md, unsigned char *out) { + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha1.curlen >= sizeof(md->sha1.buf)) { + return CRYPT_INVALID_ARG; + } + + /* increase the length of the message */ + md->sha1.length += md->sha1.curlen * 8; + + /* append the '1' bit */ + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha1.curlen > 56) { + while (md->sha1.curlen < 64) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + sha1_compress(md, md->sha1.buf); + md->sha1.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->sha1.curlen < 56) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha1.length, md->sha1.buf+56); + sha1_compress(md, md->sha1.buf); + + /* copy output */ + for (i = 0; i < 5; i++) { + STORE32H(md->sha1.state[i], out+(4*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +int sha1_test(void) { + #if 0 + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[20]; + } tests[] = { + { "abc", + { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, + 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, + 0x9c, 0xd0, 0xd8, 0x9d } + }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, + 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, + 0xE5, 0x46, 0x70, 0xF1 } + } + }; + + int i; + unsigned char tmp[20]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha1_init(&md); + sha1_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha1_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 20) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} diff --git a/tomcrypt_macros.h b/tomcrypt_macros.h new file mode 100644 index 0000000..8eb4b42 --- /dev/null +++ b/tomcrypt_macros.h @@ -0,0 +1,426 @@ +/* fix for MSVC ...evil! */ +#if 0 +#ifdef _MSC_VER + #define CONST64(n) n ## ui64 + typedef unsigned __int64 ulong64; +#else + #define CONST64(n) n ## ULL + typedef unsigned long long ulong64; +#endif + +/* this is the "32-bit at least" data type + * Re-define it to suit your platform but it must be at least 32-bits + */ +#if defined(__x86_64__) || (defined(__sparc__) && defined(__arch64__)) + typedef unsigned ulong32; +#else + typedef unsigned long ulong32; +#endif +#endif + +/* ---- HELPER MACROS ---- */ +#ifdef ENDIAN_NEUTRAL + +#define STORE32L(x, y) \ + { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD32L(x, y) \ + { x = ((unsigned long)((y)[3] & 255)<<24) | \ + ((unsigned long)((y)[2] & 255)<<16) | \ + ((unsigned long)((y)[1] & 255)<<8) | \ + ((unsigned long)((y)[0] & 255)); } + +#define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \ + (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \ + (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \ + (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \ + (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \ + (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ + (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255)<<24) | \ + ((unsigned long)((y)[1] & 255)<<16) | \ + ((unsigned long)((y)[2] & 255)<<8) | \ + ((unsigned long)((y)[3] & 255)); } + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \ + (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \ + (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \ + (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); } + +#endif /* ENDIAN_NEUTRAL */ + +#ifdef ENDIAN_LITTLE + +#if !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__)))) + +#define STORE32H(x, y) \ +asm __volatile__ ( \ + "bswapl %0 \n\t" \ + "movl %0,(%1)\n\t" \ + "bswapl %0 \n\t" \ + ::"r"(x), "r"(y)); + +#define LOAD32H(x, y) \ +asm __volatile__ ( \ + "movl (%1),%0\n\t" \ + "bswapl %0\n\t" \ + :"=r"(x): "r"(y)); + +#else + +#define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ + (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255)<<24) | \ + ((unsigned long)((y)[1] & 255)<<16) | \ + ((unsigned long)((y)[2] & 255)<<8) | \ + ((unsigned long)((y)[3] & 255)); } + +#endif + + +/* x86_64 processor */ +#if !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__)) + +#define STORE64H(x, y) \ +asm __volatile__ ( \ + "bswapq %0 \n\t" \ + "movq %0,(%1)\n\t" \ + "bswapq %0 \n\t" \ + ::"r"(x), "r"(y)); + +#define LOAD64H(x, y) \ +asm __volatile__ ( \ + "movq (%1),%0\n\t" \ + "bswapq %0\n\t" \ + :"=r"(x): "r"(y)); + +#else + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \ + (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \ + (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \ + (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); } + +#endif + +#ifdef ENDIAN_32BITWORD + +#define STORE32L(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32L(x, y) \ + XMEMCPY(&(x), y, 4); + +#define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \ + (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \ + (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \ + (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \ + (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \ + (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#else /* 64-bit words then */ + +#define STORE32L(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32L(x, y) \ + { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + +#define STORE64L(x, y) \ + { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + +#define LOAD64L(x, y) \ + { XMEMCPY(&(x), y, 8); } + +#endif /* ENDIAN_64BITWORD */ + +#endif /* ENDIAN_LITTLE */ + +#ifdef ENDIAN_BIG +#define STORE32L(x, y) \ + { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD32L(x, y) \ + { x = ((unsigned long)((y)[3] & 255)<<24) | \ + ((unsigned long)((y)[2] & 255)<<16) | \ + ((unsigned long)((y)[1] & 255)<<8) | \ + ((unsigned long)((y)[0] & 255)); } + +#define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255); \ + (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255); \ + (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255); \ + (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48) | \ + (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32) | \ + (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16) | \ + (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#ifdef ENDIAN_32BITWORD + +#define STORE32H(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32H(x, y) \ + XMEMCPY(&(x), y, 4); + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48)| \ + (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32)| \ + (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16)| \ + (((ulong64)((y)[6] & 255))<<8)| (((ulong64)((y)[7] & 255))); } + +#else /* 64-bit words then */ + +#define STORE32H(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32H(x, y) \ + { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + +#define STORE64H(x, y) \ + { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + +#define LOAD64H(x, y) \ + { XMEMCPY(&(x), y, 8); } + +#endif /* ENDIAN_64BITWORD */ +#endif /* ENDIAN_BIG */ + +#define BSWAP(x) ( ((x>>24)&0x000000FFUL) | ((x<<24)&0xFF000000UL) | \ + ((x>>8)&0x0000FF00UL) | ((x<<8)&0x00FF0000UL) ) + + +/* 32-bit Rotates */ +#if defined(_MSC_VER) + +/* instrinsic rotate */ +#include +#pragma intrinsic(_lrotr,_lrotl) +#define ROR(x,n) _lrotr(x,n) +#define ROL(x,n) _lrotl(x,n) +#define RORc(x,n) _lrotr(x,n) +#define ROLc(x,n) _lrotl(x,n) + +#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM) + +static inline unsigned ROL(unsigned word, int i) +{ + asm ("roll %%cl,%0" + :"=r" (word) + :"0" (word),"c" (i)); + return word; +} + +static inline unsigned ROR(unsigned word, int i) +{ + asm ("rorl %%cl,%0" + :"=r" (word) + :"0" (word),"c" (i)); + return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) +{ + asm ("roll %2,%0" + :"=r" (word) + :"0" (word),"I" (i)); + return word; +} + +static inline unsigned RORc(unsigned word, const int i) +{ + asm ("rorl %2,%0" + :"=r" (word) + :"0" (word),"I" (i)); + return word; +} + +#else + +#define ROLc ROL +#define RORc ROR + +#endif + +#elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32) + +static inline unsigned ROL(unsigned word, int i) +{ + asm ("rotlw %0,%0,%2" + :"=r" (word) + :"0" (word),"r" (i)); + return word; +} + +static inline unsigned ROR(unsigned word, int i) +{ + asm ("rotlw %0,%0,%2" + :"=r" (word) + :"0" (word),"r" (32-i)); + return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) +{ + asm ("rotlwi %0,%0,%2" + :"=r" (word) + :"0" (word),"I" (i)); + return word; +} + +static inline unsigned RORc(unsigned word, const int i) +{ + asm ("rotrwi %0,%0,%2" + :"=r" (word) + :"0" (word),"I" (i)); + return word; +} + +#else + +#define ROLc ROL +#define RORc ROR + +#endif + + +#else + +/* rotates the hard way */ +#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define ROR(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) + +#endif + + +/* 64-bit Rotates */ +#if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(LTC_NO_ASM) + +static inline unsigned long ROL64(unsigned long word, int i) +{ + asm("rolq %%cl,%0" + :"=r" (word) + :"0" (word),"c" (i)); + return word; +} + +static inline unsigned long ROR64(unsigned long word, int i) +{ + asm("rorq %%cl,%0" + :"=r" (word) + :"0" (word),"c" (i)); + return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned long ROL64c(unsigned long word, const int i) +{ + asm("rolq %2,%0" + :"=r" (word) + :"0" (word),"J" (i)); + return word; +} + +static inline unsigned long ROR64c(unsigned long word, const int i) +{ + asm("rorq %2,%0" + :"=r" (word) + :"0" (word),"J" (i)); + return word; +} + +#else /* LTC_NO_ROLC */ + +#define ROL64c ROL64 +#define ROR64c ROR64 + +#endif + +#else /* Not x86_64 */ + +#define ROL64(x, y) \ + ( (((x)<<((ulong64)(y)&63)) | \ + (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROR64(x, y) \ + ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \ + ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROL64c(x, y) \ + ( (((x)<<((ulong64)(y)&63)) | \ + (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROR64c(x, y) \ + ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \ + ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#endif + +#ifndef MAX + #define MAX(x, y) ( ((x)>(y))?(x):(y) ) +#endif + +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* extract a byte portably */ +#ifdef _MSC_VER + #define byte(x, n) ((unsigned char)((x) >> (8 * (n)))) +#else + #define byte(x, n) (((x) >> (8 * (n))) & 255) +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_macros.h,v $ */ +/* $Revision: 1.15 $ */ +/* $Date: 2006/11/29 23:43:57 $ */ diff --git a/uuidgen.c b/uuidgen.c new file mode 100644 index 0000000..ada26d0 --- /dev/null +++ b/uuidgen.c @@ -0,0 +1,67 @@ +/* + * test program for uuid library + * + * written by nathan wagner and placed in the public domain + */ +#include +#include +#include + +#include "pduuid.h" + +void ufmt(pd_uuid_t *uuid, char *s) { + int i; + for (i=0;i<4;i++) { + s += sprintf(s, "%02x", uuid->data[i]); + } + s += sprintf(s, "-"); + for (;i<6;i++) { + s += sprintf(s, "%02x", uuid->data[i]); + } + s += sprintf(s, "-"); + for (;i<8;i++) { + s += sprintf(s, "%02x", uuid->data[i]); + } + s += sprintf(s, "-"); + for (;i<10;i++) { + s += sprintf(s, "%02x", uuid->data[i]); + } + s += sprintf(s, "-"); + for (;i<16;i++) { + s += sprintf(s, "%02x", uuid->data[i]); + } +} + +int main(int ac, char *av[]) { + pd_uuid_t uuid; + pd_uuid_t dns; + char fmt[37]; + +#ifdef WIN32 + srand(time(0)); +#else + srandom(time(0)); +#endif + + pd_uuid_make_v1(0, &uuid); + ufmt(&uuid, fmt); + printf("v1: %s\n", fmt); + + pd_uuid_make_v4(0, &uuid); + ufmt(&uuid, fmt); + printf("v4: %s\n", fmt); + + pd_uuid_set_string(&dns, "6ba7b810-9dad-11d1-80b4-00c04fd430c8"); + ufmt(&dns, fmt); + printf("dns: %s\n", fmt); + + pd_uuid_make_v3(0, &uuid, &dns, "granicus.if.org", strlen("granicus.if.org")); + ufmt(&uuid, fmt); + printf("granicus.if.org (v3): %s\n", fmt); + + pd_uuid_make_v5(0, &uuid, &dns, "granicus.if.org", strlen("granicus.if.org")); + ufmt(&uuid, fmt); + printf("granicus.if.org (v5): %s\n", fmt); + + return 0; +} -- 2.40.0