summaryrefslogtreecommitdiffstats
path: root/contrib/pgcrypto/px.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/pgcrypto/px.c')
-rw-r--r--contrib/pgcrypto/px.c460
1 files changed, 460 insertions, 0 deletions
diff --git a/contrib/pgcrypto/px.c b/contrib/pgcrypto/px.c
new file mode 100644
index 0000000..2099ce2
--- /dev/null
+++ b/contrib/pgcrypto/px.c
@@ -0,0 +1,460 @@
+/*
+ * px.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.
+ *
+ * contrib/pgcrypto/px.c
+ */
+
+#include "postgres.h"
+
+#include "px.h"
+
+struct error_desc
+{
+ int err;
+ const char *desc;
+};
+
+static const struct error_desc px_err_list[] = {
+ {PXE_OK, "Everything ok"},
+ {PXE_ERR_GENERIC, "Some PX error (not specified)"},
+ {PXE_NO_HASH, "No such hash algorithm"},
+ {PXE_NO_CIPHER, "No such cipher algorithm"},
+ {PXE_NOTBLOCKSIZE, "Data not a multiple of block size"},
+ {PXE_BAD_OPTION, "Unknown option"},
+ {PXE_BAD_FORMAT, "Badly formatted type"},
+ {PXE_KEY_TOO_BIG, "Key was too big"},
+ {PXE_CIPHER_INIT, "Cipher cannot be initialized ?"},
+ {PXE_HASH_UNUSABLE_FOR_HMAC, "This hash algorithm is unusable for HMAC"},
+ {PXE_DEV_READ_ERROR, "Error reading from random device"},
+ {PXE_BUG, "pgcrypto bug"},
+ {PXE_ARGUMENT_ERROR, "Illegal argument to function"},
+ {PXE_UNKNOWN_SALT_ALGO, "Unknown salt algorithm"},
+ {PXE_BAD_SALT_ROUNDS, "Incorrect number of rounds"},
+ {PXE_MCRYPT_INTERNAL, "mcrypt internal error"},
+ {PXE_NO_RANDOM, "Failed to generate strong random bits"},
+ {PXE_DECRYPT_FAILED, "Decryption failed"},
+ {PXE_ENCRYPT_FAILED, "Encryption failed"},
+ {PXE_PGP_CORRUPT_DATA, "Wrong key or corrupt data"},
+ {PXE_PGP_CORRUPT_ARMOR, "Corrupt ascii-armor"},
+ {PXE_PGP_UNSUPPORTED_COMPR, "Unsupported compression algorithm"},
+ {PXE_PGP_UNSUPPORTED_CIPHER, "Unsupported cipher algorithm"},
+ {PXE_PGP_UNSUPPORTED_HASH, "Unsupported digest algorithm"},
+ {PXE_PGP_COMPRESSION_ERROR, "Compression error"},
+ {PXE_PGP_NOT_TEXT, "Not text data"},
+ {PXE_PGP_UNEXPECTED_PKT, "Unexpected packet in key data"},
+ {PXE_PGP_MATH_FAILED, "Math operation failed"},
+ {PXE_PGP_SHORT_ELGAMAL_KEY, "Elgamal keys must be at least 1024 bits long"},
+ {PXE_PGP_UNKNOWN_PUBALGO, "Unknown public-key encryption algorithm"},
+ {PXE_PGP_WRONG_KEY, "Wrong key"},
+ {PXE_PGP_MULTIPLE_KEYS,
+ "Several keys given - pgcrypto does not handle keyring"},
+ {PXE_PGP_EXPECT_PUBLIC_KEY, "Refusing to encrypt with secret key"},
+ {PXE_PGP_EXPECT_SECRET_KEY, "Cannot decrypt with public key"},
+ {PXE_PGP_NOT_V4_KEYPKT, "Only V4 key packets are supported"},
+ {PXE_PGP_KEYPKT_CORRUPT, "Corrupt key packet"},
+ {PXE_PGP_NO_USABLE_KEY, "No encryption key found"},
+ {PXE_PGP_NEED_SECRET_PSW, "Need password for secret key"},
+ {PXE_PGP_BAD_S2K_MODE, "Bad S2K mode"},
+ {PXE_PGP_UNSUPPORTED_PUBALGO, "Unsupported public key algorithm"},
+ {PXE_PGP_MULTIPLE_SUBKEYS, "Several subkeys not supported"},
+
+ {0, NULL},
+};
+
+/*
+ * Call ereport(ERROR, ...), with an error code and message corresponding to
+ * the PXE_* error code given as argument.
+ *
+ * This is similar to px_strerror(err), but for some errors, we fill in the
+ * error code and detail fields more appropriately.
+ */
+void
+px_THROW_ERROR(int err)
+{
+ if (err == PXE_NO_RANDOM)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("could not generate a random number")));
+ }
+ else
+ {
+ /* For other errors, use the message from the above list. */
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("%s", px_strerror(err))));
+ }
+}
+
+const char *
+px_strerror(int err)
+{
+ const struct error_desc *e;
+
+ for (e = px_err_list; e->desc; e++)
+ if (e->err == err)
+ return e->desc;
+ return "Bad error code";
+}
+
+/* memset that must not be optimized away */
+void
+px_memset(void *ptr, int c, size_t len)
+{
+ memset(ptr, c, len);
+}
+
+const char *
+px_resolve_alias(const PX_Alias *list, const char *name)
+{
+ while (list->name)
+ {
+ if (pg_strcasecmp(list->alias, name) == 0)
+ return list->name;
+ list++;
+ }
+ return name;
+}
+
+static void (*debug_handler) (const char *) = NULL;
+
+void
+px_set_debug_handler(void (*handler) (const char *))
+{
+ debug_handler = handler;
+}
+
+void
+px_debug(const char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug_handler)
+ {
+ char buf[512];
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ debug_handler(buf);
+ }
+ va_end(ap);
+}
+
+/*
+ * combo - cipher + padding (+ checksum)
+ */
+
+static unsigned
+combo_encrypt_len(PX_Combo *cx, unsigned dlen)
+{
+ return dlen + 512;
+}
+
+static unsigned
+combo_decrypt_len(PX_Combo *cx, unsigned dlen)
+{
+ return dlen;
+}
+
+static int
+combo_init(PX_Combo *cx, const uint8 *key, unsigned klen,
+ const uint8 *iv, unsigned ivlen)
+{
+ int err;
+ unsigned ks,
+ ivs;
+ PX_Cipher *c = cx->cipher;
+ uint8 *ivbuf = NULL;
+ uint8 *keybuf;
+
+ ks = px_cipher_key_size(c);
+
+ ivs = px_cipher_iv_size(c);
+ if (ivs > 0)
+ {
+ ivbuf = palloc0(ivs);
+ if (ivlen > ivs)
+ memcpy(ivbuf, iv, ivs);
+ else if (ivlen > 0)
+ memcpy(ivbuf, iv, ivlen);
+ }
+
+ if (klen > ks)
+ klen = ks;
+ keybuf = palloc0(ks);
+ memset(keybuf, 0, ks);
+ memcpy(keybuf, key, klen);
+
+ err = px_cipher_init(c, keybuf, klen, ivbuf);
+
+ if (ivbuf)
+ pfree(ivbuf);
+ pfree(keybuf);
+
+ return err;
+}
+
+static int
+combo_encrypt(PX_Combo *cx, const uint8 *data, unsigned dlen,
+ uint8 *res, unsigned *rlen)
+{
+ int err = 0;
+ uint8 *bbuf;
+ unsigned bs,
+ bpos,
+ i,
+ pad;
+
+ PX_Cipher *c = cx->cipher;
+
+ bbuf = NULL;
+ bs = px_cipher_block_size(c);
+
+ /* encrypt */
+ if (bs > 1)
+ {
+ bbuf = palloc(bs * 4);
+ bpos = dlen % bs;
+ *rlen = dlen - bpos;
+ memcpy(bbuf, data + *rlen, bpos);
+
+ /* encrypt full-block data */
+ if (*rlen)
+ {
+ err = px_cipher_encrypt(c, data, *rlen, res);
+ if (err)
+ goto out;
+ }
+
+ /* bbuf has now bpos bytes of stuff */
+ if (cx->padding)
+ {
+ pad = bs - (bpos % bs);
+ for (i = 0; i < pad; i++)
+ bbuf[bpos++] = pad;
+ }
+ else if (bpos % bs)
+ {
+ /* ERROR? */
+ pad = bs - (bpos % bs);
+ for (i = 0; i < pad; i++)
+ bbuf[bpos++] = 0;
+ }
+
+ /* encrypt the rest - pad */
+ if (bpos)
+ {
+ err = px_cipher_encrypt(c, bbuf, bpos, res + *rlen);
+ *rlen += bpos;
+ }
+ }
+ else
+ {
+ /* stream cipher/mode - no pad needed */
+ err = px_cipher_encrypt(c, data, dlen, res);
+ if (err)
+ goto out;
+ *rlen = dlen;
+ }
+out:
+ if (bbuf)
+ pfree(bbuf);
+
+ return err;
+}
+
+static int
+combo_decrypt(PX_Combo *cx, const uint8 *data, unsigned dlen,
+ uint8 *res, unsigned *rlen)
+{
+ int err = 0;
+ unsigned bs,
+ i,
+ pad;
+ unsigned pad_ok;
+
+ PX_Cipher *c = cx->cipher;
+
+ /* decide whether zero-length input is allowed */
+ if (dlen == 0)
+ {
+ /* with padding, empty ciphertext is not allowed */
+ if (cx->padding)
+ return PXE_DECRYPT_FAILED;
+
+ /* without padding, report empty result */
+ *rlen = 0;
+ return 0;
+ }
+
+ bs = px_cipher_block_size(c);
+ if (bs > 1 && (dlen % bs) != 0)
+ goto block_error;
+
+ /* decrypt */
+ *rlen = dlen;
+ err = px_cipher_decrypt(c, data, dlen, res);
+ if (err)
+ return err;
+
+ /* unpad */
+ if (bs > 1 && cx->padding)
+ {
+ pad = res[*rlen - 1];
+ pad_ok = 0;
+ if (pad > 0 && pad <= bs && pad <= *rlen)
+ {
+ pad_ok = 1;
+ for (i = *rlen - pad; i < *rlen; i++)
+ if (res[i] != pad)
+ {
+ pad_ok = 0;
+ break;
+ }
+ }
+
+ if (pad_ok)
+ *rlen -= pad;
+ }
+
+ return 0;
+
+block_error:
+ return PXE_NOTBLOCKSIZE;
+}
+
+static void
+combo_free(PX_Combo *cx)
+{
+ if (cx->cipher)
+ px_cipher_free(cx->cipher);
+ px_memset(cx, 0, sizeof(*cx));
+ pfree(cx);
+}
+
+/* PARSER */
+
+static int
+parse_cipher_name(char *full, char **cipher, char **pad)
+{
+ char *p,
+ *p2,
+ *q;
+
+ *cipher = full;
+ *pad = NULL;
+
+ p = strchr(full, '/');
+ if (p != NULL)
+ *p++ = 0;
+ while (p != NULL)
+ {
+ if ((q = strchr(p, '/')) != NULL)
+ *q++ = 0;
+
+ if (!*p)
+ {
+ p = q;
+ continue;
+ }
+ p2 = strchr(p, ':');
+ if (p2 != NULL)
+ {
+ *p2++ = 0;
+ if (strcmp(p, "pad") == 0)
+ *pad = p2;
+ else
+ return PXE_BAD_OPTION;
+ }
+ else
+ return PXE_BAD_FORMAT;
+
+ p = q;
+ }
+ return 0;
+}
+
+/* provider */
+
+int
+px_find_combo(const char *name, PX_Combo **res)
+{
+ int err;
+ char *buf,
+ *s_cipher,
+ *s_pad;
+
+ PX_Combo *cx;
+
+ cx = palloc0(sizeof(*cx));
+ buf = pstrdup(name);
+
+ err = parse_cipher_name(buf, &s_cipher, &s_pad);
+ if (err)
+ {
+ pfree(buf);
+ pfree(cx);
+ return err;
+ }
+
+ err = px_find_cipher(s_cipher, &cx->cipher);
+ if (err)
+ goto err1;
+
+ if (s_pad != NULL)
+ {
+ if (strcmp(s_pad, "pkcs") == 0)
+ cx->padding = 1;
+ else if (strcmp(s_pad, "none") == 0)
+ cx->padding = 0;
+ else
+ goto err1;
+ }
+ else
+ cx->padding = 1;
+
+ cx->init = combo_init;
+ cx->encrypt = combo_encrypt;
+ cx->decrypt = combo_decrypt;
+ cx->encrypt_len = combo_encrypt_len;
+ cx->decrypt_len = combo_decrypt_len;
+ cx->free = combo_free;
+
+ pfree(buf);
+
+ *res = cx;
+
+ return 0;
+
+err1:
+ if (cx->cipher)
+ px_cipher_free(cx->cipher);
+ pfree(cx);
+ pfree(buf);
+ return PXE_NO_CIPHER;
+}