266 lines
5.7 KiB
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);
|
|
}
|