SHA-1/SSSE3: Improve performance on large buffers
[libgcrypt.git] / cipher / cipher-cfb.c
1 /* cipher-cfb.c  - Generic CFB 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 "bufhelp.h"
31 #include "./cipher-internal.h"
32
33
34 gcry_err_code_t
35 _gcry_cipher_cfb_encrypt (gcry_cipher_hd_t c,
36                           unsigned char *outbuf, size_t outbuflen,
37                           const unsigned char *inbuf, size_t inbuflen)
38 {
39   unsigned char *ivp;
40   gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
41   size_t blocksize = c->spec->blocksize;
42   size_t blocksize_x_2 = blocksize + blocksize;
43   unsigned int burn, nburn;
44
45   if (outbuflen < inbuflen)
46     return GPG_ERR_BUFFER_TOO_SHORT;
47
48   if ( inbuflen <= c->unused )
49     {
50       /* Short enough to be encoded by the remaining XOR mask. */
51       /* XOR the input with the IV and store input into IV. */
52       ivp = c->u_iv.iv + blocksize - c->unused;
53       buf_xor_2dst(outbuf, ivp, inbuf, inbuflen);
54       c->unused -= inbuflen;
55       return 0;
56     }
57
58   burn = 0;
59
60   if ( c->unused )
61     {
62       /* XOR the input with the IV and store input into IV */
63       inbuflen -= c->unused;
64       ivp = c->u_iv.iv + blocksize - c->unused;
65       buf_xor_2dst(outbuf, ivp, inbuf, c->unused);
66       outbuf += c->unused;
67       inbuf += c->unused;
68       c->unused = 0;
69     }
70
71   /* Now we can process complete blocks.  We use a loop as long as we
72      have at least 2 blocks and use conditions for the rest.  This
73      also allows to use a bulk encryption function if available.  */
74   if (inbuflen >= blocksize_x_2 && c->bulk.cfb_enc)
75     {
76       size_t nblocks = inbuflen / blocksize;
77       c->bulk.cfb_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
78       outbuf += nblocks * blocksize;
79       inbuf  += nblocks * blocksize;
80       inbuflen -= nblocks * blocksize;
81     }
82   else
83     {
84       while ( inbuflen >= blocksize_x_2 )
85         {
86           /* Encrypt the IV. */
87           nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
88           burn = nburn > burn ? nburn : burn;
89           /* XOR the input with the IV and store input into IV.  */
90           buf_xor_2dst(outbuf, c->u_iv.iv, inbuf, blocksize);
91           outbuf += blocksize;
92           inbuf += blocksize;
93           inbuflen -= blocksize;
94         }
95     }
96
97   if ( inbuflen >= blocksize )
98     {
99       /* Save the current IV and then encrypt the IV. */
100       buf_cpy( c->lastiv, c->u_iv.iv, blocksize );
101       nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
102       burn = nburn > burn ? nburn : burn;
103       /* XOR the input with the IV and store input into IV */
104       buf_xor_2dst(outbuf, c->u_iv.iv, inbuf, blocksize);
105       outbuf += blocksize;
106       inbuf += blocksize;
107       inbuflen -= blocksize;
108     }
109   if ( inbuflen )
110     {
111       /* Save the current IV and then encrypt the IV. */
112       buf_cpy( c->lastiv, c->u_iv.iv, blocksize );
113       nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
114       burn = nburn > burn ? nburn : burn;
115       c->unused = blocksize;
116       /* Apply the XOR. */
117       c->unused -= inbuflen;
118       buf_xor_2dst(outbuf, c->u_iv.iv, inbuf, inbuflen);
119       outbuf += inbuflen;
120       inbuf += inbuflen;
121       inbuflen = 0;
122     }
123
124   if (burn > 0)
125     _gcry_burn_stack (burn + 4 * sizeof(void *));
126
127   return 0;
128 }
129
130
131 gcry_err_code_t
132 _gcry_cipher_cfb_decrypt (gcry_cipher_hd_t c,
133                           unsigned char *outbuf, size_t outbuflen,
134                           const unsigned char *inbuf, size_t inbuflen)
135 {
136   unsigned char *ivp;
137   gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
138   size_t blocksize = c->spec->blocksize;
139   size_t blocksize_x_2 = blocksize + blocksize;
140   unsigned int burn, nburn;
141
142   if (outbuflen < inbuflen)
143     return GPG_ERR_BUFFER_TOO_SHORT;
144
145   if (inbuflen <= c->unused)
146     {
147       /* Short enough to be encoded by the remaining XOR mask. */
148       /* XOR the input with the IV and store input into IV. */
149       ivp = c->u_iv.iv + blocksize - c->unused;
150       buf_xor_n_copy(outbuf, ivp, inbuf, inbuflen);
151       c->unused -= inbuflen;
152       return 0;
153     }
154
155   burn = 0;
156
157   if (c->unused)
158     {
159       /* XOR the input with the IV and store input into IV. */
160       inbuflen -= c->unused;
161       ivp = c->u_iv.iv + blocksize - c->unused;
162       buf_xor_n_copy(outbuf, ivp, inbuf, c->unused);
163       outbuf += c->unused;
164       inbuf += c->unused;
165       c->unused = 0;
166     }
167
168   /* Now we can process complete blocks.  We use a loop as long as we
169      have at least 2 blocks and use conditions for the rest.  This
170      also allows to use a bulk encryption function if available.  */
171   if (inbuflen >= blocksize_x_2 && c->bulk.cfb_dec)
172     {
173       size_t nblocks = inbuflen / blocksize;
174       c->bulk.cfb_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
175       outbuf += nblocks * blocksize;
176       inbuf  += nblocks * blocksize;
177       inbuflen -= nblocks * blocksize;
178     }
179   else
180     {
181       while (inbuflen >= blocksize_x_2 )
182         {
183           /* Encrypt the IV. */
184           nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
185           burn = nburn > burn ? nburn : burn;
186           /* XOR the input with the IV and store input into IV. */
187           buf_xor_n_copy(outbuf, c->u_iv.iv, inbuf, blocksize);
188           outbuf += blocksize;
189           inbuf += blocksize;
190           inbuflen -= blocksize;
191         }
192     }
193
194   if (inbuflen >= blocksize )
195     {
196       /* Save the current IV and then encrypt the IV. */
197       buf_cpy ( c->lastiv, c->u_iv.iv, blocksize);
198       nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
199       burn = nburn > burn ? nburn : burn;
200       /* XOR the input with the IV and store input into IV */
201       buf_xor_n_copy(outbuf, c->u_iv.iv, inbuf, blocksize);
202       outbuf += blocksize;
203       inbuf += blocksize;
204       inbuflen -= blocksize;
205     }
206
207   if (inbuflen)
208     {
209       /* Save the current IV and then encrypt the IV. */
210       buf_cpy ( c->lastiv, c->u_iv.iv, blocksize );
211       nburn = enc_fn ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
212       burn = nburn > burn ? nburn : burn;
213       c->unused = blocksize;
214       /* Apply the XOR. */
215       c->unused -= inbuflen;
216       buf_xor_n_copy(outbuf, c->u_iv.iv, inbuf, inbuflen);
217       outbuf += inbuflen;
218       inbuf += inbuflen;
219       inbuflen = 0;
220     }
221
222   if (burn > 0)
223     _gcry_burn_stack (burn + 4 * sizeof(void *));
224
225   return 0;
226 }