/* * pgcrypto.c * Various cryptographic stuff for PostgreSQL. * * Copyright (c) 2001 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. * * $Id: pgcrypto.c,v 1.14 2003/08/04 00:43:11 momjian Exp $ */ #include #include #include #include "px.h" #include "px-crypt.h" #include "pgcrypto.h" /* private stuff */ typedef int (*PFN) (const char *name, void **res); static void * find_provider(text *name, PFN pf, char *desc, int silent); /************* BEGIN ADDED BY CARLOS MORENO ****************/ static void px_sha1 (const unsigned char * s, unsigned int length, unsigned char * hash); /************* END ADDED BY CARLOS MORENO ******************/ /* SQL function: hash(text, text) returns text */ PG_FUNCTION_INFO_V1(pg_digest); Datum pg_digest(PG_FUNCTION_ARGS) { bytea *arg; text *name; unsigned len, hlen; PX_MD *md; bytea *res; if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) PG_RETURN_NULL(); name = PG_GETARG_TEXT_P(1); /* will give error if fails */ md = find_provider(name, (PFN) px_find_digest, "Digest", 0); hlen = px_md_result_size(md); res = (text *) palloc(hlen + VARHDRSZ); VARATT_SIZEP(res) = hlen + VARHDRSZ; arg = PG_GETARG_BYTEA_P(0); len = VARSIZE(arg) - VARHDRSZ; px_md_update(md, VARDATA(arg), len); px_md_finish(md, VARDATA(res)); px_md_free(md); PG_FREE_IF_COPY(arg, 0); PG_FREE_IF_COPY(name, 1); PG_RETURN_BYTEA_P(res); } /************* BEGIN ADDED BY CARLOS MORENO ****************/ PG_FUNCTION_INFO_V1(pg_sha1); Datum pg_sha1 (PG_FUNCTION_ARGS) { bytea *arg; unsigned len, hlen; PX_MD *md; bytea *res; if (PG_ARGISNULL(0)) PG_RETURN_NULL(); hlen = 20; res = (text *) palloc(hlen + VARHDRSZ); VARATT_SIZEP(res) = hlen + VARHDRSZ; arg = PG_GETARG_BYTEA_P(0); len = VARSIZE(arg) - VARHDRSZ; px_sha1 ((const unsigned char *)(VARDATA(arg)), len, (unsigned char *)(VARDATA(res))); PG_FREE_IF_COPY(arg, 0); PG_RETURN_BYTEA_P(res); } /************* END ADDED BY CARLOS MORENO ****************/ /************************************************************** PLENTY OF OTHER STUFF DELETED TO KEEP THIS FILE SHORT -- ENCRYPT, DECRYPT, HMAC, ETC. **************************************************************/ /********** EVERYTHING FROM HERE ON ADDED BY CARLOS MORENO ***********/ /** THE "REAL" SQL SHA1 FUNCTION IS MAPPED TO pg_sha1 ABOVE, WHICH SIMPLY CALLS px_sha1 FUNCTION, DEFINED AT THE BOTTOM OF THIS FILE **/ unsigned int old_leftrotate (unsigned int x, int num) { int i; for (i = 0; i < num; i++) { const bool msb = ((x & 0x80000000) != 0); x <<= 1; if (msb) { x |= 1; } } return x; } unsigned int leftrotate (unsigned int x, int num) { unsigned int mask = ~((1 << (32 - num)) - 1); unsigned int high_bits = (x & mask) >> (32 - num); return (x << num) | high_bits; } unsigned int rightrotate (unsigned int x, int num) { unsigned int mask = (1 << num) - 1; unsigned int low_bits = (x & mask) << (32 - num); return (x >> num) | low_bits; } unsigned int big_endian (const unsigned char * s, unsigned int len) { unsigned int x = 0; unsigned int i; for (i = 0; i < len; i++) { x <<= 8; x |= s[i]; } return x; } /* The function below is called _str because in my original version, I was producing a hex-encoded string -- I modified it to produce a "bytea" result -- so this would be rather a big_endian_unpack kind of thing */ void big_endian_str (unsigned int x, unsigned char * str) { int i; for (i = 0; i < 4; i++) { str[3-i] = (unsigned char)(x & 0xFF); x >>= 8; } } void sha1_process_chunk (const unsigned char chunk[512/8], unsigned int * h0, unsigned int * h1, unsigned int * h2, unsigned int * h3, unsigned int * h4) { unsigned int w[80]; int i; // Initialize hash value for this chunk: unsigned int a = *h0; unsigned int b = *h1; unsigned int c = *h2; unsigned int d = *h3; unsigned int e = *h4; unsigned int k, f; for (i = 0; i < 16; i++) { w[i] = big_endian (&chunk[i*4], 4); } // Extend the sixteen 32-bit words into eighty 32-bit words: for (i = 16; i < 80; i++) { w[i] = leftrotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1); } // Main loop: for (i = 0; i < 80; i++) { if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; } else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; } else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; } else { f = b ^ c ^ d; k = 0xCA62C1D6; } const unsigned int temp = leftrotate(a,5) + f + e + k + w[i]; e = d; d = c; c = leftrotate (b,30); b = a; a = temp; } // Add this chunk's hash to result so far: *h0 += a; *h1 += b; *h2 += c; *h3 += d; *h4 += e; } #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif static void px_sha1 (const unsigned char * s, unsigned int length, unsigned char * hash) { unsigned int h0 = 0x67452301; unsigned int h1 = 0xEFCDAB89; unsigned int h2 = 0x98BADCFE; unsigned int h3 = 0x10325476; unsigned int h4 = 0xC3D2E1F0; // Pre-processing: // append a single "1" bit to message // append "0" bits until message length ≡ 448 ≡ -64 (mod 512) // append length of message (before pre-processing), in bits as 64-bit big-endian integer to message // Process the message in successive 512-bit chunks: // break message into 512-bit chunks int small_block = FALSE; unsigned int chk; for (chk = 0; chk < length; chk += 512/8) { const unsigned int chunk_len = ((length - chk > 512/8) ? 512/8 : length - chk); if (chunk_len == 512/8) { sha1_process_chunk (s + chk, &h0, &h1, &h2, &h3, &h4); } else if (chunk_len < 448/8) { unsigned char chunk [512/8] = {0}; unsigned int i, s_len; int j; for (i = 0; i < chunk_len; i++) { chunk[i] = s[chk+i]; } small_block = TRUE; chunk[chunk_len] = (unsigned char)0x80; s_len = 8*length; for (j = 0; j < 4; j++) { chunk[512/8 - 1 - j] = (unsigned char)(s_len & 0xFF); chunk[512/8 - 1 - j - 4] = 0; s_len >>= 8; } sha1_process_chunk (chunk, &h0, &h1, &h2, &h3, &h4); } else { unsigned char chunk [512/8] = {0}; unsigned int i, s_len; int j; for (i = 0; i < chunk_len; i++) { chunk[i] = s[chk+i]; } small_block = TRUE; chunk[chunk_len] = (unsigned char)0x80; sha1_process_chunk (chunk, &h0, &h1, &h2, &h3, &h4); for (j = 0; j < 448/8; j++) { chunk[j] = 0; } s_len = 8*length; for (j = 0; j < 4; j++) { chunk[512/8 - 1 - j] = (unsigned char)(s_len & 0xFF); chunk[512/8 - 1 - j - 4] = 0; s_len >>= 8; } sha1_process_chunk (chunk, &h0, &h1, &h2, &h3, &h4); } // break chunk into sixteen 32-bit big-endian words w(i), 0 <= i <= 15 } if (! small_block) // would happen if the data's length is an exact multiple of 512 { unsigned char chunk[512/8] = {(unsigned char)0x80}; unsigned int s_len = 8*length; int i; for (i = 0; i < 4; i++) { chunk[512/8 - 1 - i] = (unsigned char)(s_len & 0xFF); chunk[512/8 - 1 - i - 4] = 0; s_len >>= 8; } sha1_process_chunk (chunk, &h0, &h1, &h2, &h3, &h4); } big_endian_str (h0, hash); big_endian_str (h1, hash+4); big_endian_str (h2, hash+8); big_endian_str (h3, hash+12); big_endian_str (h4, hash+16); }