More optimized CRC implementations
[libgcrypt.git] / cipher / cipher-cbc.c
1 /* cipher-cbc.c  - Generic CBC mode implementation
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
3  *               2005, 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
4  *
5  * This file is part of Libgcrypt.
6  *
7  * Libgcrypt is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser general Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * Libgcrypt is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26
27 #include "g10lib.h"
28 #include "cipher.h"
29 #include "./cipher-internal.h"
30 #include "bufhelp.h"
31
32
33
34 gcry_err_code_t
35 _gcry_cipher_cbc_encrypt (gcry_cipher_hd_t c,
36                           unsigned char *outbuf, size_t outbuflen,
37                           const unsigned char *inbuf, size_t inbuflen)
38 {
39   size_t n;
40   unsigned char *ivp;
41   int i;
42   size_t blocksize = c->spec->blocksize;
43   gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
44   size_t nblocks = inbuflen / blocksize;
45   unsigned int burn, nburn;
46
47   if (outbuflen < ((c->flags & GCRY_CIPHER_CBC_MAC)? blocksize : inbuflen))
48     return GPG_ERR_BUFFER_TOO_SHORT;
49
50   if ((inbuflen % blocksize)
51       && !(inbuflen > blocksize
52            && (c->flags & GCRY_CIPHER_CBC_CTS)))
53     return GPG_ERR_INV_LENGTH;
54
55   burn = 0;
56
57   if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
58     {
59       if ((inbuflen % blocksize) == 0)
60         nblocks--;
61     }
62
63   if (c->bulk.cbc_enc)
64     {
65       c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks,
66                        (c->flags & GCRY_CIPHER_CBC_MAC));
67       inbuf  += nblocks * blocksize;
68       if (!(c->flags & GCRY_CIPHER_CBC_MAC))
69         outbuf += nblocks * blocksize;
70     }
71   else
72     {
73       ivp = c->u_iv.iv;
74
75       for (n=0; n < nblocks; n++ )
76         {
77           buf_xor (outbuf, inbuf, ivp, blocksize);
78           nburn = enc_fn ( &c->context.c, outbuf, outbuf );
79           burn = nburn > burn ? nburn : burn;
80           ivp = outbuf;
81           inbuf  += blocksize;
82           if (!(c->flags & GCRY_CIPHER_CBC_MAC))
83             outbuf += blocksize;
84         }
85
86       if (ivp != c->u_iv.iv)
87         buf_cpy (c->u_iv.iv, ivp, blocksize );
88     }
89
90   if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
91     {
92       /* We have to be careful here, since outbuf might be equal to
93          inbuf.  */
94       size_t restbytes;
95       unsigned char b;
96
97       if ((inbuflen % blocksize) == 0)
98         restbytes = blocksize;
99       else
100         restbytes = inbuflen % blocksize;
101
102       outbuf -= blocksize;
103       for (ivp = c->u_iv.iv, i = 0; i < restbytes; i++)
104         {
105           b = inbuf[i];
106           outbuf[blocksize + i] = outbuf[i];
107           outbuf[i] = b ^ *ivp++;
108         }
109       for (; i < blocksize; i++)
110         outbuf[i] = 0 ^ *ivp++;
111
112       nburn = enc_fn (&c->context.c, outbuf, outbuf);
113       burn = nburn > burn ? nburn : burn;
114       buf_cpy (c->u_iv.iv, outbuf, blocksize);
115     }
116
117   if (burn > 0)
118     _gcry_burn_stack (burn + 4 * sizeof(void *));
119
120   return 0;
121 }
122
123
124 gcry_err_code_t
125 _gcry_cipher_cbc_decrypt (gcry_cipher_hd_t c,
126                           unsigned char *outbuf, size_t outbuflen,
127                           const unsigned char *inbuf, size_t inbuflen)
128 {
129   size_t n;
130   int i;
131   size_t blocksize = c->spec->blocksize;
132   gcry_cipher_decrypt_t dec_fn = c->spec->decrypt;
133   size_t nblocks = inbuflen / blocksize;
134   unsigned int burn, nburn;
135
136   if (outbuflen < inbuflen)
137     return GPG_ERR_BUFFER_TOO_SHORT;
138
139   if ((inbuflen % blocksize)
140       && !(inbuflen > blocksize
141            && (c->flags & GCRY_CIPHER_CBC_CTS)))
142     return GPG_ERR_INV_LENGTH;
143
144   burn = 0;
145
146   if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
147     {
148       nblocks--;
149       if ((inbuflen % blocksize) == 0)
150         nblocks--;
151       buf_cpy (c->lastiv, c->u_iv.iv, blocksize);
152     }
153
154   if (c->bulk.cbc_dec)
155     {
156       c->bulk.cbc_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
157       inbuf  += nblocks * blocksize;
158       outbuf += nblocks * blocksize;
159     }
160   else
161     {
162       for (n=0; n < nblocks; n++ )
163         {
164           /* Because outbuf and inbuf might be the same, we must not overwrite
165              the original ciphertext block.  We use LASTIV as intermediate
166              storage here because it is not used otherwise.  */
167           nburn = dec_fn ( &c->context.c, c->lastiv, inbuf );
168           burn = nburn > burn ? nburn : burn;
169           buf_xor_n_copy_2(outbuf, c->lastiv, c->u_iv.iv, inbuf, blocksize);
170           inbuf  += blocksize;
171           outbuf += blocksize;
172         }
173     }
174
175   if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
176     {
177       size_t restbytes;
178
179       if ((inbuflen % blocksize) == 0)
180         restbytes = blocksize;
181       else
182         restbytes = inbuflen % blocksize;
183
184       buf_cpy (c->lastiv, c->u_iv.iv, blocksize );         /* Save Cn-2. */
185       buf_cpy (c->u_iv.iv, inbuf + blocksize, restbytes ); /* Save Cn. */
186
187       nburn = dec_fn ( &c->context.c, outbuf, inbuf );
188       burn = nburn > burn ? nburn : burn;
189       buf_xor(outbuf, outbuf, c->u_iv.iv, restbytes);
190
191       buf_cpy (outbuf + blocksize, outbuf, restbytes);
192       for(i=restbytes; i < blocksize; i++)
193         c->u_iv.iv[i] = outbuf[i];
194       nburn = dec_fn (&c->context.c, outbuf, c->u_iv.iv);
195       burn = nburn > burn ? nburn : burn;
196       buf_xor(outbuf, outbuf, c->lastiv, blocksize);
197       /* c->lastiv is now really lastlastiv, does this matter? */
198     }
199
200   if (burn > 0)
201     _gcry_burn_stack (burn + 4 * sizeof(void *));
202
203   return 0;
204 }