123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /* cipher-cbc.c - Generic CBC mode implementation
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
- * 2005, 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
- *
- * This file is part of Libgcrypt.
- *
- * Libgcrypt is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * Libgcrypt is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include "g10lib.h"
- #include "cipher.h"
- #include "./cipher-internal.h"
- #include "bufhelp.h"
- static inline unsigned int
- cbc_encrypt_inner(gcry_cipher_hd_t c, unsigned char *outbuf,
- const unsigned char *inbuf, size_t nblocks, size_t blocksize,
- int is_cbc_cmac)
- {
- unsigned int burn, nburn;
- size_t n;
- burn = 0;
- if (c->bulk.cbc_enc)
- {
- c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks,
- is_cbc_cmac);
- }
- else
- {
- gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
- unsigned char *ivp;
- ivp = c->u_iv.iv;
- for (n=0; n < nblocks; n++ )
- {
- cipher_block_xor (outbuf, inbuf, ivp, blocksize);
- nburn = enc_fn ( &c->context.c, outbuf, outbuf );
- burn = nburn > burn ? nburn : burn;
- ivp = outbuf;
- inbuf += blocksize;
- if (!is_cbc_cmac)
- outbuf += blocksize;
- }
- if (ivp != c->u_iv.iv)
- cipher_block_cpy (c->u_iv.iv, ivp, blocksize);
- }
- return burn;
- }
- gcry_err_code_t
- _gcry_cipher_cbc_encrypt (gcry_cipher_hd_t c,
- unsigned char *outbuf, size_t outbuflen,
- const unsigned char *inbuf, size_t inbuflen)
- {
- size_t blocksize_shift = _gcry_blocksize_shift(c);
- size_t blocksize = 1 << blocksize_shift;
- size_t blocksize_mask = blocksize - 1;
- size_t nblocks = inbuflen >> blocksize_shift;
- int is_cbc_cmac = !!(c->flags & GCRY_CIPHER_CBC_MAC);
- unsigned int burn;
- if (outbuflen < (is_cbc_cmac ? blocksize : inbuflen))
- return GPG_ERR_BUFFER_TOO_SHORT;
- if (inbuflen & blocksize_mask)
- return GPG_ERR_INV_LENGTH;
- burn = cbc_encrypt_inner(c, outbuf, inbuf, nblocks, blocksize, is_cbc_cmac);
- if (burn > 0)
- _gcry_burn_stack (burn + 4 * sizeof(void *));
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_cbc_cts_encrypt (gcry_cipher_hd_t c,
- unsigned char *outbuf, size_t outbuflen,
- const unsigned char *inbuf, size_t inbuflen)
- {
- size_t blocksize_shift = _gcry_blocksize_shift(c);
- size_t blocksize = 1 << blocksize_shift;
- size_t blocksize_mask = blocksize - 1;
- gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
- size_t nblocks = inbuflen >> blocksize_shift;
- unsigned int burn, nburn;
- unsigned char *ivp;
- int i;
- if (outbuflen < inbuflen)
- return GPG_ERR_BUFFER_TOO_SHORT;
- if ((inbuflen & blocksize_mask) && !(inbuflen > blocksize))
- return GPG_ERR_INV_LENGTH;
- burn = 0;
- if (inbuflen > blocksize)
- {
- if ((inbuflen & blocksize_mask) == 0)
- nblocks--;
- }
- burn = cbc_encrypt_inner(c, outbuf, inbuf, nblocks, blocksize, 0);
- inbuf += nblocks << blocksize_shift;
- outbuf += nblocks << blocksize_shift;
- if (inbuflen > blocksize)
- {
- /* We have to be careful here, since outbuf might be equal to
- inbuf. */
- size_t restbytes;
- unsigned char b;
- if ((inbuflen & blocksize_mask) == 0)
- restbytes = blocksize;
- else
- restbytes = inbuflen & blocksize_mask;
- outbuf -= blocksize;
- for (ivp = c->u_iv.iv, i = 0; i < restbytes; i++)
- {
- b = inbuf[i];
- outbuf[blocksize + i] = outbuf[i];
- outbuf[i] = b ^ *ivp++;
- }
- for (; i < blocksize; i++)
- outbuf[i] = 0 ^ *ivp++;
- nburn = enc_fn (&c->context.c, outbuf, outbuf);
- burn = nburn > burn ? nburn : burn;
- cipher_block_cpy (c->u_iv.iv, outbuf, blocksize);
- }
- if (burn > 0)
- _gcry_burn_stack (burn + 4 * sizeof(void *));
- return 0;
- }
- static inline unsigned int
- cbc_decrypt_inner(gcry_cipher_hd_t c, unsigned char *outbuf,
- const unsigned char *inbuf, size_t nblocks, size_t blocksize)
- {
- unsigned int burn, nburn;
- size_t n;
- burn = 0;
- if (c->bulk.cbc_dec)
- {
- c->bulk.cbc_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
- }
- else
- {
- gcry_cipher_decrypt_t dec_fn = c->spec->decrypt;
- for (n = 0; n < nblocks; n++)
- {
- /* Because outbuf and inbuf might be the same, we must not overwrite
- the original ciphertext block. We use LASTIV as intermediate
- storage here because it is not used otherwise. */
- nburn = dec_fn ( &c->context.c, c->lastiv, inbuf );
- burn = nburn > burn ? nburn : burn;
- cipher_block_xor_n_copy_2 (outbuf, c->lastiv, c->u_iv.iv, inbuf,
- blocksize);
- inbuf += blocksize;
- outbuf += blocksize;
- }
- }
- return burn;
- }
- gcry_err_code_t
- _gcry_cipher_cbc_decrypt (gcry_cipher_hd_t c,
- unsigned char *outbuf, size_t outbuflen,
- const unsigned char *inbuf, size_t inbuflen)
- {
- size_t blocksize_shift = _gcry_blocksize_shift(c);
- size_t blocksize = 1 << blocksize_shift;
- size_t blocksize_mask = blocksize - 1;
- size_t nblocks = inbuflen >> blocksize_shift;
- unsigned int burn;
- if (outbuflen < inbuflen)
- return GPG_ERR_BUFFER_TOO_SHORT;
- if (inbuflen & blocksize_mask)
- return GPG_ERR_INV_LENGTH;
- burn = cbc_decrypt_inner(c, outbuf, inbuf, nblocks, blocksize);
- if (burn > 0)
- _gcry_burn_stack (burn + 4 * sizeof(void *));
- return 0;
- }
- gcry_err_code_t
- _gcry_cipher_cbc_cts_decrypt (gcry_cipher_hd_t c,
- unsigned char *outbuf, size_t outbuflen,
- const unsigned char *inbuf, size_t inbuflen)
- {
- size_t blocksize_shift = _gcry_blocksize_shift(c);
- size_t blocksize = 1 << blocksize_shift;
- size_t blocksize_mask = blocksize - 1;
- gcry_cipher_decrypt_t dec_fn = c->spec->decrypt;
- size_t nblocks = inbuflen >> blocksize_shift;
- unsigned int burn, nburn;
- int i;
- if (outbuflen < inbuflen)
- return GPG_ERR_BUFFER_TOO_SHORT;
- if ((inbuflen & blocksize_mask) && !(inbuflen > blocksize))
- return GPG_ERR_INV_LENGTH;
- burn = 0;
- if (inbuflen > blocksize)
- {
- nblocks--;
- if ((inbuflen & blocksize_mask) == 0)
- nblocks--;
- cipher_block_cpy (c->lastiv, c->u_iv.iv, blocksize);
- }
- burn = cbc_decrypt_inner(c, outbuf, inbuf, nblocks, blocksize);
- inbuf += nblocks << blocksize_shift;
- outbuf += nblocks << blocksize_shift;
- if (inbuflen > blocksize)
- {
- size_t restbytes;
- if ((inbuflen & blocksize_mask) == 0)
- restbytes = blocksize;
- else
- restbytes = inbuflen & blocksize_mask;
- cipher_block_cpy (c->lastiv, c->u_iv.iv, blocksize ); /* Save Cn-2. */
- buf_cpy (c->u_iv.iv, inbuf + blocksize, restbytes ); /* Save Cn. */
- nburn = dec_fn ( &c->context.c, outbuf, inbuf );
- burn = nburn > burn ? nburn : burn;
- buf_xor(outbuf, outbuf, c->u_iv.iv, restbytes);
- buf_cpy (outbuf + blocksize, outbuf, restbytes);
- for(i=restbytes; i < blocksize; i++)
- c->u_iv.iv[i] = outbuf[i];
- nburn = dec_fn (&c->context.c, outbuf, c->u_iv.iv);
- burn = nburn > burn ? nburn : burn;
- cipher_block_xor(outbuf, outbuf, c->lastiv, blocksize);
- /* c->lastiv is now really lastlastiv, does this matter? */
- }
- if (burn > 0)
- _gcry_burn_stack (burn + 4 * sizeof(void *));
- return 0;
- }
|