postgres/contrib/pgcrypto/pgp-cfb.c

266 lines
5.7 KiB
C

/*
* pgp-cfb.c
* Implements both normal and PGP-specific CFB mode.
*
* Copyright (c) 2005 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/pgp-cfb.c
*/
#include "postgres.h"
#include "pgp.h"
#include "px.h"
typedef int (*mix_data_t) (PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
struct PGP_CFB
{
PX_Cipher *ciph;
int block_size;
int pos;
int block_no;
int resync;
uint8 fr[PGP_MAX_BLOCK];
uint8 fre[PGP_MAX_BLOCK];
uint8 encbuf[PGP_MAX_BLOCK];
};
int
pgp_cfb_create(PGP_CFB **ctx_p, int algo, const uint8 *key, int key_len,
int resync, uint8 *iv)
{
int res;
PX_Cipher *ciph;
PGP_CFB *ctx;
res = pgp_load_cipher(algo, &ciph);
if (res < 0)
return res;
res = px_cipher_init(ciph, key, key_len, NULL);
if (res < 0)
{
px_cipher_free(ciph);
return res;
}
ctx = palloc0(sizeof(*ctx));
ctx->ciph = ciph;
ctx->block_size = px_cipher_block_size(ciph);
ctx->resync = resync;
if (iv)
memcpy(ctx->fr, iv, ctx->block_size);
*ctx_p = ctx;
return 0;
}
void
pgp_cfb_free(PGP_CFB *ctx)
{
px_cipher_free(ctx->ciph);
px_memset(ctx, 0, sizeof(*ctx));
pfree(ctx);
}
/*
* Data processing for normal CFB. (PGP_PKT_SYMENCRYPTED_DATA_MDC)
*/
static int
mix_encrypt_normal(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
{
int i;
for (i = ctx->pos; i < ctx->pos + len; i++)
*dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++);
ctx->pos += len;
return len;
}
static int
mix_decrypt_normal(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
{
int i;
for (i = ctx->pos; i < ctx->pos + len; i++)
{
ctx->encbuf[i] = *data++;
*dst++ = ctx->fre[i] ^ ctx->encbuf[i];
}
ctx->pos += len;
return len;
}
/*
* Data processing for old PGP CFB mode. (PGP_PKT_SYMENCRYPTED_DATA)
*
* The goal is to hide the horror from the rest of the code,
* thus its all concentrated here.
*/
static int
mix_encrypt_resync(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
{
int i,
n;
/* block #2 is 2 bytes long */
if (ctx->block_no == 2)
{
n = 2 - ctx->pos;
if (len < n)
n = len;
for (i = ctx->pos; i < ctx->pos + n; i++)
*dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++);
ctx->pos += n;
len -= n;
if (ctx->pos == 2)
{
memcpy(ctx->fr, ctx->encbuf + 2, ctx->block_size - 2);
memcpy(ctx->fr + ctx->block_size - 2, ctx->encbuf, 2);
ctx->pos = 0;
return n;
}
}
for (i = ctx->pos; i < ctx->pos + len; i++)
*dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++);
ctx->pos += len;
return len;
}
static int
mix_decrypt_resync(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
{
int i,
n;
/* block #2 is 2 bytes long */
if (ctx->block_no == 2)
{
n = 2 - ctx->pos;
if (len < n)
n = len;
for (i = ctx->pos; i < ctx->pos + n; i++)
{
ctx->encbuf[i] = *data++;
*dst++ = ctx->fre[i] ^ ctx->encbuf[i];
}
ctx->pos += n;
len -= n;
if (ctx->pos == 2)
{
memcpy(ctx->fr, ctx->encbuf + 2, ctx->block_size - 2);
memcpy(ctx->fr + ctx->block_size - 2, ctx->encbuf, 2);
ctx->pos = 0;
return n;
}
}
for (i = ctx->pos; i < ctx->pos + len; i++)
{
ctx->encbuf[i] = *data++;
*dst++ = ctx->fre[i] ^ ctx->encbuf[i];
}
ctx->pos += len;
return len;
}
/*
* common code for both encrypt and decrypt.
*/
static int
cfb_process(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst,
mix_data_t mix_data)
{
int n;
int res;
while (len > 0 && ctx->pos > 0)
{
n = ctx->block_size - ctx->pos;
if (len < n)
n = len;
n = mix_data(ctx, data, n, dst);
data += n;
dst += n;
len -= n;
if (ctx->pos == ctx->block_size)
{
memcpy(ctx->fr, ctx->encbuf, ctx->block_size);
ctx->pos = 0;
}
}
while (len > 0)
{
unsigned rlen;
px_cipher_encrypt(ctx->ciph, 0, ctx->fr, ctx->block_size, ctx->fre, &rlen);
if (ctx->block_no < 5)
ctx->block_no++;
n = ctx->block_size;
if (len < n)
n = len;
res = mix_data(ctx, data, n, dst);
data += res;
dst += res;
len -= res;
if (ctx->pos == ctx->block_size)
{
memcpy(ctx->fr, ctx->encbuf, ctx->block_size);
ctx->pos = 0;
}
}
return 0;
}
/*
* public interface
*/
int
pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
{
mix_data_t mix = ctx->resync ? mix_encrypt_resync : mix_encrypt_normal;
return cfb_process(ctx, data, len, dst, mix);
}
int
pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
{
mix_data_t mix = ctx->resync ? mix_decrypt_resync : mix_decrypt_normal;
return cfb_process(ctx, data, len, dst, mix);
}