mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			266 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			5.8 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.
 | 
						|
 *
 | 
						|
 * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-cfb.c,v 1.3 2005/10/15 02:49:06 momjian Exp $
 | 
						|
 */
 | 
						|
 | 
						|
#include "postgres.h"
 | 
						|
 | 
						|
#include "mbuf.h"
 | 
						|
#include "px.h"
 | 
						|
#include "pgp.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 = px_alloc(sizeof(*ctx));
 | 
						|
	memset(ctx, 0, 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);
 | 
						|
	memset(ctx, 0, sizeof(*ctx));
 | 
						|
	px_free(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)
 | 
						|
	{
 | 
						|
		px_cipher_encrypt(ctx->ciph, ctx->fr, ctx->block_size, ctx->fre);
 | 
						|
		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);
 | 
						|
}
 |