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