Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions

px.c

/*
 * 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.
 *
 * $PostgreSQL: pgsql/contrib/pgcrypto/px.c,v 1.17 2007/11/15 21:14:31 momjian Exp $
 */

#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 initalized ?"},
      {PXE_HASH_UNUSABLE_FOR_HMAC, "This hash algorithm is unusable for HMAC"},
      {PXE_DEV_READ_ERROR, "Error reading from random device"},
      {PXE_OSSL_RAND_ERROR, "OpenSSL PRNG error"},
      {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, "No strong random source"},
      {PXE_DECRYPT_FAILED, "Decryption 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_NO_BIGNUM,
            "public-key functions disabled - "
      "pgcrypto needs OpenSSL for bignums"},
      {PXE_PGP_MATH_FAILED, "Math operation failed"},
      {PXE_PGP_SHORT_ELGAMAL_KEY, "Elgamal keys must be at least 1024 bits long"},
      {PXE_PGP_RSA_UNSUPPORTED, "pgcrypto does not support RSA keys"},
      {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"},

      /* fake this as PXE_PGP_CORRUPT_DATA */
      {PXE_MBUF_SHORT_READ, "Corrupt data"},

      {0, NULL},
};

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";
}


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    bs,
                        ks,
                        ivs;
      PX_Cipher  *c = cx->cipher;
      uint8    *ivbuf = NULL;
      uint8    *keybuf;

      bs = px_cipher_block_size(c);
      ks = px_cipher_key_size(c);

      ivs = px_cipher_iv_size(c);
      if (ivs > 0)
      {
            ivbuf = px_alloc(ivs);
            memset(ivbuf, 0, ivs);
            if (ivlen > ivs)
                  memcpy(ivbuf, iv, ivs);
            else
                  memcpy(ivbuf, iv, ivlen);
      }

      if (klen > ks)
            klen = ks;
      keybuf = px_alloc(ks);
      memset(keybuf, 0, ks);
      memcpy(keybuf, key, klen);

      err = px_cipher_init(c, keybuf, klen, ivbuf);

      if (ivbuf)
            px_free(ivbuf);
      px_free(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,
                        maxlen,
                        bpos,
                        i,
                        pad;

      PX_Cipher  *c = cx->cipher;

      bbuf = NULL;
      maxlen = *rlen;
      bs = px_cipher_block_size(c);

      /* encrypt */
      if (bs > 1)
      {
            bbuf = px_alloc(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)
            px_free(bbuf);

      return err;
}

static int
combo_decrypt(PX_Combo * cx, const uint8 *data, unsigned dlen,
                    uint8 *res, unsigned *rlen)
{
      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;
      px_cipher_decrypt(c, data, dlen, res);

      /* 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);
      memset(cx, 0, sizeof(*cx));
      px_free(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"))
                        *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 = px_alloc(sizeof(*cx));
      memset(cx, 0, sizeof(*cx));

      buf = px_alloc(strlen(name) + 1);
      strcpy(buf, name);

      err = parse_cipher_name(buf, &s_cipher, &s_pad);
      if (err)
      {
            px_free(buf);
            px_free(cx);
            return err;
      }

      err = px_find_cipher(s_cipher, &cx->cipher);
      if (err)
            goto err1;

      if (s_pad != NULL)
      {
            if (!strcmp(s_pad, "pkcs"))
                  cx->padding = 1;
            else if (!strcmp(s_pad, "none"))
                  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;

      px_free(buf);

      *res = cx;

      return 0;

err1:
      if (cx->cipher)
            px_cipher_free(cx->cipher);
      px_free(cx);
      px_free(buf);
      return PXE_NO_CIPHER;
}

Generated by  Doxygen 1.6.0   Back to index