chiark / gitweb /
f9386541c8ff4be7524d32dd3c4606ee622b7351
[catacomb] / symm / rijndael-x86ish-aesni.S
1 /// -*- mode: asm; asm-comment-char: ?/ -*-
2 ///
3 /// AESNI-based implementation of Rijndael
4 ///
5 /// (c) 2015 Straylight/Edgeware
6 ///
7
8 ///----- Licensing notice ---------------------------------------------------
9 ///
10 /// This file is part of Catacomb.
11 ///
12 /// Catacomb is free software; you can redistribute it and/or modify
13 /// it under the terms of the GNU Library General Public License as
14 /// published by the Free Software Foundation; either version 2 of the
15 /// License, or (at your option) any later version.
16 ///
17 /// Catacomb is distributed in the hope that it will be useful,
18 /// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 /// GNU Library General Public License for more details.
21 ///
22 /// You should have received a copy of the GNU Library General Public
23 /// License along with Catacomb; if not, write to the Free
24 /// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 /// MA 02111-1307, USA.
26
27 ///--------------------------------------------------------------------------
28 /// External definitions.
29
30 #include "config.h"
31 #include "asm-common.h"
32
33         .globl  F(abort)
34         .globl  F(rijndael_rcon)
35
36 ///--------------------------------------------------------------------------
37 /// Local utilities.
38
39 // Magic constants for shuffling.
40 #define ROTL 0x93
41 #define ROT2 0x4e
42 #define ROTR 0x39
43
44 ///--------------------------------------------------------------------------
45 /// Main code.
46
47         .arch   .aes
48         .text
49
50 /// The AESNI instructions implement a little-endian version of AES, but
51 /// Catacomb's internal interface presents as big-endian so as to work better
52 /// with things like GCM.  We therefore maintain the round keys in
53 /// little-endian form, and have to end-swap blocks in and out.
54 ///
55 /// For added amusement, the AESNI instructions don't implement the
56 /// larger-block versions of Rijndael, so we have to end-swap the keys if
57 /// we're preparing for one of those.
58
59         // Useful constants.
60         .equ    maxrounds, 16           // maximum number of rounds
61         .equ    maxblksz, 32            // maximum block size, in bytes
62         .equ    kbufsz, maxblksz*(maxrounds + 1) // size of a key-schedule buffer
63
64         // Context structure.
65         .equ    nr, 0                   // number of rounds
66         .equ    w, nr + 4               // encryption key words
67         .equ    wi, w + kbufsz          // decryption key words
68
69 ///--------------------------------------------------------------------------
70 /// Key setup.
71
72 FUNC(rijndael_setup_x86ish_aesni)
73
74 #if CPUFAM_X86
75         // Arguments are on the stack.  We'll need to stack the caller's
76         // register veriables, but we'll manage.
77
78 #  define CTX ebp                       // context pointer
79 #  define BLKSZ [esp + 24]              // block size
80
81 #  define SI esi                        // source pointer
82 #  define DI edi                        // destination pointer
83
84 #  define KSZ ebx                       // key size
85 #  define KSZo ebx                      // ... as address offset
86 #  define NKW edx                       // total number of key words
87 #  define NKW_NEEDS_REFRESH 1           // ... needs recalculating
88 #  define RCON ecx                      // round constants table
89 #  define LIM edx                       // limit pointer
90 #  define LIMn edx                      // ... as integer offset from base
91
92 #  define NR ecx                        // number of rounds
93 #  define LRK eax                       // distance to last key
94 #  define LRKo eax                      // ... as address offset
95 #  define BLKOFF edx                    // block size in bytes
96 #  define BLKOFFo edx                   // ... as address offset
97
98         // Stack the caller's registers.
99         push    ebp
100         push    ebx
101         push    esi
102         push    edi
103
104         // Set up our own variables.
105         mov     CTX, [esp + 20]         // context base pointer
106         mov     SI, [esp + 28]          // key material
107         mov     KSZ, [esp + 32]         // key size, in words
108 #endif
109
110 #if CPUFAM_AMD64 && ABI_SYSV
111         // Arguments are in registers.  We have plenty, but, to be honest,
112         // the initial register allocation is a bit annoying.
113
114 #  define CTX r8                        // context pointer
115 #  define BLKSZ r9d                     // block size
116
117 #  define SI rsi                        // source pointer
118 #  define DI rdi                        // destination pointer
119
120 #  define KSZ edx                       // key size
121 #  define KSZo rdx                      // ... as address offset
122 #  define NKW r10d                      // total number of key words
123 #  define RCON rdi                      // round constants table
124 #  define LIMn ecx                      // limit pointer
125 #  define LIM rcx                       // ... as integer offset from base
126
127 #  define NR ecx                        // number of rounds
128 #  define LRK eax                       // distance to last key
129 #  define LRKo rax                      // ... as address offset
130 #  define BLKOFF r9d                    // block size in bytes
131 #  define BLKOFFo r9                    // ... as address offset
132
133         // Move arguments to more useful places.
134         mov     CTX, rdi                // context base pointer
135         mov     BLKSZ, esi              // block size in words
136         mov     SI, rdx                 // key material
137         mov     KSZ, ecx                // key size, in words
138 #endif
139
140 #if CPUFAM_AMD64 && ABI_WIN
141         // Arguments are in different registers, and they're a little tight.
142
143 #  define CTX r8                        // context pointer
144 #  define BLKSZ edx                     // block size
145
146 #  define SI rsi                        // source pointer
147 #  define DI rdi                        // destination pointer
148
149 #  define KSZ r9d                       // key size
150 #  define KSZo r9                       // ... as address offset
151 #  define NKW r10d                      // total number of key words
152 #  define RCON rdi                      // round constants table
153 #  define LIMn ecx                      // limit pointer
154 #  define LIM rcx                       // ... as integer offset from base
155
156 #  define NR ecx                        // number of rounds
157 #  define LRK eax                       // distance to last key
158 #  define LRKo rax                      // ... as address offset
159 #  define BLKOFF edx                    // block size in bytes
160 #  define BLKOFFo rdx                   // ... as address offset
161
162         // We'll need the index registers, which belong to the caller in this
163         // ABI.
164         push    rsi
165         push    rdi
166
167         // Move arguments to more useful places.
168         mov     SI, r8                  // key material
169         mov     CTX, rcx                // context base pointer
170 #endif
171
172         // The initial round key material is taken directly from the input
173         // key, so copy it over.
174 #if CPUFAM_AMD64 && ABI_SYSV
175         // We've been lucky.  We already have a copy of the context pointer
176         // in rdi, and the key size in ecx.
177         add     DI, w
178 #else
179         lea     DI, [CTX + w]
180         mov     ecx, KSZ
181 #endif
182         rep     movsd
183
184         // Find out other useful things.
185         mov     NKW, [CTX + nr]         // number of rounds
186         add     NKW, 1
187         imul    NKW, BLKSZ              // total key size in words
188 #if !NKW_NEEDS_REFRESH
189         // If we can't keep NKW for later, then we use the same register for
190         // it and LIM, so this move is unnecessary.
191         mov     LIMn, NKW
192 #endif
193         sub     LIMn, KSZ               // offset by the key size
194
195         // Find the round constants.
196         ldgot   ecx
197         leaext  RCON, F(rijndael_rcon), ecx
198
199         // Prepare for the main loop.
200         lea     SI, [CTX + w]
201         mov     eax, [SI + 4*KSZo - 4]  // most recent key word
202         lea     LIM, [SI + 4*LIM]       // limit, offset by one key expansion
203
204         // Main key expansion loop.  The first word of each key-length chunk
205         // needs special treatment.
206         //
207         // This is rather tedious because the Intel `AESKEYGENASSIST'
208         // instruction is very strangely shaped.  Firstly, it wants to
209         // operate on vast SSE registers, even though we're data-blocked from
210         // doing more than operation at a time unless we're doing two key
211         // schedules simultaneously -- and even then we can't do more than
212         // two, because the instruction ignores two of its input words
213         // entirely, and produces two different outputs for each of the other
214         // two.  And secondly it insists on taking the magic round constant
215         // as an immediate, so it's kind of annoying if you're not
216         // open-coding the whole thing.  It's much easier to leave that as
217         // zero and XOR in the round constant by hand.
218 9:      movd    xmm0, eax
219         pshufd  xmm0, xmm0, ROTR
220         aeskeygenassist xmm1, xmm0, 0
221         pshufd  xmm1, xmm1, ROTL
222         movd    eax, xmm1
223         xor     eax, [SI]
224         xor     al, [RCON]
225         inc     RCON
226         mov     [SI + 4*KSZo], eax
227         add     SI, 4
228         cmp     SI, LIM
229         jae     8f
230
231         // The next three words are simple...
232         xor     eax, [SI]
233         mov     [SI + 4*KSZo], eax
234         add     SI, 4
235         cmp     SI, LIM
236         jae     8f
237
238         // (Word 2...)
239         xor     eax, [SI]
240         mov     [SI + 4*KSZo], eax
241         add     SI, 4
242         cmp     SI, LIM
243         jae     8f
244
245         // (Word 3...)
246         xor     eax, [SI]
247         mov     [SI + 4*KSZo], eax
248         add     SI, 4
249         cmp     SI, LIM
250         jae     8f
251
252         // Word 4.  If the key is /more/ than 6 words long, then we must
253         // apply a substitution here.
254         cmp     KSZ, 5
255         jb      9b
256         cmp     KSZ, 7
257         jb      0f
258         movd    xmm0, eax
259         pshufd  xmm0, xmm0, ROTL
260         aeskeygenassist xmm1, xmm0, 0
261         movd    eax, xmm1
262 0:      xor     eax, [SI]
263         mov     [SI + 4*KSZo], eax
264         add     SI, 4
265         cmp     SI, LIM
266         jae     8f
267
268         // (Word 5...)
269         cmp     KSZ, 6
270         jb      9b
271         xor     eax, [SI]
272         mov     [SI + 4*KSZo], eax
273         add     SI, 4
274         cmp     SI, LIM
275         jae     8f
276
277         // (Word 6...)
278         cmp     KSZ, 7
279         jb      9b
280         xor     eax, [SI]
281         mov     [SI + 4*KSZo], eax
282         add     SI, 4
283         cmp     SI, LIM
284         jae     8f
285
286         // (Word 7...)
287         cmp     KSZ, 8
288         jb      9b
289         xor     eax, [SI]
290         mov     [SI + 4*KSZo], eax
291         add     SI, 4
292         cmp     SI, LIM
293         jae     8f
294
295         // Must be done by now.
296         jmp     9b
297
298         // Next job is to construct the decryption keys.  The keys for the
299         // first and last rounds don't need to be mangled, but the remaining
300         // ones do -- and they all need to be reordered too.
301         //
302         // The plan of action, then, is to copy the final encryption round's
303         // keys into place first, then to do each of the intermediate rounds
304         // in reverse order, and finally do the first round.
305         //
306         // Do all of the heavy lifting with SSE registers.  The order we're
307         // doing this in means that it's OK if we read or write too much, and
308         // there's easily enough buffer space for the over-enthusiastic reads
309         // and writes because the context has space for 32-byte blocks, which
310         // is our maximum and an exact fit for two SSE registers.
311 8:      mov     NR, [CTX + nr]          // number of rounds
312 #if NKW_NEEDS_REFRESH
313         mov     BLKOFF, BLKSZ
314         mov     LRK, NR
315         imul    LRK, BLKOFF
316 #else
317         // If we retain NKW, then BLKSZ and BLKOFF are the same register
318         // because we won't need the former again.
319         mov     LRK, NKW
320         sub     LRK, BLKSZ
321 #endif
322         lea     DI, [CTX + wi]
323         lea     SI, [CTX + w + 4*LRKo]  // last round's keys
324         shl     BLKOFF, 2               // block size (in bytes now)
325
326         // Copy the last encryption round's keys.
327         movdqu  xmm0, [SI]
328         movdqu  [DI], xmm0
329         cmp     BLKOFF, 16
330         jbe     9f
331         movdqu  xmm0, [SI + 16]
332         movdqu  [DI + 16], xmm0
333
334         // Update the loop variables and stop if we've finished.
335 9:      add     DI, BLKOFFo
336         sub     SI, BLKOFFo
337         sub     NR, 1
338         jbe     0f
339
340         // Do another middle round's keys...
341         movdqu  xmm0, [SI]
342         aesimc  xmm0, xmm0
343         movdqu  [DI], xmm0
344         cmp     BLKOFF, 16
345         jbe     9b
346         movdqu  xmm0, [SI + 16]
347         aesimc  xmm0, xmm0
348         movdqu  [DI + 16], xmm0
349         jmp     9b
350
351         // Finally do the first encryption round.
352 0:      movdqu  xmm0, [SI]
353         movdqu  [DI], xmm0
354         cmp     BLKOFF, 16
355         jbe     0f
356         movdqu  xmm0, [SI + 16]
357         movdqu  [DI + 16], xmm0
358
359         // If the block size is not exactly four words then we must end-swap
360         // everything.  We can use fancy SSE toys for this.
361 0:      cmp     BLKOFF, 16
362         je      0f
363
364         // Find the byte-reordering table.
365         ldgot   ecx
366         movdqa  xmm5, [INTADDR(endswap_tab, ecx)]
367
368 #if NKW_NEEDS_REFRESH
369         // Calculate the number of subkey words again.  (It's a good job
370         // we've got a fast multiplier.)
371         mov     NKW, [CTX + nr]
372         add     NKW, 1
373         imul    NKW, BLKSZ
374 #endif
375
376         // End-swap the encryption keys.
377         lea     SI, [CTX + w]
378         call    endswap_block
379
380         // And the decryption keys.
381         lea     SI, [CTX + wi]
382         call    endswap_block
383
384 0:      // All done.
385 #if CPUFAM_X86
386         pop     edi
387         pop     esi
388         pop     ebx
389         pop     ebp
390 #endif
391 #if CPUFAM_AMD64 && ABI_WIN
392         pop     rdi
393         pop     rsi
394 #endif
395         ret
396
397         .align  16
398 endswap_block:
399         // End-swap NKW words starting at SI.  The end-swapping table is
400         // already loaded into XMM5; and it's OK to work in 16-byte chunks.
401         mov     ecx, NKW
402 0:      movdqu  xmm1, [SI]
403         pshufb  xmm1, xmm5
404         movdqu  [SI], xmm1
405         add     SI, 16
406         sub     ecx, 4
407         ja      0b
408         ret
409
410 #undef CTX
411 #undef BLKSZ
412 #undef SI
413 #undef DI
414 #undef KSZ
415 #undef KSZo
416 #undef RCON
417 #undef LIMn
418 #undef LIM
419 #undef NR
420 #undef LRK
421 #undef LRKo
422 #undef BLKOFF
423 #undef BLKOFFo
424
425 ENDFUNC
426
427 ///--------------------------------------------------------------------------
428 /// Encrypting and decrypting blocks.
429
430 .macro  encdec  op, aes, koff
431   FUNC(rijndael_\op\()_x86ish_aesni)
432
433         // Find the magic endianness-swapping table.
434         ldgot   ecx
435         movdqa  xmm5, [INTADDR(endswap_tab, ecx)]
436
437 #if CPUFAM_X86
438         // Arguments come in on the stack, and need to be collected.  We
439         // don't have a shortage of registers.
440
441 #  define K ecx
442 #  define SRC edx
443 #  define DST edx
444 #  define NR eax
445
446         mov     K, [esp + 4]
447         mov     SRC, [esp + 8]
448 #endif
449
450 #if CPUFAM_AMD64 && ABI_SYSV
451         // Arguments come in registers.  All is good.
452
453 #  define K rdi
454 #  define SRC rsi
455 #  define DST rdx
456 #  define NR eax
457 #endif
458
459 #if CPUFAM_AMD64 && ABI_WIN
460         // Arguments come in different registers.
461
462 #  define K rcx
463 #  define SRC rdx
464 #  define DST r8
465 #  define NR eax
466 #endif
467
468         // Initial setup.
469         movdqu  xmm0, [SRC]
470         pshufb  xmm0, xmm5
471         mov     NR, [K + nr]
472         add     K, \koff
473
474         // Initial whitening.
475         movdqu  xmm1, [K]
476         add     K, 16
477         pxor    xmm0, xmm1
478
479         // Dispatch to the correct code.
480         cmp     NR, 10
481         je      10f
482         jb      bogus
483         cmp     NR, 14
484         je      14f
485         ja      bogus
486         cmp     NR, 12
487         je      12f
488         jb      11f
489         jmp     13f
490
491         .align  2
492
493         // 14 rounds...
494 14:     movdqu  xmm1, [K]
495         add     K, 16
496         \aes    xmm0, xmm1
497
498         // 13 rounds...
499 13:     movdqu  xmm1, [K]
500         add     K, 16
501         \aes    xmm0, xmm1
502
503         // 12 rounds...
504 12:     movdqu  xmm1, [K]
505         add     K, 16
506         \aes    xmm0, xmm1
507
508         // 11 rounds...
509 11:     movdqu  xmm1, [K]
510         add     K, 16
511         \aes    xmm0, xmm1
512
513         // 10 rounds...
514 10:     movdqu  xmm1, [K]
515         \aes    xmm0, xmm1
516
517         // 9 rounds...
518         movdqu  xmm1, [K + 16]
519         \aes    xmm0, xmm1
520
521         // 8 rounds...
522         movdqu  xmm1, [K + 32]
523         \aes    xmm0, xmm1
524
525         // 7 rounds...
526         movdqu  xmm1, [K + 48]
527         \aes    xmm0, xmm1
528
529         // 6 rounds...
530         movdqu  xmm1, [K + 64]
531         \aes    xmm0, xmm1
532
533         // 5 rounds...
534         movdqu  xmm1, [K + 80]
535         \aes    xmm0, xmm1
536
537         // 4 rounds...
538         movdqu  xmm1, [K + 96]
539         \aes    xmm0, xmm1
540
541         // 3 rounds...
542         movdqu  xmm1, [K + 112]
543         \aes    xmm0, xmm1
544
545         // 2 rounds...
546         movdqu  xmm1, [K + 128]
547         \aes    xmm0, xmm1
548
549         // Final round...
550         movdqu  xmm1, [K + 144]
551         \aes\()last xmm0, xmm1
552
553         // Unpermute the ciphertext block and store it.
554         pshufb  xmm0, xmm5
555 #if CPUFAM_X86
556         mov     DST, [esp + 12]
557 #endif
558         movdqu  [DST], xmm0
559
560         // And we're done.
561         ret
562
563 #undef K
564 #undef SRC
565 #undef DST
566 #undef NR
567
568   ENDFUNC
569 .endm
570
571         encdec  eblk, aesenc, w
572         encdec  dblk, aesdec, wi
573
574 ///--------------------------------------------------------------------------
575 /// Random utilities.
576
577         .align  16
578         // Abort the process because of a programming error.  Indirecting
579         // through this point serves several purposes: (a) by CALLing, rather
580         // than branching to, `abort', we can save the return address, which
581         // might at least provide a hint as to what went wrong; (b) we don't
582         // have conditional CALLs (and they'd be big anyway); and (c) we can
583         // write a HLT here as a backstop against `abort' being mad.
584 bogus:  callext F(abort)
585 0:      hlt
586         jmp     0b
587
588         gotaux  ecx
589
590 ///--------------------------------------------------------------------------
591 /// Data tables.
592
593         .align  16
594 endswap_tab:
595         .byte    3,  2,  1,  0
596         .byte    7,  6,  5,  4
597         .byte   11, 10,  9,  8
598         .byte   15, 14, 13, 12
599
600 ///----- That's all, folks --------------------------------------------------