SHA-512: Add AVX and AVX2 implementations for x86-64
[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 "ath.h"
30 #include "./cipher-internal.h"
31 #include "bufhelp.h"
32
33
34
35 gcry_err_code_t
36 _gcry_cipher_cbc_encrypt (gcry_cipher_hd_t c,
37                           unsigned char *outbuf, size_t outbuflen,
38                           const unsigned char *inbuf, size_t inbuflen)
39 {
40   size_t n;
41   unsigned char *ivp;
42   int i;
43   size_t blocksize = c->spec->blocksize;
44   gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
45   size_t nblocks = inbuflen / blocksize;
46   unsigned int burn, nburn;
47
48   if (outbuflen < ((c->flags & GCRY_CIPHER_CBC_MAC)? blocksize : inbuflen))
49     return GPG_ERR_BUFFER_TOO_SHORT;
50
51   if ((inbuflen % blocksize)
52       && !(inbuflen > blocksize
53            && (c->flags & GCRY_CIPHER_CBC_CTS)))
54     return GPG_ERR_INV_LENGTH;
55
56   burn = 0;
57
58   if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
59     {
60       if ((inbuflen % blocksize) == 0)
61         nblocks--;
62     }
63
64   if (c->bulk.cbc_enc)
65     {
66       c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks,
67                        (c->flags & GCRY_CIPHER_CBC_MAC));
68       inbuf  += nblocks * blocksize;
69       if (!(c->flags & GCRY_CIPHER_CBC_MAC))
70         outbuf += nblocks * blocksize;
71     }
72   else
73     {
74       ivp = c->u_iv.iv;
75
76       for (n=0; n < nblocks; n++ )
77         {
78           buf_xor (outbuf, inbuf, ivp, blocksize);
79           nburn = enc_fn ( &c->context.c, outbuf, outbuf );
80           burn = nburn > burn ? nburn : burn;
81           ivp = outbuf;
82           inbuf  += blocksize;
83           if (!(c->flags & GCRY_CIPHER_CBC_MAC))
84             outbuf += blocksize;
85         }
86
87       if (ivp != c->u_iv.iv)
88         buf_cpy (c->u_iv.iv, ivp, blocksize );
89     }
90
91   if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
92     {
93       /* We have to be careful here, since outbuf might be equal to
94          inbuf.  */
95       size_t restbytes;
96       unsigned char b;
97
98       if ((inbuflen % blocksize) == 0)
99         restbytes = blocksize;
100       else
101         restbytes = inbuflen % blocksize;
102
103       outbuf -= blocksize;
104       for (ivp = c->u_iv.iv, i = 0; i < restbytes; i++)
105         {
106           b = inbuf[i];
107           outbuf[blocksize + i] = outbuf[i];
108           outbuf[i] = b ^ *ivp++;
109         }
110       for (; i < blocksize; i++)
111         outbuf[i] = 0 ^ *ivp++;
112
113       nburn = enc_fn (&c->context.c, outbuf, outbuf);
114       burn = nburn > burn ? nburn : burn;
115       buf_cpy (c->u_iv.iv, outbuf, blocksize);
116     }
117
118   if (burn > 0)
119     _gcry_burn_stack (burn + 4 * sizeof(void *));
120
121   return 0;
122 }
123
124
125 gcry_err_code_t
126 _gcry_cipher_cbc_decrypt (gcry_cipher_hd_t c,
127                           unsigned char *outbuf, size_t outbuflen,
128                           const unsigned char *inbuf, size_t inbuflen)
129 {
130   size_t n;
131   int i;
132   size_t blocksize = c->spec->blocksize;
133   gcry_cipher_decrypt_t dec_fn = c->spec->decrypt;
134   size_t nblocks = inbuflen / blocksize;
135   unsigned int burn, nburn;
136
137   if (outbuflen < inbuflen)
138     return GPG_ERR_BUFFER_TOO_SHORT;
139
140   if ((inbuflen % blocksize)
141       && !(inbuflen > blocksize
142            && (c->flags & GCRY_CIPHER_CBC_CTS)))
143     return GPG_ERR_INV_LENGTH;
144
145   burn = 0;
146
147   if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
148     {
149       nblocks--;
150       if ((inbuflen % blocksize) == 0)
151         nblocks--;
152       buf_cpy (c->lastiv, c->u_iv.iv, blocksize);
153     }
154
155   if (c->bulk.cbc_dec)
156     {
157       c->bulk.cbc_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
158       inbuf  += nblocks * blocksize;
159       outbuf += nblocks * blocksize;
160     }
161   else
162     {
163       for (n=0; n < nblocks; n++ )
164         {
165           /* Because outbuf and inbuf might be the same, we must not overwrite
166              the original ciphertext block.  We use LASTIV as intermediate
167              storage here because it is not used otherwise.  */
168           nburn = dec_fn ( &c->context.c, c->lastiv, inbuf );
169           burn = nburn > burn ? nburn : burn;
170           buf_xor_n_copy_2(outbuf, c->lastiv, c->u_iv.iv, inbuf, blocksize);
171           inbuf  += blocksize;
172           outbuf += blocksize;
173         }
174     }
175
176   if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
177     {
178       size_t restbytes;
179
180       if ((inbuflen % blocksize) == 0)
181         restbytes = blocksize;
182       else
183         restbytes = inbuflen % blocksize;
184
185       buf_cpy (c->lastiv, c->u_iv.iv, blocksize );         /* Save Cn-2. */
186       buf_cpy (c->u_iv.iv, inbuf + blocksize, restbytes ); /* Save Cn. */
187
188       nburn = dec_fn ( &c->context.c, outbuf, inbuf );
189       burn = nburn > burn ? nburn : burn;
190       buf_xor(outbuf, outbuf, c->u_iv.iv, restbytes);
191
192       buf_cpy (outbuf + blocksize, outbuf, restbytes);
193       for(i=restbytes; i < blocksize; i++)
194         c->u_iv.iv[i] = outbuf[i];
195       nburn = dec_fn (&c->context.c, outbuf, c->u_iv.iv);
196       burn = nburn > burn ? nburn : burn;
197       buf_xor(outbuf, outbuf, c->lastiv, blocksize);
198       /* c->lastiv is now really lastlastiv, does this matter? */
199     }
200
201   if (burn > 0)
202     _gcry_burn_stack (burn + 4 * sizeof(void *));
203
204   return 0;
205 }