diff options
Diffstat (limited to 'contrib/pgcrypto/px.c')
-rw-r--r-- | contrib/pgcrypto/px.c | 460 |
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; +} |