Thanks. Applied.
>
> I would like to do a interface change in pgcrypto. (Good
> timing, I know :)) At the moment the digest() function returns
> hexadecimal coded hash, but I want it to return pure binary. I
> have also included functions encode() and decode() which support
> 'base64' and 'hex' encodings, so if anyone needs digest() in hex
> he can do encode(digest(...), 'hex').
>
> Main reason for it is "to do one thing and do it well" :)
>
> Another reason is if someone needs really lot of digesting, in
> the end he wants to store the binary not the hexadecimal result.
> It is really silly to convert it to hex then back to binary
> again. As I said if someone needs hex he can get it.
>
> Well, and the real reason that I am doing encrypt()/decrypt()
> functions and _they_ return binary. For testing I like to see
> it in hex occasionally, but it is really wrong to let them
> return hex. Only now it caught my eye that hex-coding in
> digest() is wrong. When doing digest() I thought about 'common
> case' but hacking with psql is probably _not_ the common case :)
>
> --
> marko
>
>
> diff -urNX /home/marko/misc/diff-exclude contrib/pgcrypto.orig/Makefile contrib/pgcrypto/Makefile
> --- contrib/pgcrypto.orig/Makefile Tue Oct 31 15:11:28 2000
> +++ contrib/pgcrypto/Makefile Sun Jan 21 00:14:54 2001
> @@ -34,7 +34,7 @@
> endif
>
> NAME := pgcrypto
> -SRCS += pgcrypto.c
> +SRCS += pgcrypto.c encode.c
> OBJS := $(SRCS:.c=.o)
> SO_MAJOR_VERSION = 0
> SO_MINOR_VERSION = 1
> diff -urNX /home/marko/misc/diff-exclude contrib/pgcrypto.orig/README.pgcrypto contrib/pgcrypto/README.pgcrypto
> --- contrib/pgcrypto.orig/README.pgcrypto Tue Oct 31 15:11:28 2000
> +++ contrib/pgcrypto/README.pgcrypto Sun Jan 21 00:21:29 2001
> @@ -1,14 +1,21 @@
>
> DESCRIPTION
>
> - Here is a implementation of crypto hashes for PostgreSQL.
> - It exports 2 functions to SQL level:
> + Here are various cryptographic and otherwise useful
> + functions for PostgreSQL.
> +
> + encode(data, type)
> + encodes binary data into ASCII-only representation.
> + Types supported are 'hex' and 'base64'.
> +
> + decode(data, type)
> + decodes the data processed by encode()
>
> digest(data::text, hash_name::text)
> - which returns hexadecimal coded hash over data by
> + which returns cryptographic checksum over data by
> specified algorithm. eg
>
> - > select digest('blah', 'sha1');
> + > select encode(digest('blah', 'sha1'), 'hex');
> 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145
>
> digest_exists(hash_name::text)::bool
> diff -urNX /home/marko/misc/diff-exclude contrib/pgcrypto.orig/encode.c contrib/pgcrypto/encode.c
> --- contrib/pgcrypto.orig/encode.c Thu Jan 1 03:00:00 1970
> +++ contrib/pgcrypto/encode.c Sun Jan 21 23:48:55 2001
> @@ -0,0 +1,345 @@
> +/*
> + * encode.c
> + * Various data encoding/decoding things.
> + *
> + * 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$
> + */
> +
> +#include <postgres.h>
> +#include <fmgr.h>
> +
> +#include "encode.h"
> +
> +/*
> + * NAMEDATALEN is used for hash names
> + */
> +#if NAMEDATALEN < 16
> +#error "NAMEDATALEN < 16: too small"
> +#endif
> +
> +static pg_coding *
> +find_coding(pg_coding *hbuf, text *name, int silent);
> +static pg_coding *
> +pg_find_coding(pg_coding *res, char *name);
> +
> +
> +/* SQL function: encode(bytea, text) returns text */
> +PG_FUNCTION_INFO_V1(encode);
> +
> +Datum
> +encode(PG_FUNCTION_ARGS)
> +{
> + text *arg;
> + text *name;
> + uint len, rlen, rlen0;
> + pg_coding *c, cbuf;
> + text *res;
> +
> + if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
> + PG_RETURN_NULL();
> +
> + name = PG_GETARG_TEXT_P(1);
> + c = find_coding(&cbuf, name, 0); /* will give error if fails */
> +
> + arg = PG_GETARG_TEXT_P(0);
> + len = VARSIZE(arg) - VARHDRSZ;
> +
> + rlen0 = c->encode_len(len);
> +
> + res = (text *)palloc(rlen0 + VARHDRSZ);
> +
> + rlen = c->encode(VARDATA(arg), len, VARDATA(res));
> + VARATT_SIZEP(res) = rlen + VARHDRSZ;
> +
> + if (rlen > rlen0)
> + elog(FATAL, "pg_encode: overflow, encode estimate too small");
> +
> + PG_FREE_IF_COPY(arg, 0);
> + PG_FREE_IF_COPY(name, 0);
> +
> + PG_RETURN_TEXT_P(res);
> +}
> +
> +/* SQL function: decode(text, text) returns bytea */
> +PG_FUNCTION_INFO_V1(decode);
> +
> +Datum
> +decode(PG_FUNCTION_ARGS)
> +{
> + text *arg;
> + text *name;
> + uint len, rlen, rlen0;
> + pg_coding *c, cbuf;
> + text *res;
> +
> + if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
> + PG_RETURN_NULL();
> +
> + name = PG_GETARG_TEXT_P(1);
> + c = find_coding(&cbuf, name, 0); /* will give error if fails */
> +
> + arg = PG_GETARG_TEXT_P(0);
> + len = VARSIZE(arg) - VARHDRSZ;
> +
> + rlen0 = c->decode_len(len);
> +
> + res = (text *)palloc(rlen0 + VARHDRSZ);
> +
> + rlen = c->decode(VARDATA(arg), len, VARDATA(res));
> + VARATT_SIZEP(res) = rlen + VARHDRSZ;
> +
> + if (rlen > rlen0)
> + elog(FATAL, "pg_decode: overflow, decode estimate too small");
> +
> + PG_FREE_IF_COPY(arg, 0);
> + PG_FREE_IF_COPY(name, 0);
> +
> + PG_RETURN_TEXT_P(res);
> +}
> +
> +static pg_coding *
> +find_coding(pg_coding *dst, text *name, int silent)
> +{
> + pg_coding *p;
> + char buf[NAMEDATALEN];
> + uint len;
> +
> + len = VARSIZE(name) - VARHDRSZ;
> + if (len >= NAMEDATALEN) {
> + if (silent)
> + return NULL;
> + elog(ERROR, "Encoding type does not exist (name too long)");
> + }
> +
> + memcpy(buf, VARDATA(name), len);
> + buf[len] = 0;
> +
> + p = pg_find_coding(dst, buf);
> +
> + if (p == NULL && !silent)
> + elog(ERROR, "Encoding type does not exist: '%s'", buf);
> + return p;
> +}
> +
> +static char *hextbl = "0123456789abcdef";
> +
> +uint
> +hex_encode(uint8 *src, uint len, uint8 *dst)
> +{
> + uint8 *end = src + len;
> + while (src < end) {
> + *dst++ = hextbl[(*src >> 4) & 0xF];
> + *dst++ = hextbl[*src & 0xF];
> + src++;
> + }
> + return len*2;
> +}
> +
> +/* probably should use lookup table */
> +static uint8
> +get_hex(char c)
> +{
> + uint8 res = 0;
> +
> + if (c >= '0' && c <= '9')
> + res = c - '0';
> + else if (c >= 'a' && c <= 'f')
> + res = c - 'a' + 10;
> + else if (c >= 'A' && c <= 'F')
> + res = c - 'A' + 10;
> + else
> + elog(ERROR, "Bad hex code: '%c'", c);
> +
> + return res;
> +}
> +
> +uint
> +hex_decode(uint8 *src, uint len, uint8 *dst)
> +{
> + uint8 *s, *srcend, v1, v2, *p = dst;
> +
> + srcend = src + len;
> + s = src; p = dst;
> + while (s < srcend) {
> + if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r') {
> + s++;
> + continue;
> + }
> + v1 = get_hex(*s++) << 4;
> + if (s >= srcend)
> + elog(ERROR, "hex_decode: invalid data");
> + v2 = get_hex(*s++);
> + *p++ = v1 | v2;
> + }
> +
> + return p - dst;
> +}
> +
> +
> +static unsigned char _base64[] =
> + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
> +
> +uint
> +b64_encode(uint8 *src, uint len, uint8 *dst)
> +{
> + uint8 *s, *p, *end = src + len, *lend = dst + 76;
> + int pos = 2;
> + unsigned long buf = 0;
> +
> + s = src; p = dst;
> +
> + while (s < end) {
> + buf |= *s << (pos << 3);
> + pos--;
> + s++;
> +
> + /* write it out */
> + if (pos < 0) {
> + *p++ = _base64[(buf >> 18) & 0x3f];
> + *p++ = _base64[(buf >> 12) & 0x3f];
> + *p++ = _base64[(buf >> 6) & 0x3f];
> + *p++ = _base64[buf & 0x3f];
> +
> + pos = 2;
> + buf = 0;
> + }
> + if (p >= lend) {
> + *p++ = '\n';
> + lend = p + 76;
> + }
> + }
> + if (pos != 2) {
> + *p++ = _base64[(buf >> 18) & 0x3f];
> + *p++ = _base64[(buf >> 12) & 0x3f];
> + *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
> + *p++ = '=';
> + }
> +
> + return p - dst;
> +}
> +
> +/* probably should use lookup table */
> +uint
> +b64_decode(uint8 *src, uint len, uint8 *dst)
> +{
> + char *srcend = src + len, *s = src;
> + uint8 *p = dst;
> + char c;
> + uint b = 0;
> + unsigned long buf = 0;
> + int pos = 0, end = 0;
> +
> + while (s < srcend) {
> + c = *s++;
> + if (c >= 'A' && c <= 'Z')
> + b = c - 'A';
> + else if (c >= 'a' && c <= 'z')
> + b = c - 'a' + 26;
> + else if (c >= '0' && c <= '9')
> + b = c - '0' + 52;
> + else if (c == '+')
> + b = 62;
> + else if (c == '/')
> + b = 63;
> + else if (c == '=') {
> + /* end sequence */
> + if (!end) {
> + if (pos == 2) end = 1;
> + else if (pos == 3) end = 2;
> + else
> + elog(ERROR, "base64: unexpected '='");
> + }
> + b = 0;
> + } else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
> + continue;
> + else
> + elog(ERROR, "base64: Invalid symbol");
> +
> + /* add it to buffer */
> + buf = (buf << 6) + b;
> + pos++;
> + if (pos == 4) {
> + *p++ = (buf >> 16) & 255;
> + if (end == 0 || end > 1)
> + *p++ = (buf >> 8) & 255;
> + if (end == 0 || end > 2)
> + *p++ = buf & 255;
> + buf = 0;
> + pos = 0;
> + }
> + }
> +
> + if (pos != 0)
> + elog(ERROR, "base64: invalid end sequence");
> +
> + return p - dst;
> +}
> +
> +
> +uint
> +hex_enc_len(uint srclen)
> +{
> + return srclen << 1;
> +}
> +
> +uint
> +hex_dec_len(uint srclen)
> +{
> + return srclen >> 1;
> +}
> +
> +uint
> +b64_enc_len(uint srclen)
> +{
> + return srclen + (srclen / 3) + (srclen / (76 / 2));
> +}
> +
> +uint
> +b64_dec_len(uint srclen)
> +{
> + return (srclen * 3) >> 2;
> +}
> +
> +static pg_coding
> +encoding_list [] = {
> + { "hex", hex_enc_len, hex_dec_len, hex_encode, hex_decode},
> + { "base64", b64_enc_len, b64_dec_len, b64_encode, b64_decode},
> + { NULL, NULL, NULL, NULL, NULL}
> +};
> +
> +
> +static pg_coding *
> +pg_find_coding(pg_coding *res, char *name)
> +{
> + pg_coding *p;
> + for (p = encoding_list; p->name; p++) {
> + if (!strcasecmp(p->name, name))
> + return p;
> + }
> + return NULL;
> +}
> +
> diff -urNX /home/marko/misc/diff-exclude contrib/pgcrypto.orig/encode.h contrib/pgcrypto/encode.h
> --- contrib/pgcrypto.orig/encode.h Thu Jan 1 03:00:00 1970
> +++ contrib/pgcrypto/encode.h Sun Jan 21 20:01:01 2001
> @@ -0,0 +1,60 @@
> +/*
> + * pg_encode.h
> + * encode.c
> + *
> + * 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$
> + */
> +
> +#ifndef __PG_ENCODE_H
> +#define __PG_ENCODE_H
> +
> +/* exported functions */
> +Datum encode(PG_FUNCTION_ARGS);
> +Datum decode(PG_FUNCTION_ARGS);
> +
> +typedef struct _pg_coding pg_coding;
> +struct _pg_coding {
> + char *name;
> + uint (*encode_len)(uint dlen);
> + uint (*decode_len)(uint dlen);
> + uint (*encode)(uint8 *data, uint dlen, uint8 *res);
> + uint (*decode)(uint8 *data, uint dlen, uint8 *res);
> +};
> +
> +/* They are for outside usage in C code, if needed */
> +uint hex_encode(uint8 *src, uint len, uint8 *dst);
> +uint hex_decode(uint8 *src, uint len, uint8 *dst);
> +uint b64_encode(uint8 *src, uint len, uint8 *dst);
> +uint b64_decode(uint8 *src, uint len, uint8 *dst);
> +
> +uint hex_enc_len(uint srclen);
> +uint hex_dec_len(uint srclen);
> +uint b64_enc_len(uint srclen);
> +uint b64_dec_len(uint srclen);
> +
> +#endif /* __PG_ENCODE_H */
> +
> Binary files contrib/pgcrypto.orig/libpgcrypto.so.0 and contrib/pgcrypto/libpgcrypto.so.0 differ
> diff -urNX /home/marko/misc/diff-exclude contrib/pgcrypto.orig/pgcrypto.c contrib/pgcrypto/pgcrypto.c
> --- contrib/pgcrypto.orig/pgcrypto.c Wed Jan 10 08:23:22 2001
> +++ contrib/pgcrypto/pgcrypto.c Sun Jan 21 19:59:38 2001
> @@ -35,11 +35,6 @@
> #include "pgcrypto.h"
>
> /*
> - * maximum length of digest for internal buffers
> - */
> -#define MAX_DIGEST_LENGTH 128
> -
> -/*
> * NAMEDATALEN is used for hash names
> */
> #if NAMEDATALEN < 16
> @@ -52,8 +47,6 @@
> Datum digest_exists(PG_FUNCTION_ARGS);
>
> /* private stuff */
> -static char *
> -to_hex(uint8 *src, uint len, char *dst);
> static pg_digest *
> find_digest(pg_digest *hbuf, text *name, int silent);
>
> @@ -66,7 +59,6 @@
> {
> text *arg;
> text *name;
> - uint8 *p, buf[MAX_DIGEST_LENGTH];
> uint len, hlen;
> pg_digest *h, _hbuf;
> text *res;
> @@ -78,17 +70,14 @@
> h = find_digest(&_hbuf, name, 0); /* will give error if fails */
>
> hlen = h->length(h);
> - if (hlen > MAX_DIGEST_LENGTH)
> - elog(ERROR, "Hash length overflow: %d", hlen);
>
> - res = (text *)palloc(hlen*2 + VARHDRSZ);
> - VARATT_SIZEP(res) = hlen*2 + VARHDRSZ;
> + res = (text *)palloc(hlen + VARHDRSZ);
> + VARATT_SIZEP(res) = hlen + VARHDRSZ;
>
> arg = PG_GETARG_TEXT_P(0);
> len = VARSIZE(arg) - VARHDRSZ;
>
> - p = h->digest(h, VARDATA(arg), len, buf);
> - to_hex(p, hlen, VARDATA(res));
> + h->digest(h, VARDATA(arg), len, VARDATA(res));
>
> PG_FREE_IF_COPY(arg, 0);
> PG_FREE_IF_COPY(name, 0);
> @@ -141,19 +130,5 @@
> if (p == NULL && !silent)
> elog(ERROR, "Hash type does not exist: '%s'", buf);
> return p;
> -}
> -
> -static unsigned char *hextbl = "0123456789abcdef";
> -
> -/* dumps binary to hex... Note that it does not null-terminate */
> -static char *
> -to_hex(uint8 *buf, uint len, char *dst)
> -{
> - uint i;
> - for (i = 0; i < len; i++) {
> - dst[i*2] = hextbl[(buf[i] >> 4) & 0xF];
> - dst[i*2 + 1] = hextbl[buf[i] & 0xF];
> - }
> - return dst;
> }
>
> diff -urNX /home/marko/misc/diff-exclude contrib/pgcrypto.orig/pgcrypto.sql.in contrib/pgcrypto/pgcrypto.sql.in
> --- contrib/pgcrypto.orig/pgcrypto.sql.in Mon Nov 20 22:36:56 2000
> +++ contrib/pgcrypto/pgcrypto.sql.in Sun Jan 21 21:27:48 2001
> @@ -1,6 +1,9 @@
>
> -- drop function digest(text, text);
> -- drop function digest_exists(text);
> +-- drop function encode(text, text);
> +-- drop function decode(text, text);
> +
>
> CREATE FUNCTION digest(text, text) RETURNS text
> AS '@MODULE_FILENAME@',
> @@ -9,4 +12,12 @@
> CREATE FUNCTION digest_exists(text) RETURNS bool
> AS '@MODULE_FILENAME@',
> 'digest_exists' LANGUAGE 'C';
> +
> +CREATE FUNCTION encode(text, text) RETURNS text
> + AS '@MODULE_FILENAME@',
> + 'encode' LANGUAGE 'C';
> +
> +CREATE FUNCTION decode(text, text) RETURNS text
> + AS '@MODULE_FILENAME@',
> + 'decode' LANGUAGE 'C';
>
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 853-3000
+ If your life is a hard drive, | 830 Blythe Avenue
+ Christ can be your backup. | Drexel Hill, Pennsylvania 19026