AES IGE mode speedup.
[openssl.git] / crypto / aes / aes_ige.c
1 /* crypto/aes/aes_ige.c -*- mode:C; c-file-style: "eay" -*- */
2 /* ====================================================================
3  * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer. 
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. All advertising materials mentioning features or use of this
18  *    software must display the following acknowledgment:
19  *    "This product includes software developed by the OpenSSL Project
20  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
21  *
22  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
23  *    endorse or promote products derived from this software without
24  *    prior written permission. For written permission, please contact
25  *    openssl-core@openssl.org.
26  *
27  * 5. Products derived from this software may not be called "OpenSSL"
28  *    nor may "OpenSSL" appear in their names without prior written
29  *    permission of the OpenSSL Project.
30  *
31  * 6. Redistributions of any form whatsoever must retain the following
32  *    acknowledgment:
33  *    "This product includes software developed by the OpenSSL Project
34  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
37  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
42  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
43  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
45  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
47  * OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  */
51
52 #include "cryptlib.h"
53
54 #include <openssl/aes.h>
55 #include "aes_locl.h"
56
57 /*
58 static void hexdump(FILE *f,const char *title,const unsigned char *s,int l)
59     {
60     int n=0;
61
62     fprintf(f,"%s",title);
63     for( ; n < l ; ++n)
64                 {
65                 if((n%16) == 0)
66                         fprintf(f,"\n%04x",n);
67                 fprintf(f," %02x",s[n]);
68                 }
69     fprintf(f,"\n");
70     }
71 */
72
73 #define N_WORDS (AES_BLOCK_SIZE / sizeof(unsigned long))
74 typedef struct {
75         unsigned long data[N_WORDS];
76 } aes_block_t;
77
78 // XXX: probably some better way to do this
79 #if defined(__i386__) || defined(__x86_64__)
80 #define UNALIGNED_MEMOPS_ARE_FAST 1
81 #endif
82
83 #ifdef UNALIGNED_MEMOPS_ARE_FAST
84 #define load_block(d, s)        (d) = *(const aes_block_t *)(s)
85 #define store_block(d, s)       *(aes_block_t *)(d) = (s)
86 #else
87 #define load_block(d, s)        memcpy((d).data, (s), AES_BLOCK_SIZE)
88 #define store_block(d, s)       memcpy((d), (s).data, AES_BLOCK_SIZE)
89 #endif
90
91 /* N.B. The IV for this mode is _twice_ the block size */
92
93 void AES_ige_encrypt(const unsigned char *in, unsigned char *out,
94                                          const unsigned long length, const AES_KEY *key,
95                                          unsigned char *ivec, const int enc)
96         {
97         unsigned long n;
98         unsigned long len;
99         aes_block_t tmp, tmp2;
100         aes_block_t iv;
101         aes_block_t iv2;
102
103         OPENSSL_assert(in && out && key && ivec);
104         OPENSSL_assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));
105         OPENSSL_assert((length%AES_BLOCK_SIZE) == 0);
106
107         len = length / AES_BLOCK_SIZE;
108         load_block(iv, ivec);
109         load_block(iv2, ivec + AES_BLOCK_SIZE);
110
111         if (AES_ENCRYPT == enc)
112                 {
113                 /* XXX: Do a separate case for when in != out (strictly should
114                    check for overlap, too) */
115                 while (len)
116                         {
117                         load_block(tmp, in);
118                         /*                      hexdump(stdout, "in", in, AES_BLOCK_SIZE); */
119                         /*                      hexdump(stdout, "iv", iv, AES_BLOCK_SIZE); */
120                         for(n=0 ; n < N_WORDS; ++n)
121                                 tmp2.data[n] = tmp.data[n] ^ iv.data[n];
122                         /*                      hexdump(stdout, "in ^ iv", out, AES_BLOCK_SIZE); */
123                         AES_encrypt((unsigned char *)tmp2.data, (unsigned char *)tmp2.data, key);
124                         /*                      hexdump(stdout,"enc", out, AES_BLOCK_SIZE); */
125                         /*                      hexdump(stdout,"iv2", iv2, AES_BLOCK_SIZE); */
126                         for(n=0 ; n < N_WORDS; ++n)
127                                 tmp2.data[n] ^= iv2.data[n];
128                         store_block(out, tmp2);
129                         /*                      hexdump(stdout,"out", out, AES_BLOCK_SIZE); */
130                         iv = tmp2;
131                         iv2 = tmp;
132                         --len;
133                         in += AES_BLOCK_SIZE;
134                         out += AES_BLOCK_SIZE;
135                         }
136                 memcpy(ivec, iv.data, AES_BLOCK_SIZE);
137                 memcpy(ivec + AES_BLOCK_SIZE, iv2.data, AES_BLOCK_SIZE);
138                 }
139         else
140                 {
141                 while (len)
142                         {
143                         load_block(tmp, in);
144                         tmp2 = tmp;
145                         /*                      hexdump(stdout, "in", in, AES_BLOCK_SIZE); */
146                         /*                      hexdump(stdout, "iv2", iv2, AES_BLOCK_SIZE); */
147                         for(n=0 ; n < N_WORDS; ++n)
148                                 tmp.data[n] ^= iv2.data[n];
149                         /*                      hexdump(stdout, "in ^ iv2", tmp, AES_BLOCK_SIZE); */
150                         AES_decrypt((unsigned char *)tmp.data, (unsigned char *)tmp.data, key);
151                         /*                      hexdump(stdout, "dec", out, AES_BLOCK_SIZE); */
152                         /*                      hexdump(stdout, "iv", iv, AES_BLOCK_SIZE); */
153                         for(n=0 ; n < N_WORDS; ++n)
154                                 tmp.data[n] ^= iv.data[n];
155                         store_block(out, tmp);
156                         /*                      hexdump(stdout, "out", out, AES_BLOCK_SIZE); */
157                         iv = tmp2;
158                         iv2 = tmp;
159                         --len;
160                         in += AES_BLOCK_SIZE;
161                         out += AES_BLOCK_SIZE;
162                         }
163                 memcpy(ivec, iv.data, AES_BLOCK_SIZE);
164                 memcpy(ivec + AES_BLOCK_SIZE, iv2.data, AES_BLOCK_SIZE);
165                 }
166         }
167
168 /*
169  * Note that its effectively impossible to do biIGE in anything other
170  * than a single pass, so no provision is made for chaining.
171  */
172
173 /* N.B. The IV for this mode is _four times_ the block size */
174
175 void AES_bi_ige_encrypt(const unsigned char *in, unsigned char *out,
176                                                 const unsigned long length, const AES_KEY *key,
177                                                 const AES_KEY *key2, const unsigned char *ivec,
178                                                 const int enc)
179         {
180         unsigned long n;
181         unsigned long len = length;
182         unsigned char tmp[AES_BLOCK_SIZE];
183         unsigned char tmp2[AES_BLOCK_SIZE];
184         unsigned char tmp3[AES_BLOCK_SIZE];
185         unsigned char prev[AES_BLOCK_SIZE];
186         const unsigned char *iv;
187         const unsigned char *iv2;
188
189         OPENSSL_assert(in && out && key && ivec);
190         OPENSSL_assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));
191         OPENSSL_assert((length%AES_BLOCK_SIZE) == 0);
192
193         if (AES_ENCRYPT == enc)
194                 {
195                 /* XXX: Do a separate case for when in != out (strictly should
196                    check for overlap, too) */
197
198                 /* First the forward pass */ 
199                 iv = ivec;
200                 iv2 = ivec + AES_BLOCK_SIZE;
201                 while (len >= AES_BLOCK_SIZE)
202                         {
203                         /*                      hexdump(stdout, "in", in, AES_BLOCK_SIZE); */
204                         /*                      hexdump(stdout, "iv", iv, AES_BLOCK_SIZE); */
205                         for(n=0 ; n < AES_BLOCK_SIZE ; ++n)
206                                 out[n] = in[n] ^ iv[n];
207                         /*                      hexdump(stdout, "in ^ iv", out, AES_BLOCK_SIZE); */
208                         AES_encrypt(out, out, key);
209                         /*                      hexdump(stdout,"enc", out, AES_BLOCK_SIZE); */
210                         /*                      hexdump(stdout,"iv2", iv2, AES_BLOCK_SIZE); */
211                         for(n=0 ; n < AES_BLOCK_SIZE ; ++n)
212                                 out[n] ^= iv2[n];
213                         /*                      hexdump(stdout,"out", out, AES_BLOCK_SIZE); */
214                         iv = out;
215                         memcpy(prev, in, AES_BLOCK_SIZE);
216                         iv2 = prev;
217                         len -= AES_BLOCK_SIZE;
218                         in += AES_BLOCK_SIZE;
219                         out += AES_BLOCK_SIZE;
220                         }
221
222                 /* And now backwards */
223                 iv = ivec + AES_BLOCK_SIZE*2;
224                 iv2 = ivec + AES_BLOCK_SIZE*3;
225                 len = length;
226                 while(len >= AES_BLOCK_SIZE)
227                         {
228                         out -= AES_BLOCK_SIZE;
229                         /*                      hexdump(stdout, "intermediate", out, AES_BLOCK_SIZE); */
230                         /*                      hexdump(stdout, "iv", iv, AES_BLOCK_SIZE); */
231                         /* XXX: reduce copies by alternating between buffers */
232                         memcpy(tmp, out, AES_BLOCK_SIZE);
233                         for(n=0 ; n < AES_BLOCK_SIZE ; ++n)
234                                 out[n] ^= iv[n];
235                         /*                      hexdump(stdout, "out ^ iv", out, AES_BLOCK_SIZE); */
236                         AES_encrypt(out, out, key);
237                         /*                      hexdump(stdout,"enc", out, AES_BLOCK_SIZE); */
238                         /*                      hexdump(stdout,"iv2", iv2, AES_BLOCK_SIZE); */
239                         for(n=0 ; n < AES_BLOCK_SIZE ; ++n)
240                                 out[n] ^= iv2[n];
241                         /*                      hexdump(stdout,"out", out, AES_BLOCK_SIZE); */
242                         iv = out;
243                         memcpy(prev, tmp, AES_BLOCK_SIZE);
244                         iv2 = prev;
245                         len -= AES_BLOCK_SIZE;
246                         }
247                 }
248         else
249                 {
250                 /* First backwards */
251                 iv = ivec + AES_BLOCK_SIZE*2;
252                 iv2 = ivec + AES_BLOCK_SIZE*3;
253                 in += length;
254                 out += length;
255                 while (len >= AES_BLOCK_SIZE)
256                         {
257                         in -= AES_BLOCK_SIZE;
258                         out -= AES_BLOCK_SIZE;
259                         memcpy(tmp, in, AES_BLOCK_SIZE);
260                         memcpy(tmp2, in, AES_BLOCK_SIZE);
261                         /*                      hexdump(stdout, "in", in, AES_BLOCK_SIZE); */
262                         /*                      hexdump(stdout, "iv2", iv2, AES_BLOCK_SIZE); */
263                         for(n=0 ; n < AES_BLOCK_SIZE ; ++n)
264                                 tmp[n] ^= iv2[n];
265                         /*                      hexdump(stdout, "in ^ iv2", tmp, AES_BLOCK_SIZE); */
266                         AES_decrypt(tmp, out, key);
267                         /*                      hexdump(stdout, "dec", out, AES_BLOCK_SIZE); */
268                         /*                      hexdump(stdout, "iv", iv, AES_BLOCK_SIZE); */
269                         for(n=0 ; n < AES_BLOCK_SIZE ; ++n)
270                                 out[n] ^= iv[n];
271                         /*                      hexdump(stdout, "out", out, AES_BLOCK_SIZE); */
272                         memcpy(tmp3, tmp2, AES_BLOCK_SIZE);
273                         iv = tmp3;
274                         iv2 = out;
275                         len -= AES_BLOCK_SIZE;
276                         }
277
278                 /* And now forwards */
279                 iv = ivec;
280                 iv2 = ivec + AES_BLOCK_SIZE;
281                 len = length;
282                 while (len >= AES_BLOCK_SIZE)
283                         {
284                         memcpy(tmp, out, AES_BLOCK_SIZE);
285                         memcpy(tmp2, out, AES_BLOCK_SIZE);
286                         /*                      hexdump(stdout, "intermediate", out, AES_BLOCK_SIZE); */
287                         /*                      hexdump(stdout, "iv2", iv2, AES_BLOCK_SIZE); */
288                         for(n=0 ; n < AES_BLOCK_SIZE ; ++n)
289                                 tmp[n] ^= iv2[n];
290                         /*                      hexdump(stdout, "out ^ iv2", tmp, AES_BLOCK_SIZE); */
291                         AES_decrypt(tmp, out, key);
292                         /*                      hexdump(stdout, "dec", out, AES_BLOCK_SIZE); */
293                         /*                      hexdump(stdout, "iv", ivec, AES_BLOCK_SIZE); */
294                         for(n=0 ; n < AES_BLOCK_SIZE ; ++n)
295                                 out[n] ^= iv[n];
296                         /*                      hexdump(stdout, "out", out, AES_BLOCK_SIZE); */
297                         memcpy(tmp3, tmp2, AES_BLOCK_SIZE);
298                         iv = tmp3;
299                         iv2 = out;
300                         len -= AES_BLOCK_SIZE;
301                         in += AES_BLOCK_SIZE;
302                         out += AES_BLOCK_SIZE;
303                         }
304
305                 }
306         }