Thread: [patch 0/2] Add Fortuna PRNG to pgcrypto
Following two patches add Fortuna PRNG to pgcrypto. I think this is needed to really complete the 'no hand-config' direction of pgcrypto. Patch #1 adds implementation of SHA2 hashes (SHA256/384/512). They are required for Fortuna. As they are replacements for SHA1 (which was replacement for MD5), they would be good to have anyway. Patch #2 adds implementation of Fortuna, plus reorganizes random functions a bit. random.c is made 'system randomness' provider, for initial seeding of PRNG. There I put 2 randomness sources for Windows, /dev/urandom reader, and a fallback provider, that does the getpid/gettimeofday stuff. The idea is to initially seed Fortuna with randomness from system and later feed SHA1 of user data into it too. Just to keep it from degenerating into pure PRNG. I looked at various PRNGs, and Fortuna seemed to best fit pgcrypto situation. Fortuna predecessor, from same authors, Yarrow - does not fit here as it needs precise accounting of entropy. PRNGs based on stream-ciphers also do not fit as they cannot handle feeding of dubious quality entropy. (And using just a PRNG without entropy feeding is not secure enough for session keys.) Fortuna does not need entropy accounting, in exchange it wastes entropy - keeps some of away from current output. ---------------------- I know, this is past the feature freeze, but in a way its not a new feature, rather is enables a already accepted (?) feature on a rather common configuration (no-OpenSSL). In particular, I am thinking about win32 port. Also, the goal for the PGP work was to replace the current encrypt() code, thus is needs also work everywhere encrypt() works. Without strong PRNG included, this goal is not filled. -- marko PS. If the inline patches bother, I can send them attached. Just that I use 'quilt' for preparing them, and it does not have option to send them as attachments.
- Add Fortuna PRNG to pgcrypto. - Move openssl random provider to openssl.c and builtin provider to internal.c - Make px_random_bytes use Fortuna, instead of giving error. - Retarget random.c to aquiring system randomness, for initial seeding of Fortuna. There is ATM 2 functions for Windows, reader from /dev/urandom and the regular time()/getpid() silliness. Index: pgsql/contrib/pgcrypto/fortuna.c =================================================================== *** /dev/null --- pgsql/contrib/pgcrypto/fortuna.c *************** *** 0 **** --- 1,365 ---- + /* + * fortuna.c + * Fortuna-like PRNG. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + + #include <postgres.h> + #include <sys/time.h> + #include <time.h> + + #include "rijndael.h" + #include "sha2.h" + + #include "fortuna.h" + + + /* + * Why Fortuna-like: There does not seem to be any definitive reference + * on Fortuna in the net. Instead this implementation is based on + * following references: + * + * http://en.wikipedia.org/wiki/Fortuna_(PRNG) + * - Wikipedia article + * http://jlcooke.ca/random/ + * - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux. + */ + + /* + * There is some confusion about whether and how to carry forward + * the state of the pools. Seems like original Fortuna does not + * do it, resetting hash after each request. I guess expecting + * feeding to happen more often that requesting. This is absolutely + * unsuitable for pgcrypto, as nothing asynchronous happens here. + * + * J.L. Cooke fixed this by feeding previous hash to new re-initialized + * hash context. + * + * Fortuna predecessor Yarrow requires ability to query intermediate + * 'final result' from hash, without affecting it. + * + * This implementation uses the Yarrow method - asking intermediate + * results, but continuing with old state. + */ + + + /* + * Algorithm parameters + */ + + /* + * How many pools. + * + * Original Fortuna uses 32 pools, that means 32'th pool is + * used not earlier than in 13th year. This is a waste in + * pgcrypto, as we have very low-frequancy seeding. Here + * is preferable to have all entropy usable in reasonable time. + * + * With 23 pools, 23th pool is used after 9 days which seems + * more sane. + * + * In our case the minimal cycle time would be bit longer + * than the system-randomness feeding frequency. + */ + #define NUM_POOLS 23 + + /* in microseconds */ + #define RESEED_INTERVAL 100000 /* 0.1 sec */ + + /* for one big request, reseed after this many bytes */ + #define RESEED_BYTES (1024*1024) + + + /* + * Algorithm constants + */ + + /* max sources */ + #define MAX_SOURCES 8 + + /* Both cipher key size and hash result size */ + #define BLOCK 32 + + /* cipher block size */ + #define CIPH_BLOCK 16 + + /* for internal wrappers */ + #define MD_CTX SHA256_CTX + #define CIPH_CTX rijndael_ctx + + struct fortuna_state { + uint8 counter[CIPH_BLOCK]; + uint8 result[CIPH_BLOCK]; + uint8 key[BLOCK]; + MD_CTX pool[NUM_POOLS]; + CIPH_CTX ciph; + unsigned source_pos[MAX_SOURCES]; + unsigned reseed_count; + struct timeval last_reseed_time; + }; + typedef struct fortuna_state FState; + + + /* + * Use our own wrappers here. + * - Need to get intermediate result from digest, without affecting it. + * - Need re-set key on a cipher context. + * - Algorithms are guaranteed to exist. + * - No memory allocations. + */ + + static void ciph_init(CIPH_CTX *ctx, const uint8 *key, int klen) + { + rijndael_set_key(ctx, (const uint32 *)key, klen, 1); + } + + static void ciph_encrypt(CIPH_CTX *ctx, const uint8 *in, uint8 *out) + { + rijndael_encrypt(ctx, (const uint32 *)in, (uint32 *)out); + } + + static void md_init(MD_CTX *ctx) + { + SHA256_Init(ctx); + } + + static void md_update(MD_CTX *ctx, const uint8 *data, int len) + { + SHA256_Update(ctx, data, len); + } + + static void md_result(MD_CTX *ctx, uint8 *dst) + { + SHA256_CTX tmp; + memcpy(&tmp, ctx, sizeof(*ctx)); + SHA256_Final(dst, &tmp); + memset(&tmp, 0, sizeof(tmp)); + } + + + /* + * initialize state + */ + static void init_state(FState *st) + { + int i; + memset(st, 0, sizeof(*st)); + for (i = 0; i < NUM_POOLS; i++) + md_init(&st->pool[i]); + } + + /* + * Must not reseed more ofter than RESEED_PER_SEC + * times per second. + */ + static int too_often(FState *st) + { + int ok; + struct timeval tv; + struct timeval *last = &st->last_reseed_time; + + gettimeofday(&tv, NULL); + + ok = 0; + if (tv.tv_sec != last->tv_sec) + ok = 1; + else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL) + ok = 1; + + memcpy(last, &tv, sizeof(tv)); + memset(&tv, 0, sizeof(tv)); + + return ok; + } + + /* + * generate new key from all the pools + */ + static void reseed(FState *st) + { + unsigned k; + unsigned n; + MD_CTX key_md; + uint8 buf[BLOCK]; + + /* check frequency */ + if (too_often(st)) + return; + + /* + * Both #0 and #1 reseed would use only pool 0. + * Just skip #0 then. + */ + n = ++st->reseed_count; + + /* + * The goal: use k-th pool only 1/(2^k) of the time. + */ + md_init(&key_md); + for (k = 0; k < NUM_POOLS; k++) { + md_result(&st->pool[k], buf); + md_update(&key_md, buf, BLOCK); + + if (n & 1 || !n) + break; + n >>= 1; + } + + /* add old key into mix too */ + md_update(&key_md, st->key, BLOCK); + + /* now we have new key */ + md_result(&key_md, st->key); + + /* use new key */ + ciph_init(&st->ciph, st->key, BLOCK); + + memset(&key_md, 0, sizeof(key_md)); + memset(buf, 0, BLOCK); + n = k = 0; + } + + /* + * update pools + */ + static void add_entropy(FState *st, unsigned src_id, const uint8 *data, unsigned len) + { + unsigned pos; + uint8 hash[BLOCK]; + MD_CTX md; + + /* just in case there's a bug somewhere */ + if (src_id >= MAX_SOURCES) + src_id = USER_ENTROPY; + + /* hash given data */ + md_init(&md); + md_update(&md, data, len); + md_result(&md, hash); + + /* update pools round-robin manner */ + pos = st->source_pos[src_id]; + md_update( &st->pool[pos], hash, BLOCK); + + if (++pos >= NUM_POOLS) + pos = 0; + st->source_pos[src_id] = pos; + + memset(hash, 0, BLOCK); + memset(&md, 0, sizeof(md)); + } + + /* + * Endianess does not matter. + * It just needs to change without repeating. + */ + static void inc_counter(FState *st) + { + uint32 *val = (uint32*)st->counter; + if (++val[0]) + return; + if (++val[1]) + return; + if (++val[2]) + return; + ++val[3]; + } + + static void extract_data(FState *st, unsigned count, uint8 *dst) + { + unsigned n; + unsigned block_nr = 0; + + /* + * Every request should be with different key, + * if possible. + */ + reseed(st); + + /* + * If the reseed didn't happen, don't use the old data + * rather encrypt again. + */ + + while (count > 0) { + /* must not give out too many bytes with one key */ + if (block_nr > (RESEED_BYTES / CIPH_BLOCK)) + { + reseed(st); + block_nr = 0; + } + + /* produce bytes */ + ciph_encrypt(&st->ciph, st->counter, st->result); + block_nr++; + + /* prepare for next time */ + inc_counter(st); + + /* copy result */ + if (count > CIPH_BLOCK) + n = CIPH_BLOCK; + else + n = count; + memcpy(dst, st->result, n); + dst += n; + count -= n; + } + } + + /* + * public interface + */ + + static FState main_state; + static int init_done = 0; + + void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len) + { + if (!init_done) + { + init_state(&main_state); + init_done = 1; + } + if (!data || !len) + return; + add_entropy(&main_state, src_id, data, len); + } + + void fortuna_get_bytes(unsigned len, uint8 *dst) + { + if (!init_done) + { + init_state(&main_state); + init_done = 1; + } + if (!dst || !len) + return; + extract_data(&main_state, len, dst); + } + Index: pgsql/contrib/pgcrypto/fortuna.h =================================================================== *** /dev/null --- pgsql/contrib/pgcrypto/fortuna.h *************** *** 0 **** --- 1,45 ---- + /* + * fortuna.c + * Fortuna PRNG. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + + #ifndef __FORTUNA_H + #define __FORTUNA_H + + /* + * Event source ID's + */ + #define SYSTEM_ENTROPY 0 + #define USER_ENTROPY 1 + + void fortuna_get_bytes(unsigned len, uint8 *dst); + void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len); + + #endif + Index: pgsql/contrib/pgcrypto/Makefile =================================================================== *** pgsql.orig/contrib/pgcrypto/Makefile --- pgsql/contrib/pgcrypto/Makefile *************** *** 2,25 **** # $PostgreSQL: pgsql/contrib/pgcrypto/Makefile,v 1.16 2005/07/05 23:18:44 tgl Exp $ # ! # if you don't have OpenSSL, you can use libc random() or /dev/urandom ! INT_CFLAGS = -DRAND_SILLY ! #INT_CFLAGS = -DRAND_DEV=\"/dev/urandom\" ! ! INT_SRCS = md5.c sha1.c sha2.c internal.c blf.c rijndael.c INT_TESTS = sha2 - OSSL_CFLAGS = -DRAND_OPENSSL OSSL_SRCS = openssl.c OSSL_TESTS = des 3des cast5 CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS)) CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS), $(INT_TESTS)) ! CF_CFLAGS = $(if $(subst no,,$(with_openssl)), $(OSSL_CFLAGS), $(INT_CFLAGS)) PG_CPPFLAGS = $(CF_CFLAGS) ! SRCS = pgcrypto.c px.c px-hmac.c px-crypt.c misc.c random.c \ crypt-gensalt.c crypt-blowfish.c crypt-des.c \ crypt-md5.c $(CF_SRCS) --- 2,21 ---- # $PostgreSQL: pgsql/contrib/pgcrypto/Makefile,v 1.16 2005/07/05 23:18:44 tgl Exp $ # ! INT_SRCS = md5.c sha1.c sha2.c internal.c blf.c rijndael.c \ ! fortuna.c random.c INT_TESTS = sha2 OSSL_SRCS = openssl.c OSSL_TESTS = des 3des cast5 CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS)) CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS), $(INT_TESTS)) ! CF_CFLAGS = PG_CPPFLAGS = $(CF_CFLAGS) ! SRCS = pgcrypto.c px.c px-hmac.c px-crypt.c misc.c \ crypt-gensalt.c crypt-blowfish.c crypt-des.c \ crypt-md5.c $(CF_SRCS) Index: pgsql/contrib/pgcrypto/internal.c =================================================================== *** pgsql.orig/contrib/pgcrypto/internal.c --- pgsql/contrib/pgcrypto/internal.c *************** *** 31,36 **** --- 31,37 ---- #include <postgres.h> + #include <time.h> #include "px.h" *************** *** 39,44 **** --- 40,52 ---- #include "sha2.h" #include "blf.h" #include "rijndael.h" + #include "fortuna.h" + + /* + * How often to try to acquire system entropy. (In seconds) + */ + #define SYSTEM_RESEED_FREQ (3*60*60) + #ifndef MD5_DIGEST_LENGTH #define MD5_DIGEST_LENGTH 16 *************** px_find_cipher(const char *name, PX_Ciph *** 784,786 **** --- 792,849 ---- *res = c; return 0; } + + /* + * Randomness provider + */ + + /* + * Use libc for all 'public' bytes. + * + * That way we don't expose bytes from Fortuna + * to the public, in case it has some bugs. + */ + int + px_get_pseudo_random_bytes(uint8 *dst, unsigned count) + { + int i; + + for (i = 0; i < count; i++) + *dst++ = random(); + return i; + } + + static time_t seed_time = 0; + static void system_reseed() + { + uint8 buf[1024]; + int n; + time_t t; + + t = time(NULL); + if (seed_time && (t - seed_time) < SYSTEM_RESEED_FREQ) + return; + + n = px_acquire_system_randomness(buf); + if (n > 0) + fortuna_add_entropy(SYSTEM_ENTROPY, buf, n); + + seed_time = t; + } + + int + px_get_random_bytes(uint8 *dst, unsigned count) + { + system_reseed(); + fortuna_get_bytes(count, dst); + return 0; + } + + int + px_add_entropy(const uint8 *data, unsigned count) + { + system_reseed(); + fortuna_add_entropy(USER_ENTROPY, data, count); + return 0; + } + Index: pgsql/contrib/pgcrypto/openssl.c =================================================================== *** pgsql.orig/contrib/pgcrypto/openssl.c --- pgsql/contrib/pgcrypto/openssl.c *************** *** 37,42 **** --- 37,45 ---- #include <openssl/blowfish.h> #include <openssl/cast.h> #include <openssl/des.h> + #include <openssl/rand.h> + #include <openssl/err.h> + /* * Does OpenSSL support AES? *************** px_find_cipher(const char *name, PX_Ciph *** 759,761 **** --- 762,819 ---- *res = c; return 0; } + + + static int openssl_random_init = 0; + + /* + * OpenSSL random should re-feeded occasionally. From /dev/urandom + * preferably. + */ + static void init_openssl_rand() + { + if (RAND_get_rand_method() == NULL) + RAND_set_rand_method(RAND_SSLeay()); + openssl_random_init = 1; + } + + int + px_get_random_bytes(uint8 *dst, unsigned count) + { + int res; + + if (!openssl_random_init) + init_openssl_rand(); + + res = RAND_bytes(dst, count); + if (res == 1) + return count; + + return PXE_OSSL_RAND_ERROR; + } + + int + px_get_pseudo_random_bytes(uint8 *dst, unsigned count) + { + int res; + + if (!openssl_random_init) + init_openssl_rand(); + + res = RAND_pseudo_bytes(dst, count); + if (res == 0 || res == 1) + return count; + + return PXE_OSSL_RAND_ERROR; + } + + int + px_add_entropy(const uint8 *data, unsigned count) + { + /* + * estimate 0 bits + */ + RAND_add(data, count, 0); + return 0; + } + Index: pgsql/contrib/pgcrypto/px.h =================================================================== *** pgsql.orig/contrib/pgcrypto/px.h --- pgsql/contrib/pgcrypto/px.h *************** int px_find_combo(const char *name, PX *** 170,175 **** --- 170,178 ---- int px_get_random_bytes(uint8 *dst, unsigned count); int px_get_pseudo_random_bytes(uint8 *dst, unsigned count); + int px_add_entropy(const uint8 *data, unsigned count); + + unsigned px_acquire_system_randomness(uint8 *dst); const char *px_strerror(int err); Index: pgsql/contrib/pgcrypto/random.c =================================================================== *** pgsql.orig/contrib/pgcrypto/random.c --- pgsql/contrib/pgcrypto/random.c *************** *** 1,6 **** /* * random.c ! * Random functions. * * Copyright (c) 2001 Marko Kreen * All rights reserved. --- 1,6 ---- /* * random.c ! * Acquire randomness from system. For seeding RNG. * * Copyright (c) 2001 Marko Kreen * All rights reserved. *************** *** 34,41 **** #include "px.h" ! #if defined(RAND_DEV) #include <errno.h> #include <fcntl.h> --- 34,53 ---- #include "px.h" + /* how many bytes to ask from system random provider */ + #define RND_BYTES 32 ! /* ! * Try to read from /dev/urandom or /dev/random on these OS'es. ! * ! * The list can be pretty liberal, as the device not existing ! * is expected event. ! */ ! #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ ! || defined(__NetBSD__) || defined(__DragonFly__) \ ! || defined(__darwin__) || defined(__SOLARIS__) ! ! #define TRY_DEV_RANDOM #include <errno.h> #include <fcntl.h> *************** safe_read(int fd, void *buf, size_t coun *** 64,157 **** return done; } ! int ! px_get_random_bytes(uint8 *dst, unsigned count) { int fd; int res; ! fd = open(RAND_DEV, O_RDONLY); if (fd == -1) ! return PXE_DEV_READ_ERROR; ! res = safe_read(fd, dst, count); close(fd); ! return res; } ! int ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) ! { ! return px_get_random_bytes(dst, count); ! } ! #elif defined(RAND_SILLY) ! int ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) { ! int i; ! for (i = 0; i < count; i++) ! *dst++ = random(); ! return i; } ! int ! px_get_random_bytes(uint8 *dst, unsigned count) { ! return PXE_NO_RANDOM; ! } ! #elif defined(RAND_OPENSSL) ! #include <openssl/evp.h> ! #include <openssl/blowfish.h> ! #include <openssl/rand.h> ! #include <openssl/err.h> - static int openssl_random_init = 0; /* ! * OpenSSL random should re-feeded occasionally. From /dev/urandom ! * preferably. */ ! static void init_openssl() ! { ! if (RAND_get_rand_method() == NULL) ! RAND_set_rand_method(RAND_SSLeay()); ! openssl_random_init = 1; ! } ! int ! px_get_random_bytes(uint8 *dst, unsigned count) ! { ! int res; ! ! if (!openssl_random_init) ! init_openssl(); ! ! res = RAND_bytes(dst, count); ! if (res == 1) ! return count; ! return PXE_OSSL_RAND_ERROR; ! } ! int ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) { ! int res; ! if (!openssl_random_init) ! init_openssl(); ! res = RAND_pseudo_bytes(dst, count); ! if (res == 0 || res == 1) ! return count; ! return PXE_OSSL_RAND_ERROR; } - #else - #error "Invalid random source" #endif --- 76,244 ---- return done; } ! static uint8 * ! try_dev_random(uint8 *dst) { int fd; int res; ! fd = open("/dev/urandom", O_RDONLY); if (fd == -1) ! { ! fd = open("/dev/random", O_RDONLY); ! if (fd == -1) ! return dst; ! } ! res = safe_read(fd, dst, RND_BYTES); close(fd); ! if (res > 0) ! dst += res; ! return dst; } ! #endif ! ! /* ! * Try to find randomness on Windows ! */ ! #ifdef WIN32 ! ! #define TRY_WIN32_GENRAND ! #define TRY_WIN32_PERFC ! #define _WIN32_WINNT 0x0400 ! #include <windows.h> ! #include <wincrypt.h> ! /* ! * this function is from libtomcrypt ! * ! * try to use Microsoft crypto API ! */ ! static uint8 * try_win32_genrand(uint8 *dst) { ! int res; ! HCRYPTPROV h = 0; ! ! res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL, ! (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)); ! if (!res) ! res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL, ! CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET); ! if (!res) ! return dst; ! ! res = CryptGenRandom(h, NUM_BYTES, dst); ! if (res == TRUE) ! dst += len; ! CryptReleaseContext(h, 0); ! return dst; } ! static uint8 * try_win32_perfc(uint8 *dst) { ! int res; ! LARGE_INTEGER time; ! res = QueryPerformanceCounter(&time); ! if (!res) ! return dst; ! memcpy(dst, &time, sizeof(time)); ! return dst + sizeof(time); ! } ! ! #endif /* WIN32 */ /* ! * If we are not on Windows, then hopefully we are ! * on a unix-like system. Use the usual suspects ! * for randomness. */ ! #ifndef WIN32 ! #define TRY_UNIXSTD ! #include <sys/types.h> ! #include <sys/time.h> ! #include <time.h> ! #include <unistd.h> ! /* ! * Everything here is predictible, only needs some patience. ! * ! * But there is a chance that the system-specific functions ! * did not work. So keep faith and try to slow the attacker down. ! */ ! static uint8 * ! try_unix_std(uint8 *dst) { ! pid_t pid; ! int x; ! PX_MD *md; ! struct timeval tv; ! int res; ! ! /* process id */ ! pid = getpid(); ! memcpy(dst, (uint8*)&pid, sizeof(pid)); ! dst += sizeof(pid); ! ! /* time */ ! gettimeofday(&tv, NULL); ! memcpy(dst, (uint8*)&tv, sizeof(tv)); ! dst += sizeof(tv); ! ! /* pointless, but should not hurt */ ! x = random(); ! memcpy(dst, (uint8*)&x, sizeof(x)); ! dst += sizeof(x); ! ! /* let's be desperate */ ! res = px_find_digest("sha1", &md); ! if (res >= 0) { ! uint8 *ptr; ! uint8 stack[8192]; ! int alloc = 32*1024; ! ! px_md_update(md, stack, sizeof(stack)); ! ptr = px_alloc(alloc); ! px_md_update(md, ptr, alloc); ! px_free(ptr); ! px_md_finish(md, dst); ! px_md_free(md); ! dst += 20; ! } ! return dst; } #endif + + /* + * try to extract some randomness for initial seeding + * + * dst should have room for 1024 bytes. + */ + unsigned px_acquire_system_randomness(uint8 *dst) + { + uint8 *p = dst; + #ifdef TRY_DEV_RANDOM + p = try_dev_random(p); + #endif + #ifdef TRY_WIN32_GENRAND + p = try_win32_genrand(p); + #endif + #ifdef TRY_WIN32_PERFC + p = try_win32_perfc(p); + #endif + #ifdef TRY_UNIXSTD + p = try_unix_std(p); + #endif + return p - dst; + } + --
This patch adds implementation of SHA2 to pgcrypto. New hashes: SHA256, SHA384, SHA512. Code from OpenBSD. Index: pgsql/contrib/pgcrypto/Makefile =================================================================== *** pgsql.orig/contrib/pgcrypto/Makefile --- pgsql/contrib/pgcrypto/Makefile *************** *** 6,19 **** INT_CFLAGS = -DRAND_SILLY #INT_CFLAGS = -DRAND_DEV=\"/dev/urandom\" ! INT_SRCS = md5.c sha1.c internal.c blf.c rijndael.c OSSL_CFLAGS = -DRAND_OPENSSL OSSL_SRCS = openssl.c OSSL_TESTS = des 3des cast5 CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS)) ! CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS)) CF_CFLAGS = $(if $(subst no,,$(with_openssl)), $(OSSL_CFLAGS), $(INT_CFLAGS)) PG_CPPFLAGS = $(CF_CFLAGS) --- 6,20 ---- INT_CFLAGS = -DRAND_SILLY #INT_CFLAGS = -DRAND_DEV=\"/dev/urandom\" ! INT_SRCS = md5.c sha1.c sha2.c internal.c blf.c rijndael.c ! INT_TESTS = sha2 OSSL_CFLAGS = -DRAND_OPENSSL OSSL_SRCS = openssl.c OSSL_TESTS = des 3des cast5 CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS)) ! CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS), $(INT_TESTS)) CF_CFLAGS = $(if $(subst no,,$(with_openssl)), $(OSSL_CFLAGS), $(INT_CFLAGS)) PG_CPPFLAGS = $(CF_CFLAGS) Index: pgsql/contrib/pgcrypto/internal.c =================================================================== *** pgsql.orig/contrib/pgcrypto/internal.c --- pgsql/contrib/pgcrypto/internal.c *************** *** 36,41 **** --- 36,42 ---- #include "md5.h" #include "sha1.h" + #include "sha2.h" #include "blf.h" #include "rijndael.h" *************** *** 56,61 **** --- 57,65 ---- static void init_md5(PX_MD * h); static void init_sha1(PX_MD * h); + static void init_sha256(PX_MD * h); + static void init_sha384(PX_MD * h); + static void init_sha512(PX_MD * h); struct int_digest { *************** static const struct int_digest *** 67,72 **** --- 71,79 ---- int_digest_list[] = { { "md5", init_md5 }, { "sha1", init_sha1 }, + { "sha256", init_sha256 }, + { "sha384", init_sha384 }, + { "sha512", init_sha512 }, { NULL, NULL } }; *************** int_sha1_free(PX_MD * h) *** 164,169 **** --- 171,316 ---- px_free(h); } + /* SHA256 */ + + static unsigned + int_sha256_len(PX_MD * h) + { + return SHA256_DIGEST_LENGTH; + } + + static unsigned + int_sha256_block_len(PX_MD * h) + { + return SHA256_BLOCK_LENGTH; + } + + static void + int_sha256_update(PX_MD * h, const uint8 *data, unsigned dlen) + { + SHA256_CTX *ctx = (SHA256_CTX *) h->p.ptr; + + SHA256_Update(ctx, data, dlen); + } + + static void + int_sha256_reset(PX_MD * h) + { + SHA256_CTX *ctx = (SHA256_CTX *) h->p.ptr; + + SHA256_Init(ctx); + } + + static void + int_sha256_finish(PX_MD * h, uint8 *dst) + { + SHA256_CTX *ctx = (SHA256_CTX *) h->p.ptr; + + SHA256_Final(dst, ctx); + } + + static void + int_sha256_free(PX_MD * h) + { + SHA256_CTX *ctx = (SHA256_CTX *) h->p.ptr; + + px_free(ctx); + px_free(h); + } + /* SHA384 */ + + static unsigned + int_sha384_len(PX_MD * h) + { + return SHA384_DIGEST_LENGTH; + } + + static unsigned + int_sha384_block_len(PX_MD * h) + { + return SHA384_BLOCK_LENGTH; + } + + static void + int_sha384_update(PX_MD * h, const uint8 *data, unsigned dlen) + { + SHA384_CTX *ctx = (SHA384_CTX *) h->p.ptr; + + SHA384_Update(ctx, data, dlen); + } + + static void + int_sha384_reset(PX_MD * h) + { + SHA384_CTX *ctx = (SHA384_CTX *) h->p.ptr; + + SHA384_Init(ctx); + } + + static void + int_sha384_finish(PX_MD * h, uint8 *dst) + { + SHA384_CTX *ctx = (SHA384_CTX *) h->p.ptr; + + SHA384_Final(dst, ctx); + } + + static void + int_sha384_free(PX_MD * h) + { + SHA384_CTX *ctx = (SHA384_CTX *) h->p.ptr; + + px_free(ctx); + px_free(h); + } + + /* SHA512 */ + + static unsigned + int_sha512_len(PX_MD * h) + { + return SHA512_DIGEST_LENGTH; + } + + static unsigned + int_sha512_block_len(PX_MD * h) + { + return SHA512_BLOCK_LENGTH; + } + + static void + int_sha512_update(PX_MD * h, const uint8 *data, unsigned dlen) + { + SHA512_CTX *ctx = (SHA512_CTX *) h->p.ptr; + + SHA512_Update(ctx, data, dlen); + } + + static void + int_sha512_reset(PX_MD * h) + { + SHA512_CTX *ctx = (SHA512_CTX *) h->p.ptr; + + SHA512_Init(ctx); + } + + static void + int_sha512_finish(PX_MD * h, uint8 *dst) + { + SHA512_CTX *ctx = (SHA512_CTX *) h->p.ptr; + + SHA512_Final(dst, ctx); + } + + static void + int_sha512_free(PX_MD * h) + { + SHA512_CTX *ctx = (SHA512_CTX *) h->p.ptr; + + px_free(ctx); + px_free(h); + } + /* init functions */ static void *************** init_sha1(PX_MD * md) *** 204,209 **** --- 351,413 ---- md->reset(md); } + static void + init_sha256(PX_MD * md) + { + SHA256_CTX *ctx; + + ctx = px_alloc(sizeof(*ctx)); + + md->p.ptr = ctx; + + md->result_size = int_sha256_len; + md->block_size = int_sha256_block_len; + md->reset = int_sha256_reset; + md->update = int_sha256_update; + md->finish = int_sha256_finish; + md->free = int_sha256_free; + + md->reset(md); + } + + static void + init_sha384(PX_MD * md) + { + SHA384_CTX *ctx; + + ctx = px_alloc(sizeof(*ctx)); + + md->p.ptr = ctx; + + md->result_size = int_sha384_len; + md->block_size = int_sha384_block_len; + md->reset = int_sha384_reset; + md->update = int_sha384_update; + md->finish = int_sha384_finish; + md->free = int_sha384_free; + + md->reset(md); + } + + static void + init_sha512(PX_MD * md) + { + SHA512_CTX *ctx; + + ctx = px_alloc(sizeof(*ctx)); + + md->p.ptr = ctx; + + md->result_size = int_sha512_len; + md->block_size = int_sha512_block_len; + md->reset = int_sha512_reset; + md->update = int_sha512_update; + md->finish = int_sha512_finish; + md->free = int_sha512_free; + + md->reset(md); + } + /* * ciphers generally */ Index: pgsql/contrib/pgcrypto/sha2.c =================================================================== *** /dev/null --- pgsql/contrib/pgcrypto/sha2.c *************** *** 0 **** --- 1,895 ---- + /* $PostgreSQL$ */ + /* $OpenBSD: sha2.c,v 1.6 2004/05/03 02:57:36 millert Exp $ */ + + /* + * FILE: sha2.c + * AUTHOR: Aaron D. Gifford <me@aarongifford.com> + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $ + */ + + #include <postgres.h> + + #include "sha2.h" + + #undef bcopy + #undef bzero + #define bcopy(src, dst, len) memcpy((dst), (src), (len)) + #define bzero(ptr, len) memset((ptr), 0, (len)) + + /* + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ + + + /*** SHA-256/384/512 Machine Architecture Definitions *****************/ + /* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivilent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including <sys/types.h> (which in turn includes + * <machine/endian.h> where the appropriate definitions are actually + * made). + */ + #if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) + #error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN + #endif + + + /*** SHA-256/384/512 Various Length Definitions ***********************/ + /* NOTE: Most of these are in sha2.h */ + #define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) + #define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) + #define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + + + /*** ENDIAN REVERSAL MACROS *******************************************/ + #if BYTE_ORDER == LITTLE_ENDIAN + #define REVERSE32(w,x) { \ + uint32 tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ + } + #define REVERSE64(w,x) { \ + uint64 tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ + ((tmp & 0x0000ffff0000ffffULL) << 16); \ + } + #endif /* BYTE_ORDER == LITTLE_ENDIAN */ + + /* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ + #define ADDINC128(w,n) { \ + (w)[0] += (uint64)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ + } + + /*** THE SIX LOGICAL FUNCTIONS ****************************************/ + /* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ + /* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ + #define R(b,x) ((x) >> (b)) + /* 32-bit Rotate-right (used in SHA-256): */ + #define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) + /* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ + #define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) + + /* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ + #define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) + #define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + + /* Four of six logical functions used in SHA-256: */ + #define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) + #define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) + #define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) + #define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + + /* Four of six logical functions used in SHA-384 and SHA-512: */ + #define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) + #define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) + #define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) + #define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) + + /*** INTERNAL FUNCTION PROTOTYPES *************************************/ + /* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ + void SHA512_Last(SHA512_CTX *); + void SHA256_Transform(SHA256_CTX *, const uint8 *); + void SHA512_Transform(SHA512_CTX *, const uint8 *); + + + /*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ + /* Hash constant words K for SHA-256: */ + const static uint32 K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL + }; + + /* Initial hash value H for SHA-256: */ + const static uint32 sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL + }; + + /* Hash constant words K for SHA-384 and SHA-512: */ + const static uint64 K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL + }; + + /* Initial hash value H for SHA-384 */ + const static uint64 sha384_initial_hash_value[8] = { + 0xcbbb9d5dc1059ed8ULL, + 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, + 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, + 0x47b5481dbefa4fa4ULL + }; + + /* Initial hash value H for SHA-512 */ + const static uint64 sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL + }; + + + /*** SHA-256: *********************************************************/ + void + SHA256_Init(SHA256_CTX *context) + { + if (context == NULL) + return; + bcopy(sha256_initial_hash_value, context->state, SHA256_DIGEST_LENGTH); + bzero(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = 0; + } + + #ifdef SHA2_UNROLL_TRANSFORM + + /* Unrolled SHA-256 round macros: */ + + #define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do { \ + W256[j] = (uint32)data[3] | ((uint32)data[2] << 8) | \ + ((uint32)data[1] << 16) | ((uint32)data[0] << 24); \ + data += 4; \ + T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ + j++; \ + } while(0) + + #define ROUND256(a,b,c,d,e,f,g,h) do { \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ + j++; \ + } while(0) + + void + SHA256_Transform(SHA256_CTX *context, const uint8 *data) + { + uint32 a, b, c, d, e, f, g, h, s0, s1; + uint32 T1, *W256; + int j; + + W256 = (uint32 *)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; + } + + #else /* SHA2_UNROLL_TRANSFORM */ + + void + SHA256_Transform(SHA256_CTX *context, const uint8 *data) + { + uint32 a, b, c, d, e, f, g, h, s0, s1; + uint32 T1, T2, *W256; + int j; + + W256 = (uint32 *)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + W256[j] = (uint32)data[3] | ((uint32)data[2] << 8) | + ((uint32)data[1] << 16) | ((uint32)data[0] << 24); + data += 4; + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; + } + + #endif /* SHA2_UNROLL_TRANSFORM */ + + void + SHA256_Update(SHA256_CTX *context, const uint8 *data, size_t len) + { + size_t freespace, usedspace; + + /* Calling with no data is valid (we do nothing) */ + if (len == 0) + return; + + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + bcopy(data, &context->buffer[usedspace], freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; + SHA256_Transform(context, context->buffer); + } else { + /* The buffer is not yet full */ + bcopy(data, &context->buffer[usedspace], len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA256_Transform(context, data); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + bcopy(data, context->buffer, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; + } + + void + SHA256_Final(uint8 digest[], SHA256_CTX *context) + { + uint32 *d = (uint32 *)digest; + unsigned int usedspace; + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != NULL) { + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + #if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount,context->bitcount); + #endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + bzero(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA256_BLOCK_LENGTH) { + bzero(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA256_Transform(context, context->buffer); + + /* And set-up for the last transform: */ + bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + } + } else { + /* Set-up for the last transform: */ + bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Set the bit count: */ + *(uint64 *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount; + + /* Final transform: */ + SHA256_Transform(context, context->buffer); + + #if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE32(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } + #else + bcopy(context->state, d, SHA256_DIGEST_LENGTH); + #endif + } + + /* Clean up state data: */ + bzero(context, sizeof(*context)); + usedspace = 0; + } + + + /*** SHA-512: *********************************************************/ + void + SHA512_Init(SHA512_CTX *context) + { + if (context == NULL) + return; + bcopy(sha512_initial_hash_value, context->state, SHA512_DIGEST_LENGTH); + bzero(context->buffer, SHA512_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; + } + + #ifdef SHA2_UNROLL_TRANSFORM + + /* Unrolled SHA-512 round macros: */ + + #define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do { \ + W512[j] = (uint64)data[7] | ((uint64)data[6] << 8) | \ + ((uint64)data[5] << 16) | ((uint64)data[4] << 24) | \ + ((uint64)data[3] << 32) | ((uint64)data[2] << 40) | \ + ((uint64)data[1] << 48) | ((uint64)data[0] << 56); \ + data += 8; \ + T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ + j++; \ + } while(0) + + + #define ROUND512(a,b,c,d,e,f,g,h) do { \ + s0 = W512[(j+1)&0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j+14)&0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ + j++; \ + } while(0) + + void + SHA512_Transform(SHA512_CTX *context, const uint8 *data) + { + uint64 a, b, c, d, e, f, g, h, s0, s1; + uint64 T1, *W512 = (uint64 *)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + ROUND512_0_TO_15(a,b,c,d,e,f,g,h); + ROUND512_0_TO_15(h,a,b,c,d,e,f,g); + ROUND512_0_TO_15(g,h,a,b,c,d,e,f); + ROUND512_0_TO_15(f,g,h,a,b,c,d,e); + ROUND512_0_TO_15(e,f,g,h,a,b,c,d); + ROUND512_0_TO_15(d,e,f,g,h,a,b,c); + ROUND512_0_TO_15(c,d,e,f,g,h,a,b); + ROUND512_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a,b,c,d,e,f,g,h); + ROUND512(h,a,b,c,d,e,f,g); + ROUND512(g,h,a,b,c,d,e,f); + ROUND512(f,g,h,a,b,c,d,e); + ROUND512(e,f,g,h,a,b,c,d); + ROUND512(d,e,f,g,h,a,b,c); + ROUND512(c,d,e,f,g,h,a,b); + ROUND512(b,c,d,e,f,g,h,a); + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; + } + + #else /* SHA2_UNROLL_TRANSFORM */ + + void + SHA512_Transform(SHA512_CTX *context, const uint8 *data) + { + uint64 a, b, c, d, e, f, g, h, s0, s1; + uint64 T1, T2, *W512 = (uint64 *)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + W512[j] = (uint64)data[7] | ((uint64)data[6] << 8) | + ((uint64)data[5] << 16) | ((uint64)data[4] << 24) | + ((uint64)data[3] << 32) | ((uint64)data[2] << 40) | + ((uint64)data[1] << 48) | ((uint64)data[0] << 56); + data += 8; + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j+1)&0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j+14)&0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; + } + + #endif /* SHA2_UNROLL_TRANSFORM */ + + void + SHA512_Update(SHA512_CTX *context, const uint8 *data, size_t len) + { + size_t freespace, usedspace; + + /* Calling with no data is valid (we do nothing) */ + if (len == 0) + return; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + bcopy(data, &context->buffer[usedspace], freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; + SHA512_Transform(context, context->buffer); + } else { + /* The buffer is not yet full */ + bcopy(data, &context->buffer[usedspace], len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA512_Transform(context, data); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + bcopy(data, context->buffer, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; + } + + void + SHA512_Last(SHA512_CTX *context) + { + unsigned int usedspace; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + #if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount[0],context->bitcount[0]); + REVERSE64(context->bitcount[1],context->bitcount[1]); + #endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + bzero(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA512_BLOCK_LENGTH) { + bzero(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA512_Transform(context, context->buffer); + + /* And set-up for the last transform: */ + bzero(context->buffer, SHA512_BLOCK_LENGTH - 2); + } + } else { + /* Prepare for final transform: */ + bzero(context->buffer, SHA512_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits): */ + *(uint64 *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; + *(uint64 *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0]; + + /* Final transform: */ + SHA512_Transform(context, context->buffer); + } + + void + SHA512_Final(uint8 digest[], SHA512_CTX *context) + { + uint64 *d = (uint64 *)digest; + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != NULL) { + SHA512_Last(context); + + /* Save the hash data for output: */ + #if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE64(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } + #else + bcopy(context->state, d, SHA512_DIGEST_LENGTH); + #endif + } + + /* Zero out state data */ + bzero(context, sizeof(*context)); + } + + + /*** SHA-384: *********************************************************/ + void + SHA384_Init(SHA384_CTX *context) + { + if (context == NULL) + return; + bcopy(sha384_initial_hash_value, context->state, SHA512_DIGEST_LENGTH); + bzero(context->buffer, SHA384_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; + } + + void + SHA384_Update(SHA384_CTX *context, const uint8 *data, size_t len) + { + SHA512_Update((SHA512_CTX *)context, data, len); + } + + void + SHA384_Final(uint8 digest[], SHA384_CTX *context) + { + uint64 *d = (uint64 *)digest; + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != NULL) { + SHA512_Last((SHA512_CTX *)context); + + /* Save the hash data for output: */ + #if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 6; j++) { + REVERSE64(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } + #else + bcopy(context->state, d, SHA384_DIGEST_LENGTH); + #endif + } + + /* Zero out state data */ + bzero(context, sizeof(*context)); + } Index: pgsql/contrib/pgcrypto/sha2.h =================================================================== *** /dev/null --- pgsql/contrib/pgcrypto/sha2.h *************** *** 0 **** --- 1,80 ---- + /* $PostgreSQL$ */ + /* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */ + + /* + * FILE: sha2.h + * AUTHOR: Aaron D. Gifford <me@aarongifford.com> + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ + */ + + #ifndef _SHA2_H + #define _SHA2_H + + + /*** SHA-256/384/512 Various Length Definitions ***********************/ + #define SHA256_BLOCK_LENGTH 64 + #define SHA256_DIGEST_LENGTH 32 + #define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) + #define SHA384_BLOCK_LENGTH 128 + #define SHA384_DIGEST_LENGTH 48 + #define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) + #define SHA512_BLOCK_LENGTH 128 + #define SHA512_DIGEST_LENGTH 64 + #define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) + + + /*** SHA-256/384/512 Context Structures *******************************/ + typedef struct _SHA256_CTX { + uint32 state[8]; + uint64 bitcount; + uint8 buffer[SHA256_BLOCK_LENGTH]; + } SHA256_CTX; + typedef struct _SHA512_CTX { + uint64 state[8]; + uint64 bitcount[2]; + uint8 buffer[SHA512_BLOCK_LENGTH]; + } SHA512_CTX; + + typedef SHA512_CTX SHA384_CTX; + + void SHA256_Init(SHA256_CTX *); + void SHA256_Update(SHA256_CTX *, const uint8 *, size_t); + void SHA256_Final(uint8[SHA256_DIGEST_LENGTH], SHA256_CTX *); + + void SHA384_Init(SHA384_CTX *); + void SHA384_Update(SHA384_CTX *, const uint8 *, size_t); + void SHA384_Final(uint8[SHA384_DIGEST_LENGTH], SHA384_CTX *); + + void SHA512_Init(SHA512_CTX *); + void SHA512_Update(SHA512_CTX *, const uint8 *, size_t); + void SHA512_Final(uint8[SHA512_DIGEST_LENGTH], SHA512_CTX *); + + #endif /* _SHA2_H */ Index: pgsql/contrib/pgcrypto/expected/sha2.out =================================================================== *** /dev/null --- pgsql/contrib/pgcrypto/expected/sha2.out *************** *** 0 **** --- 1,108 ---- + -- + -- SHA2 family + -- + -- SHA256 + SELECT encode(digest('', 'sha256'), 'hex'); + encode + ------------------------------------------------------------------ + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 + (1 row) + + SELECT encode(digest('a', 'sha256'), 'hex'); + encode + ------------------------------------------------------------------ + ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb + (1 row) + + SELECT encode(digest('abc', 'sha256'), 'hex'); + encode + ------------------------------------------------------------------ + ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad + (1 row) + + SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha256'), 'hex'); + encode + ------------------------------------------------------------------ + 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1 + (1 row) + + SELECT encode(digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha256'), 'hex'); + encode + ------------------------------------------------------------------ + f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e + (1 row) + + -- SHA384 + SELECT encode(digest('', 'sha384'), 'hex'); + encode + -------------------------------------------------------------------------------------------------- + 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b + (1 row) + + SELECT encode(digest('a', 'sha384'), 'hex'); + encode + -------------------------------------------------------------------------------------------------- + 54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31 + (1 row) + + SELECT encode(digest('abc', 'sha384'), 'hex'); + encode + -------------------------------------------------------------------------------------------------- + cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7 + (1 row) + + SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha384'), 'hex'); + encode + -------------------------------------------------------------------------------------------------- + 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b + (1 row) + + SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha384'),'hex'); + encode + -------------------------------------------------------------------------------------------------- + 09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039 + (1 row) + + SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha384'),'hex'); + encode + -------------------------------------------------------------------------------------------------- + 3d208973ab3508dbbd7e2c2862ba290ad3010e4978c198dc4d8fd014e582823a89e16f9b2a7bbc1ac938e2d199e8bea4 + (1 row) + + -- SHA512 + SELECT encode(digest('', 'sha512'), 'hex'); + encode + ---------------------------------------------------------------------------------------------------------------------------------- + cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e + (1 row) + + SELECT encode(digest('a', 'sha512'), 'hex'); + encode + ---------------------------------------------------------------------------------------------------------------------------------- + 1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75 + (1 row) + + SELECT encode(digest('abc', 'sha512'), 'hex'); + encode + ---------------------------------------------------------------------------------------------------------------------------------- + ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f + (1 row) + + SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha512'), 'hex'); + encode + ---------------------------------------------------------------------------------------------------------------------------------- + 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445 + (1 row) + + SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha512'),'hex'); + encode + ---------------------------------------------------------------------------------------------------------------------------------- + 8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909 + (1 row) + + SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha512'),'hex'); + encode + ---------------------------------------------------------------------------------------------------------------------------------- + 930d0cefcb30ff1133b6898121f1cf3d27578afcafe8677c5257cf069911f75d8f5831b56ebfda67b278e66dff8b84fe2b2870f742a580d8edb41987232850c9 + (1 row) + Index: pgsql/contrib/pgcrypto/sql/sha2.sql =================================================================== *** /dev/null --- pgsql/contrib/pgcrypto/sql/sha2.sql *************** *** 0 **** --- 1,28 ---- + -- + -- SHA2 family + -- + + -- SHA256 + SELECT encode(digest('', 'sha256'), 'hex'); + SELECT encode(digest('a', 'sha256'), 'hex'); + SELECT encode(digest('abc', 'sha256'), 'hex'); + SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha256'), 'hex'); + SELECT encode(digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha256'), 'hex'); + + -- SHA384 + SELECT encode(digest('', 'sha384'), 'hex'); + SELECT encode(digest('a', 'sha384'), 'hex'); + SELECT encode(digest('abc', 'sha384'), 'hex'); + SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha384'), 'hex'); + SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha384'),'hex'); + SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha384'),'hex'); + + -- SHA512 + SELECT encode(digest('', 'sha512'), 'hex'); + SELECT encode(digest('a', 'sha512'), 'hex'); + SELECT encode(digest('abc', 'sha512'), 'hex'); + SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha512'), 'hex'); + SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha512'),'hex'); + SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha512'),'hex'); + + --
On Fri, Jul 08, 2005 at 20:54:40 +0300, Marko Kreen <marko@l-t.ee> wrote: > > The idea is to initially seed Fortuna with randomness from > system and later feed SHA1 of user data into it too. Just > to keep it from degenerating into pure PRNG. How is fortuna getting entropy? Wouldn't this be better placed in the OS as the source of /dev/urandom rather than as part of Postgres?
On Fri, Jul 08, 2005 at 02:10:47PM -0500, Bruno Wolff III wrote: > On Fri, Jul 08, 2005 at 20:54:40 +0300, > Marko Kreen <marko@l-t.ee> wrote: > > > > The idea is to initially seed Fortuna with randomness from > > system and later feed SHA1 of user data into it too. Just > > to keep it from degenerating into pure PRNG. > > How is fortuna getting entropy? - From /dev/urandom or /dev/random on Linux, *BSD, Darwin and Solaris. - On Windows it uses CryptGenRandom function from Microsoft CryptoAPI and additionally a high-resolution timer output. (I must admit I haven't even compile tested them, I hope that win32 guys will look into it.) - On other OS'es, or in case reading /dev/random fails, the regular silly random functions: getpid, gettimeofday, etc. The system randomness is aquired on first call, and then after every 3h or so. In addition SHA1 of user data is feeded into it just to keep things moving. Actually, this is the preferred usage for /dev/random - to seed a strong PRNG, then use that. Unless you are generating secret keys, that is. But we aren't. Basically, the hardware entropy the kernel has aquired is precious, and the user should not waste it. This leaves OS'es that don't have /dev/random and aren't Windows out cold, but I really can't do anything for them. With the mixing of user data, the random output should not immidiately fail for them too. Ofcourse, if anyone knows any system-specific entropy gathering tricks, please tell. > Wouldn't this be better placed in the OS as the source of /dev/urandom rather > than as part of Postgres? Well, those OS'es that already have urandom/random, don't need it. And those that don't - I really don't feel responsibility to write one... -- marko
On Fri, Jul 08, 2005 at 23:03:49 +0300, Marko Kreen <marko@l-t.ee> wrote: > > Well, those OS'es that already have urandom/random, don't need > it. And those that don't - I really don't feel responsibility > to write one... But fortuna is essentially a high quality /dev/urandom. It doesn't make seem to much sense to implement it on top on /dev/urandom.
On Fri, Jul 08, 2005 at 03:12:59PM -0500, Bruno Wolff III wrote: > On Fri, Jul 08, 2005 at 23:03:49 +0300, > Marko Kreen <marko@l-t.ee> wrote: > > > > Well, those OS'es that already have urandom/random, don't need > > it. And those that don't - I really don't feel responsibility > > to write one... > > But fortuna is essentially a high quality /dev/urandom. It doesn't make seem to > much sense to implement it on top on /dev/urandom. Ah, the chain /dev/urandom -> Fortuna -> result irritates you? The main reason for Fortuna is to cover OS'es that don't have /dev/random. Currently only Win32 is adequately covered as it provides it's own hardware random for seeding. On others the output is theoretically weak, but the silly randomness plus hashing user data (which is supposedly secret) and key (ditto) should provide 'Good Enough for 99% cases' randomness. Now - for /dev/random OS'es: ofcourse, if the /dev/urandom works, I could skip all of the Fortuna and use that directly, but I don't see the need for that. I even see disadvantage in doing that. First, I prefer /dev/urandom to /dev/random (in Linux terms). The /dev/random advantade is too theoretical for me - not worth the practical disadvantage. But following applies to it too. While there is hardware-backed entropy available the urandom gives true randomness - no way to predict it. If the entropy is drained, it degenerates into PRNG (although very good one). The danger is, that if it runs as PRNG, some resourceful 'interested party' can read some of the output and then predict what the next user will get or what some other user got before. So it is pretty important not to drain all entropy from it. /dev/random avoids this with blocking, but IMHO urandom has state large enough to allow running as a PRNG some time. Now consider following scenario: you have 30 clients connected to the server, each encryption something occasionally. The current reseed delay is 3h - the result is that on average, after every 6 seconds someone asks 256 bits from /dev/urandom. This pretty much guarantees that entropy will be drained - especially on an unattanded server. The situation would be much worse if they all read directly from /dev/urandom. And here's the key difference - if they read directly from /dev/urandom they all have PRNG with same state. If someone observes enough of the output from there to predict what others get, he can. But when using Fortuna inside pgcrypto: - Each user (connection) will have different state. - There is bigger chance that seeding from /dev/urandom has true entropy in it. So even if someone predicts one state, it has no effect on other users. Ok, this is not exatly true when using pooled connections - attacker could predict what the previous user got or what next one will, but the fact that other states are not compromised still stays. OK, all this is rather theorethical. Practically speaking, if we need to have Fortuna anyway, we may as well be better OS citizens by using it on /dev/urandom too. -- marko
Patch applied. Thanks. --------------------------------------------------------------------------- Marko Kreen wrote: > This patch adds implementation of SHA2 to pgcrypto. > New hashes: SHA256, SHA384, SHA512. > > Code from OpenBSD. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
Patch applied. Thanks. --------------------------------------------------------------------------- Marko Kreen wrote: > - Add Fortuna PRNG to pgcrypto. > - Move openssl random provider to openssl.c and builtin provider > to internal.c > - Make px_random_bytes use Fortuna, instead of giving error. > - Retarget random.c to aquiring system randomness, for initial seeding > of Fortuna. There is ATM 2 functions for Windows, > reader from /dev/urandom and the regular time()/getpid() silliness. > > > Index: pgsql/contrib/pgcrypto/fortuna.c > =================================================================== > *** /dev/null > --- pgsql/contrib/pgcrypto/fortuna.c > *************** > *** 0 **** > --- 1,365 ---- > + /* > + * fortuna.c > + * Fortuna-like PRNG. > + * > + * Copyright (c) 2005 Marko Kreen > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + * > + * $PostgreSQL$ > + */ > + > + #include <postgres.h> > + #include <sys/time.h> > + #include <time.h> > + > + #include "rijndael.h" > + #include "sha2.h" > + > + #include "fortuna.h" > + > + > + /* > + * Why Fortuna-like: There does not seem to be any definitive reference > + * on Fortuna in the net. Instead this implementation is based on > + * following references: > + * > + * http://en.wikipedia.org/wiki/Fortuna_(PRNG) > + * - Wikipedia article > + * http://jlcooke.ca/random/ > + * - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux. > + */ > + > + /* > + * There is some confusion about whether and how to carry forward > + * the state of the pools. Seems like original Fortuna does not > + * do it, resetting hash after each request. I guess expecting > + * feeding to happen more often that requesting. This is absolutely > + * unsuitable for pgcrypto, as nothing asynchronous happens here. > + * > + * J.L. Cooke fixed this by feeding previous hash to new re-initialized > + * hash context. > + * > + * Fortuna predecessor Yarrow requires ability to query intermediate > + * 'final result' from hash, without affecting it. > + * > + * This implementation uses the Yarrow method - asking intermediate > + * results, but continuing with old state. > + */ > + > + > + /* > + * Algorithm parameters > + */ > + > + /* > + * How many pools. > + * > + * Original Fortuna uses 32 pools, that means 32'th pool is > + * used not earlier than in 13th year. This is a waste in > + * pgcrypto, as we have very low-frequancy seeding. Here > + * is preferable to have all entropy usable in reasonable time. > + * > + * With 23 pools, 23th pool is used after 9 days which seems > + * more sane. > + * > + * In our case the minimal cycle time would be bit longer > + * than the system-randomness feeding frequency. > + */ > + #define NUM_POOLS 23 > + > + /* in microseconds */ > + #define RESEED_INTERVAL 100000 /* 0.1 sec */ > + > + /* for one big request, reseed after this many bytes */ > + #define RESEED_BYTES (1024*1024) > + > + > + /* > + * Algorithm constants > + */ > + > + /* max sources */ > + #define MAX_SOURCES 8 > + > + /* Both cipher key size and hash result size */ > + #define BLOCK 32 > + > + /* cipher block size */ > + #define CIPH_BLOCK 16 > + > + /* for internal wrappers */ > + #define MD_CTX SHA256_CTX > + #define CIPH_CTX rijndael_ctx > + > + struct fortuna_state { > + uint8 counter[CIPH_BLOCK]; > + uint8 result[CIPH_BLOCK]; > + uint8 key[BLOCK]; > + MD_CTX pool[NUM_POOLS]; > + CIPH_CTX ciph; > + unsigned source_pos[MAX_SOURCES]; > + unsigned reseed_count; > + struct timeval last_reseed_time; > + }; > + typedef struct fortuna_state FState; > + > + > + /* > + * Use our own wrappers here. > + * - Need to get intermediate result from digest, without affecting it. > + * - Need re-set key on a cipher context. > + * - Algorithms are guaranteed to exist. > + * - No memory allocations. > + */ > + > + static void ciph_init(CIPH_CTX *ctx, const uint8 *key, int klen) > + { > + rijndael_set_key(ctx, (const uint32 *)key, klen, 1); > + } > + > + static void ciph_encrypt(CIPH_CTX *ctx, const uint8 *in, uint8 *out) > + { > + rijndael_encrypt(ctx, (const uint32 *)in, (uint32 *)out); > + } > + > + static void md_init(MD_CTX *ctx) > + { > + SHA256_Init(ctx); > + } > + > + static void md_update(MD_CTX *ctx, const uint8 *data, int len) > + { > + SHA256_Update(ctx, data, len); > + } > + > + static void md_result(MD_CTX *ctx, uint8 *dst) > + { > + SHA256_CTX tmp; > + memcpy(&tmp, ctx, sizeof(*ctx)); > + SHA256_Final(dst, &tmp); > + memset(&tmp, 0, sizeof(tmp)); > + } > + > + > + /* > + * initialize state > + */ > + static void init_state(FState *st) > + { > + int i; > + memset(st, 0, sizeof(*st)); > + for (i = 0; i < NUM_POOLS; i++) > + md_init(&st->pool[i]); > + } > + > + /* > + * Must not reseed more ofter than RESEED_PER_SEC > + * times per second. > + */ > + static int too_often(FState *st) > + { > + int ok; > + struct timeval tv; > + struct timeval *last = &st->last_reseed_time; > + > + gettimeofday(&tv, NULL); > + > + ok = 0; > + if (tv.tv_sec != last->tv_sec) > + ok = 1; > + else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL) > + ok = 1; > + > + memcpy(last, &tv, sizeof(tv)); > + memset(&tv, 0, sizeof(tv)); > + > + return ok; > + } > + > + /* > + * generate new key from all the pools > + */ > + static void reseed(FState *st) > + { > + unsigned k; > + unsigned n; > + MD_CTX key_md; > + uint8 buf[BLOCK]; > + > + /* check frequency */ > + if (too_often(st)) > + return; > + > + /* > + * Both #0 and #1 reseed would use only pool 0. > + * Just skip #0 then. > + */ > + n = ++st->reseed_count; > + > + /* > + * The goal: use k-th pool only 1/(2^k) of the time. > + */ > + md_init(&key_md); > + for (k = 0; k < NUM_POOLS; k++) { > + md_result(&st->pool[k], buf); > + md_update(&key_md, buf, BLOCK); > + > + if (n & 1 || !n) > + break; > + n >>= 1; > + } > + > + /* add old key into mix too */ > + md_update(&key_md, st->key, BLOCK); > + > + /* now we have new key */ > + md_result(&key_md, st->key); > + > + /* use new key */ > + ciph_init(&st->ciph, st->key, BLOCK); > + > + memset(&key_md, 0, sizeof(key_md)); > + memset(buf, 0, BLOCK); > + n = k = 0; > + } > + > + /* > + * update pools > + */ > + static void add_entropy(FState *st, unsigned src_id, const uint8 *data, unsigned len) > + { > + unsigned pos; > + uint8 hash[BLOCK]; > + MD_CTX md; > + > + /* just in case there's a bug somewhere */ > + if (src_id >= MAX_SOURCES) > + src_id = USER_ENTROPY; > + > + /* hash given data */ > + md_init(&md); > + md_update(&md, data, len); > + md_result(&md, hash); > + > + /* update pools round-robin manner */ > + pos = st->source_pos[src_id]; > + md_update( &st->pool[pos], hash, BLOCK); > + > + if (++pos >= NUM_POOLS) > + pos = 0; > + st->source_pos[src_id] = pos; > + > + memset(hash, 0, BLOCK); > + memset(&md, 0, sizeof(md)); > + } > + > + /* > + * Endianess does not matter. > + * It just needs to change without repeating. > + */ > + static void inc_counter(FState *st) > + { > + uint32 *val = (uint32*)st->counter; > + if (++val[0]) > + return; > + if (++val[1]) > + return; > + if (++val[2]) > + return; > + ++val[3]; > + } > + > + static void extract_data(FState *st, unsigned count, uint8 *dst) > + { > + unsigned n; > + unsigned block_nr = 0; > + > + /* > + * Every request should be with different key, > + * if possible. > + */ > + reseed(st); > + > + /* > + * If the reseed didn't happen, don't use the old data > + * rather encrypt again. > + */ > + > + while (count > 0) { > + /* must not give out too many bytes with one key */ > + if (block_nr > (RESEED_BYTES / CIPH_BLOCK)) > + { > + reseed(st); > + block_nr = 0; > + } > + > + /* produce bytes */ > + ciph_encrypt(&st->ciph, st->counter, st->result); > + block_nr++; > + > + /* prepare for next time */ > + inc_counter(st); > + > + /* copy result */ > + if (count > CIPH_BLOCK) > + n = CIPH_BLOCK; > + else > + n = count; > + memcpy(dst, st->result, n); > + dst += n; > + count -= n; > + } > + } > + > + /* > + * public interface > + */ > + > + static FState main_state; > + static int init_done = 0; > + > + void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len) > + { > + if (!init_done) > + { > + init_state(&main_state); > + init_done = 1; > + } > + if (!data || !len) > + return; > + add_entropy(&main_state, src_id, data, len); > + } > + > + void fortuna_get_bytes(unsigned len, uint8 *dst) > + { > + if (!init_done) > + { > + init_state(&main_state); > + init_done = 1; > + } > + if (!dst || !len) > + return; > + extract_data(&main_state, len, dst); > + } > + > Index: pgsql/contrib/pgcrypto/fortuna.h > =================================================================== > *** /dev/null > --- pgsql/contrib/pgcrypto/fortuna.h > *************** > *** 0 **** > --- 1,45 ---- > + /* > + * fortuna.c > + * Fortuna PRNG. > + * > + * Copyright (c) 2005 Marko Kreen > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + * > + * $PostgreSQL$ > + */ > + > + #ifndef __FORTUNA_H > + #define __FORTUNA_H > + > + /* > + * Event source ID's > + */ > + #define SYSTEM_ENTROPY 0 > + #define USER_ENTROPY 1 > + > + void fortuna_get_bytes(unsigned len, uint8 *dst); > + void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len); > + > + #endif > + > Index: pgsql/contrib/pgcrypto/Makefile > =================================================================== > *** pgsql.orig/contrib/pgcrypto/Makefile > --- pgsql/contrib/pgcrypto/Makefile > *************** > *** 2,25 **** > # $PostgreSQL: pgsql/contrib/pgcrypto/Makefile,v 1.16 2005/07/05 23:18:44 tgl Exp $ > # > > ! # if you don't have OpenSSL, you can use libc random() or /dev/urandom > ! INT_CFLAGS = -DRAND_SILLY > ! #INT_CFLAGS = -DRAND_DEV=\"/dev/urandom\" > ! > ! INT_SRCS = md5.c sha1.c sha2.c internal.c blf.c rijndael.c > INT_TESTS = sha2 > > - OSSL_CFLAGS = -DRAND_OPENSSL > OSSL_SRCS = openssl.c > OSSL_TESTS = des 3des cast5 > > CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS)) > CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS), $(INT_TESTS)) > ! CF_CFLAGS = $(if $(subst no,,$(with_openssl)), $(OSSL_CFLAGS), $(INT_CFLAGS)) > > PG_CPPFLAGS = $(CF_CFLAGS) > > ! SRCS = pgcrypto.c px.c px-hmac.c px-crypt.c misc.c random.c \ > crypt-gensalt.c crypt-blowfish.c crypt-des.c \ > crypt-md5.c $(CF_SRCS) > > --- 2,21 ---- > # $PostgreSQL: pgsql/contrib/pgcrypto/Makefile,v 1.16 2005/07/05 23:18:44 tgl Exp $ > # > > ! INT_SRCS = md5.c sha1.c sha2.c internal.c blf.c rijndael.c \ > ! fortuna.c random.c > INT_TESTS = sha2 > > OSSL_SRCS = openssl.c > OSSL_TESTS = des 3des cast5 > > CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS)) > CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS), $(INT_TESTS)) > ! CF_CFLAGS = > > PG_CPPFLAGS = $(CF_CFLAGS) > > ! SRCS = pgcrypto.c px.c px-hmac.c px-crypt.c misc.c \ > crypt-gensalt.c crypt-blowfish.c crypt-des.c \ > crypt-md5.c $(CF_SRCS) > > Index: pgsql/contrib/pgcrypto/internal.c > =================================================================== > *** pgsql.orig/contrib/pgcrypto/internal.c > --- pgsql/contrib/pgcrypto/internal.c > *************** > *** 31,36 **** > --- 31,37 ---- > > > #include <postgres.h> > + #include <time.h> > > #include "px.h" > > *************** > *** 39,44 **** > --- 40,52 ---- > #include "sha2.h" > #include "blf.h" > #include "rijndael.h" > + #include "fortuna.h" > + > + /* > + * How often to try to acquire system entropy. (In seconds) > + */ > + #define SYSTEM_RESEED_FREQ (3*60*60) > + > > #ifndef MD5_DIGEST_LENGTH > #define MD5_DIGEST_LENGTH 16 > *************** px_find_cipher(const char *name, PX_Ciph > *** 784,786 **** > --- 792,849 ---- > *res = c; > return 0; > } > + > + /* > + * Randomness provider > + */ > + > + /* > + * Use libc for all 'public' bytes. > + * > + * That way we don't expose bytes from Fortuna > + * to the public, in case it has some bugs. > + */ > + int > + px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > + { > + int i; > + > + for (i = 0; i < count; i++) > + *dst++ = random(); > + return i; > + } > + > + static time_t seed_time = 0; > + static void system_reseed() > + { > + uint8 buf[1024]; > + int n; > + time_t t; > + > + t = time(NULL); > + if (seed_time && (t - seed_time) < SYSTEM_RESEED_FREQ) > + return; > + > + n = px_acquire_system_randomness(buf); > + if (n > 0) > + fortuna_add_entropy(SYSTEM_ENTROPY, buf, n); > + > + seed_time = t; > + } > + > + int > + px_get_random_bytes(uint8 *dst, unsigned count) > + { > + system_reseed(); > + fortuna_get_bytes(count, dst); > + return 0; > + } > + > + int > + px_add_entropy(const uint8 *data, unsigned count) > + { > + system_reseed(); > + fortuna_add_entropy(USER_ENTROPY, data, count); > + return 0; > + } > + > Index: pgsql/contrib/pgcrypto/openssl.c > =================================================================== > *** pgsql.orig/contrib/pgcrypto/openssl.c > --- pgsql/contrib/pgcrypto/openssl.c > *************** > *** 37,42 **** > --- 37,45 ---- > #include <openssl/blowfish.h> > #include <openssl/cast.h> > #include <openssl/des.h> > + #include <openssl/rand.h> > + #include <openssl/err.h> > + > > /* > * Does OpenSSL support AES? > *************** px_find_cipher(const char *name, PX_Ciph > *** 759,761 **** > --- 762,819 ---- > *res = c; > return 0; > } > + > + > + static int openssl_random_init = 0; > + > + /* > + * OpenSSL random should re-feeded occasionally. From /dev/urandom > + * preferably. > + */ > + static void init_openssl_rand() > + { > + if (RAND_get_rand_method() == NULL) > + RAND_set_rand_method(RAND_SSLeay()); > + openssl_random_init = 1; > + } > + > + int > + px_get_random_bytes(uint8 *dst, unsigned count) > + { > + int res; > + > + if (!openssl_random_init) > + init_openssl_rand(); > + > + res = RAND_bytes(dst, count); > + if (res == 1) > + return count; > + > + return PXE_OSSL_RAND_ERROR; > + } > + > + int > + px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > + { > + int res; > + > + if (!openssl_random_init) > + init_openssl_rand(); > + > + res = RAND_pseudo_bytes(dst, count); > + if (res == 0 || res == 1) > + return count; > + > + return PXE_OSSL_RAND_ERROR; > + } > + > + int > + px_add_entropy(const uint8 *data, unsigned count) > + { > + /* > + * estimate 0 bits > + */ > + RAND_add(data, count, 0); > + return 0; > + } > + > Index: pgsql/contrib/pgcrypto/px.h > =================================================================== > *** pgsql.orig/contrib/pgcrypto/px.h > --- pgsql/contrib/pgcrypto/px.h > *************** int px_find_combo(const char *name, PX > *** 170,175 **** > --- 170,178 ---- > > int px_get_random_bytes(uint8 *dst, unsigned count); > int px_get_pseudo_random_bytes(uint8 *dst, unsigned count); > + int px_add_entropy(const uint8 *data, unsigned count); > + > + unsigned px_acquire_system_randomness(uint8 *dst); > > const char *px_strerror(int err); > > Index: pgsql/contrib/pgcrypto/random.c > =================================================================== > *** pgsql.orig/contrib/pgcrypto/random.c > --- pgsql/contrib/pgcrypto/random.c > *************** > *** 1,6 **** > /* > * random.c > ! * Random functions. > * > * Copyright (c) 2001 Marko Kreen > * All rights reserved. > --- 1,6 ---- > /* > * random.c > ! * Acquire randomness from system. For seeding RNG. > * > * Copyright (c) 2001 Marko Kreen > * All rights reserved. > *************** > *** 34,41 **** > > #include "px.h" > > > ! #if defined(RAND_DEV) > > #include <errno.h> > #include <fcntl.h> > --- 34,53 ---- > > #include "px.h" > > + /* how many bytes to ask from system random provider */ > + #define RND_BYTES 32 > > ! /* > ! * Try to read from /dev/urandom or /dev/random on these OS'es. > ! * > ! * The list can be pretty liberal, as the device not existing > ! * is expected event. > ! */ > ! #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ > ! || defined(__NetBSD__) || defined(__DragonFly__) \ > ! || defined(__darwin__) || defined(__SOLARIS__) > ! > ! #define TRY_DEV_RANDOM > > #include <errno.h> > #include <fcntl.h> > *************** safe_read(int fd, void *buf, size_t coun > *** 64,157 **** > return done; > } > > ! int > ! px_get_random_bytes(uint8 *dst, unsigned count) > { > int fd; > int res; > > ! fd = open(RAND_DEV, O_RDONLY); > if (fd == -1) > ! return PXE_DEV_READ_ERROR; > ! res = safe_read(fd, dst, count); > close(fd); > ! return res; > } > > ! int > ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > ! { > ! return px_get_random_bytes(dst, count); > ! } > > ! #elif defined(RAND_SILLY) > > ! int > ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > { > ! int i; > > ! for (i = 0; i < count; i++) > ! *dst++ = random(); > ! return i; > } > > ! int > ! px_get_random_bytes(uint8 *dst, unsigned count) > { > ! return PXE_NO_RANDOM; > ! } > > ! #elif defined(RAND_OPENSSL) > > ! #include <openssl/evp.h> > ! #include <openssl/blowfish.h> > ! #include <openssl/rand.h> > ! #include <openssl/err.h> > > - static int openssl_random_init = 0; > > /* > ! * OpenSSL random should re-feeded occasionally. From /dev/urandom > ! * preferably. > */ > ! static void init_openssl() > ! { > ! if (RAND_get_rand_method() == NULL) > ! RAND_set_rand_method(RAND_SSLeay()); > ! openssl_random_init = 1; > ! } > > ! int > ! px_get_random_bytes(uint8 *dst, unsigned count) > ! { > ! int res; > ! > ! if (!openssl_random_init) > ! init_openssl(); > ! > ! res = RAND_bytes(dst, count); > ! if (res == 1) > ! return count; > > ! return PXE_OSSL_RAND_ERROR; > ! } > > ! int > ! px_get_pseudo_random_bytes(uint8 *dst, unsigned count) > { > ! int res; > > ! if (!openssl_random_init) > ! init_openssl(); > > ! res = RAND_pseudo_bytes(dst, count); > ! if (res == 0 || res == 1) > ! return count; > > ! return PXE_OSSL_RAND_ERROR; > } > > - #else > - #error "Invalid random source" > #endif > --- 76,244 ---- > return done; > } > > ! static uint8 * > ! try_dev_random(uint8 *dst) > { > int fd; > int res; > > ! fd = open("/dev/urandom", O_RDONLY); > if (fd == -1) > ! { > ! fd = open("/dev/random", O_RDONLY); > ! if (fd == -1) > ! return dst; > ! } > ! res = safe_read(fd, dst, RND_BYTES); > close(fd); > ! if (res > 0) > ! dst += res; > ! return dst; > } > > ! #endif > ! > ! /* > ! * Try to find randomness on Windows > ! */ > ! #ifdef WIN32 > ! > ! #define TRY_WIN32_GENRAND > ! #define TRY_WIN32_PERFC > > ! #define _WIN32_WINNT 0x0400 > ! #include <windows.h> > ! #include <wincrypt.h> > > ! /* > ! * this function is from libtomcrypt > ! * > ! * try to use Microsoft crypto API > ! */ > ! static uint8 * try_win32_genrand(uint8 *dst) > { > ! int res; > ! HCRYPTPROV h = 0; > ! > ! res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL, > ! (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)); > ! if (!res) > ! res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL, > ! CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET); > ! if (!res) > ! return dst; > ! > ! res = CryptGenRandom(h, NUM_BYTES, dst); > ! if (res == TRUE) > ! dst += len; > > ! CryptReleaseContext(h, 0); > ! return dst; > } > > ! static uint8 * try_win32_perfc(uint8 *dst) > { > ! int res; > ! LARGE_INTEGER time; > > ! res = QueryPerformanceCounter(&time); > ! if (!res) > ! return dst; > > ! memcpy(dst, &time, sizeof(time)); > ! return dst + sizeof(time); > ! } > ! > ! #endif /* WIN32 */ > > > /* > ! * If we are not on Windows, then hopefully we are > ! * on a unix-like system. Use the usual suspects > ! * for randomness. > */ > ! #ifndef WIN32 > > ! #define TRY_UNIXSTD > > ! #include <sys/types.h> > ! #include <sys/time.h> > ! #include <time.h> > ! #include <unistd.h> > > ! /* > ! * Everything here is predictible, only needs some patience. > ! * > ! * But there is a chance that the system-specific functions > ! * did not work. So keep faith and try to slow the attacker down. > ! */ > ! static uint8 * > ! try_unix_std(uint8 *dst) > { > ! pid_t pid; > ! int x; > ! PX_MD *md; > ! struct timeval tv; > ! int res; > ! > ! /* process id */ > ! pid = getpid(); > ! memcpy(dst, (uint8*)&pid, sizeof(pid)); > ! dst += sizeof(pid); > ! > ! /* time */ > ! gettimeofday(&tv, NULL); > ! memcpy(dst, (uint8*)&tv, sizeof(tv)); > ! dst += sizeof(tv); > ! > ! /* pointless, but should not hurt */ > ! x = random(); > ! memcpy(dst, (uint8*)&x, sizeof(x)); > ! dst += sizeof(x); > ! > ! /* let's be desperate */ > ! res = px_find_digest("sha1", &md); > ! if (res >= 0) { > ! uint8 *ptr; > ! uint8 stack[8192]; > ! int alloc = 32*1024; > ! > ! px_md_update(md, stack, sizeof(stack)); > ! ptr = px_alloc(alloc); > ! px_md_update(md, ptr, alloc); > ! px_free(ptr); > > ! px_md_finish(md, dst); > ! px_md_free(md); > > ! dst += 20; > ! } > > ! return dst; > } > > #endif > + > + /* > + * try to extract some randomness for initial seeding > + * > + * dst should have room for 1024 bytes. > + */ > + unsigned px_acquire_system_randomness(uint8 *dst) > + { > + uint8 *p = dst; > + #ifdef TRY_DEV_RANDOM > + p = try_dev_random(p); > + #endif > + #ifdef TRY_WIN32_GENRAND > + p = try_win32_genrand(p); > + #endif > + #ifdef TRY_WIN32_PERFC > + p = try_win32_perfc(p); > + #endif > + #ifdef TRY_UNIXSTD > + p = try_unix_std(p); > + #endif > + return p - dst; > + } > + > > -- > > ---------------------------(end of broadcast)--------------------------- > TIP 5: don't forget to increase your free space map settings > -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073