| /* |
| * 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 "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); |
| } |