Crypto++
panama.cpp
1 // panama.cpp - written and placed in the public domain by Wei Dai
2 
3 // use "cl /EP /P /DCRYPTOPP_GENERATE_X64_MASM panama.cpp" to generate MASM code
4 
5 #include "pch.h"
6 
7 #ifndef CRYPTOPP_GENERATE_X64_MASM
8 
9 #include "panama.h"
10 #include "misc.h"
11 #include "cpu.h"
12 
13 NAMESPACE_BEGIN(CryptoPP)
14 
15 template <class B>
16 void Panama<B>::Reset()
17 {
18  memset(m_state, 0, m_state.SizeInBytes());
19 #if CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE
20  m_state[17] = HasSSSE3();
21 #endif
22 }
23 
24 #endif // #ifndef CRYPTOPP_GENERATE_X64_MASM
25 
26 #ifdef CRYPTOPP_X64_MASM_AVAILABLE
27 extern "C" {
28 void Panama_SSE2_Pull(size_t count, word32 *state, word32 *z, const word32 *y);
29 }
30 #elif CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE
31 
32 #ifdef CRYPTOPP_GENERATE_X64_MASM
33  Panama_SSE2_Pull PROC FRAME
34  rex_push_reg rdi
35  alloc_stack(2*16)
36  save_xmm128 xmm6, 0h
37  save_xmm128 xmm7, 10h
38  .endprolog
39 #else
40 #pragma warning(disable: 4731) // frame pointer register 'ebp' modified by inline assembly code
41 void CRYPTOPP_NOINLINE Panama_SSE2_Pull(size_t count, word32 *state, word32 *z, const word32 *y)
42 {
43 #ifdef CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY
44  asm __volatile__
45  (
46  ".intel_syntax noprefix;"
47  AS_PUSH_IF86( bx)
48 #else
49  AS2( mov AS_REG_1, count)
50  AS2( mov AS_REG_2, state)
51  AS2( mov AS_REG_3, z)
52  AS2( mov AS_REG_4, y)
53 #endif
54 #endif // #ifdef CRYPTOPP_GENERATE_X64_MASM
55 
56 #if CRYPTOPP_BOOL_X86
57  #define REG_loopEnd [esp]
58 #elif defined(CRYPTOPP_GENERATE_X64_MASM)
59  #define REG_loopEnd rdi
60 #else
61  #define REG_loopEnd r8
62 #endif
63 
64  AS2( shl AS_REG_1, 5)
65  ASJ( jz, 5, f)
66  AS2( mov AS_REG_6d, [AS_REG_2+4*17])
67  AS2( add AS_REG_1, AS_REG_6)
68 
69  #if CRYPTOPP_BOOL_X64
70  AS2( mov REG_loopEnd, AS_REG_1)
71  #else
72  AS1( push ebp)
73  AS1( push AS_REG_1)
74  #endif
75 
76  AS2( movdqa xmm0, XMMWORD_PTR [AS_REG_2+0*16])
77  AS2( movdqa xmm1, XMMWORD_PTR [AS_REG_2+1*16])
78  AS2( movdqa xmm2, XMMWORD_PTR [AS_REG_2+2*16])
79  AS2( movdqa xmm3, XMMWORD_PTR [AS_REG_2+3*16])
80  AS2( mov eax, dword ptr [AS_REG_2+4*16])
81 
82  ASL(4)
83  // gamma and pi
84 #if CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE
85  AS2( test AS_REG_6, 1)
86  ASJ( jnz, 6, f)
87 #endif
88  AS2( movdqa xmm6, xmm2)
89  AS2( movss xmm6, xmm3)
90  ASS( pshufd xmm5, xmm6, 0, 3, 2, 1)
91  AS2( movd xmm6, eax)
92  AS2( movdqa xmm7, xmm3)
93  AS2( movss xmm7, xmm6)
94  ASS( pshufd xmm6, xmm7, 0, 3, 2, 1)
95 #if CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE
96  ASJ( jmp, 7, f)
97  ASL(6)
98  AS2( movdqa xmm5, xmm3)
99  AS3( palignr xmm5, xmm2, 4)
100  AS2( movd xmm6, eax)
101  AS3( palignr xmm6, xmm3, 4)
102  ASL(7)
103 #endif
104 
105  AS2( movd AS_REG_1d, xmm2)
106  AS1( not AS_REG_1d)
107  AS2( movd AS_REG_7d, xmm3)
108  AS2( or AS_REG_1d, AS_REG_7d)
109  AS2( xor eax, AS_REG_1d)
110 
111 #define SSE2_Index(i) ASM_MOD(((i)*13+16), 17)
112 
113 #define pi(i) \
114  AS2( movd AS_REG_1d, xmm7)\
115  AS2( rol AS_REG_1d, ASM_MOD((ASM_MOD(5*i,17)*(ASM_MOD(5*i,17)+1)/2), 32))\
116  AS2( mov [AS_REG_2+SSE2_Index(ASM_MOD(5*(i), 17))*4], AS_REG_1d)
117 
118 #define pi4(x, y, z, a, b, c, d) \
119  AS2( pcmpeqb xmm7, xmm7)\
120  AS2( pxor xmm7, x)\
121  AS2( por xmm7, y)\
122  AS2( pxor xmm7, z)\
123  pi(a)\
124  ASS( pshuflw xmm7, xmm7, 1, 0, 3, 2)\
125  pi(b)\
126  AS2( punpckhqdq xmm7, xmm7)\
127  pi(c)\
128  ASS( pshuflw xmm7, xmm7, 1, 0, 3, 2)\
129  pi(d)
130 
131  pi4(xmm1, xmm2, xmm3, 1, 5, 9, 13)
132  pi4(xmm0, xmm1, xmm2, 2, 6, 10, 14)
133  pi4(xmm6, xmm0, xmm1, 3, 7, 11, 15)
134  pi4(xmm5, xmm6, xmm0, 4, 8, 12, 16)
135 
136  // output keystream and update buffer here to hide partial memory stalls between pi and theta
137  AS2( movdqa xmm4, xmm3)
138  AS2( punpcklqdq xmm3, xmm2) // 1 5 2 6
139  AS2( punpckhdq xmm4, xmm2) // 9 10 13 14
140  AS2( movdqa xmm2, xmm1)
141  AS2( punpcklqdq xmm1, xmm0) // 3 7 4 8
142  AS2( punpckhdq xmm2, xmm0) // 11 12 15 16
143 
144  // keystream
145  AS2( test AS_REG_3, AS_REG_3)
146  ASJ( jz, 0, f)
147  AS2( movdqa xmm6, xmm4)
148  AS2( punpcklqdq xmm4, xmm2)
149  AS2( punpckhqdq xmm6, xmm2)
150  AS2( test AS_REG_4, 15)
151  ASJ( jnz, 2, f)
152  AS2( test AS_REG_4, AS_REG_4)
153  ASJ( jz, 1, f)
154  AS2( pxor xmm4, [AS_REG_4])
155  AS2( pxor xmm6, [AS_REG_4+16])
156  AS2( add AS_REG_4, 32)
157  ASJ( jmp, 1, f)
158  ASL(2)
159  AS2( movdqu xmm0, [AS_REG_4])
160  AS2( movdqu xmm2, [AS_REG_4+16])
161  AS2( pxor xmm4, xmm0)
162  AS2( pxor xmm6, xmm2)
163  AS2( add AS_REG_4, 32)
164  ASL(1)
165  AS2( test AS_REG_3, 15)
166  ASJ( jnz, 3, f)
167  AS2( movdqa XMMWORD_PTR [AS_REG_3], xmm4)
168  AS2( movdqa XMMWORD_PTR [AS_REG_3+16], xmm6)
169  AS2( add AS_REG_3, 32)
170  ASJ( jmp, 0, f)
171  ASL(3)
172  AS2( movdqu XMMWORD_PTR [AS_REG_3], xmm4)
173  AS2( movdqu XMMWORD_PTR [AS_REG_3+16], xmm6)
174  AS2( add AS_REG_3, 32)
175  ASL(0)
176 
177  // buffer update
178  AS2( lea AS_REG_1, [AS_REG_6 + 32])
179  AS2( and AS_REG_1, 31*32)
180  AS2( lea AS_REG_7, [AS_REG_6 + (32-24)*32])
181  AS2( and AS_REG_7, 31*32)
182 
183  AS2( movdqa xmm0, XMMWORD_PTR [AS_REG_2+20*4+AS_REG_1+0*8])
184  AS2( pxor xmm3, xmm0)
185  ASS( pshufd xmm0, xmm0, 2, 3, 0, 1)
186  AS2( movdqa XMMWORD_PTR [AS_REG_2+20*4+AS_REG_1+0*8], xmm3)
187  AS2( pxor xmm0, XMMWORD_PTR [AS_REG_2+20*4+AS_REG_7+2*8])
188  AS2( movdqa XMMWORD_PTR [AS_REG_2+20*4+AS_REG_7+2*8], xmm0)
189 
190  AS2( movdqa xmm4, XMMWORD_PTR [AS_REG_2+20*4+AS_REG_1+2*8])
191  AS2( pxor xmm1, xmm4)
192  AS2( movdqa XMMWORD_PTR [AS_REG_2+20*4+AS_REG_1+2*8], xmm1)
193  AS2( pxor xmm4, XMMWORD_PTR [AS_REG_2+20*4+AS_REG_7+0*8])
194  AS2( movdqa XMMWORD_PTR [AS_REG_2+20*4+AS_REG_7+0*8], xmm4)
195 
196  // theta
197  AS2( movdqa xmm3, XMMWORD_PTR [AS_REG_2+3*16])
198  AS2( movdqa xmm2, XMMWORD_PTR [AS_REG_2+2*16])
199  AS2( movdqa xmm1, XMMWORD_PTR [AS_REG_2+1*16])
200  AS2( movdqa xmm0, XMMWORD_PTR [AS_REG_2+0*16])
201 
202 #if CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE
203  AS2( test AS_REG_6, 1)
204  ASJ( jnz, 8, f)
205 #endif
206  AS2( movd xmm6, eax)
207  AS2( movdqa xmm7, xmm3)
208  AS2( movss xmm7, xmm6)
209  AS2( movdqa xmm6, xmm2)
210  AS2( movss xmm6, xmm3)
211  AS2( movdqa xmm5, xmm1)
212  AS2( movss xmm5, xmm2)
213  AS2( movdqa xmm4, xmm0)
214  AS2( movss xmm4, xmm1)
215  ASS( pshufd xmm7, xmm7, 0, 3, 2, 1)
216  ASS( pshufd xmm6, xmm6, 0, 3, 2, 1)
217  ASS( pshufd xmm5, xmm5, 0, 3, 2, 1)
218  ASS( pshufd xmm4, xmm4, 0, 3, 2, 1)
219 #if CRYPTOPP_BOOL_SSSE3_ASM_AVAILABLE
220  ASJ( jmp, 9, f)
221  ASL(8)
222  AS2( movd xmm7, eax)
223  AS3( palignr xmm7, xmm3, 4)
224  AS2( movq xmm6, xmm3)
225  AS3( palignr xmm6, xmm2, 4)
226  AS2( movq xmm5, xmm2)
227  AS3( palignr xmm5, xmm1, 4)
228  AS2( movq xmm4, xmm1)
229  AS3( palignr xmm4, xmm0, 4)
230  ASL(9)
231 #endif
232 
233  AS2( xor eax, 1)
234  AS2( movd AS_REG_1d, xmm0)
235  AS2( xor eax, AS_REG_1d)
236  AS2( movd AS_REG_1d, xmm3)
237  AS2( xor eax, AS_REG_1d)
238 
239  AS2( pxor xmm3, xmm2)
240  AS2( pxor xmm2, xmm1)
241  AS2( pxor xmm1, xmm0)
242  AS2( pxor xmm0, xmm7)
243  AS2( pxor xmm3, xmm7)
244  AS2( pxor xmm2, xmm6)
245  AS2( pxor xmm1, xmm5)
246  AS2( pxor xmm0, xmm4)
247 
248  // sigma
249  AS2( lea AS_REG_1, [AS_REG_6 + (32-4)*32])
250  AS2( and AS_REG_1, 31*32)
251  AS2( lea AS_REG_7, [AS_REG_6 + 16*32])
252  AS2( and AS_REG_7, 31*32)
253 
254  AS2( movdqa xmm4, XMMWORD_PTR [AS_REG_2+20*4+AS_REG_1+0*16])
255  AS2( movdqa xmm5, XMMWORD_PTR [AS_REG_2+20*4+AS_REG_7+0*16])
256  AS2( movdqa xmm6, xmm4)
257  AS2( punpcklqdq xmm4, xmm5)
258  AS2( punpckhqdq xmm6, xmm5)
259  AS2( pxor xmm3, xmm4)
260  AS2( pxor xmm2, xmm6)
261 
262  AS2( movdqa xmm4, XMMWORD_PTR [AS_REG_2+20*4+AS_REG_1+1*16])
263  AS2( movdqa xmm5, XMMWORD_PTR [AS_REG_2+20*4+AS_REG_7+1*16])
264  AS2( movdqa xmm6, xmm4)
265  AS2( punpcklqdq xmm4, xmm5)
266  AS2( punpckhqdq xmm6, xmm5)
267  AS2( pxor xmm1, xmm4)
268  AS2( pxor xmm0, xmm6)
269 
270  // loop
271  AS2( add AS_REG_6, 32)
272  AS2( cmp AS_REG_6, REG_loopEnd)
273  ASJ( jne, 4, b)
274 
275  // save state
276  AS2( mov [AS_REG_2+4*16], eax)
277  AS2( movdqa XMMWORD_PTR [AS_REG_2+3*16], xmm3)
278  AS2( movdqa XMMWORD_PTR [AS_REG_2+2*16], xmm2)
279  AS2( movdqa XMMWORD_PTR [AS_REG_2+1*16], xmm1)
280  AS2( movdqa XMMWORD_PTR [AS_REG_2+0*16], xmm0)
281 
282  #if CRYPTOPP_BOOL_X86
283  AS2( add esp, 4)
284  AS1( pop ebp)
285  #endif
286  ASL(5)
287 
288 #ifdef CRYPTOPP_GNU_STYLE_INLINE_ASSEMBLY
289  AS_POP_IF86( bx)
290  ".att_syntax prefix;"
291  :
292  #if CRYPTOPP_BOOL_X64
293  : "D" (count), "S" (state), "d" (z), "c" (y)
294  : "%r8", "%r9", "r10", "%eax", "memory", "cc", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7"
295  #else
296  : "c" (count), "d" (state), "S" (z), "D" (y)
297  : "%eax", "memory", "cc"
298  #endif
299  );
300 #endif
301 #ifdef CRYPTOPP_GENERATE_X64_MASM
302  movdqa xmm6, [rsp + 0h]
303  movdqa xmm7, [rsp + 10h]
304  add rsp, 2*16
305  pop rdi
306  ret
307  Panama_SSE2_Pull ENDP
308 #else
309 }
310 #endif
311 #endif // #ifdef CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE
312 
313 #ifndef CRYPTOPP_GENERATE_X64_MASM
314 
315 template <class B>
316 void Panama<B>::Iterate(size_t count, const word32 *p, word32 *z, const word32 *y)
317 {
318  word32 bstart = m_state[17];
319  word32 *const aPtr = m_state;
320  word32 cPtr[17];
321 
322 #define bPtr ((byte *)(aPtr+20))
323 
324 // reorder the state for SSE2
325 // a and c: 4 8 12 16 | 3 7 11 15 | 2 6 10 14 | 1 5 9 13 | 0
326 // xmm0 xmm1 xmm2 xmm3 eax
327 #define a(i) aPtr[((i)*13+16) % 17] // 13 is inverse of 4 mod 17
328 #define c(i) cPtr[((i)*13+16) % 17]
329 // b: 0 4 | 1 5 | 2 6 | 3 7
330 #define b(i, j) b##i[(j)*2%8 + (j)/4]
331 
332 // output
333 #define OA(i) z[i] = ConditionalByteReverse(B::ToEnum(), a(i+9))
334 #define OX(i) z[i] = y[i] ^ ConditionalByteReverse(B::ToEnum(), a(i+9))
335 // buffer update
336 #define US(i) {word32 t=b(0,i); b(0,i)=ConditionalByteReverse(B::ToEnum(), p[i])^t; b(25,(i+6)%8)^=t;}
337 #define UL(i) {word32 t=b(0,i); b(0,i)=a(i+1)^t; b(25,(i+6)%8)^=t;}
338 // gamma and pi
339 #define GP(i) c(5*i%17) = rotlFixed(a(i) ^ (a((i+1)%17) | ~a((i+2)%17)), ((5*i%17)*((5*i%17)+1)/2)%32)
340 // theta and sigma
341 #define T(i,x) a(i) = c(i) ^ c((i+1)%17) ^ c((i+4)%17) ^ x
342 #define TS1S(i) T(i+1, ConditionalByteReverse(B::ToEnum(), p[i]))
343 #define TS1L(i) T(i+1, b(4,i))
344 #define TS2(i) T(i+9, b(16,i))
345 
346  while (count--)
347  {
348  if (z)
349  {
350  if (y)
351  {
352  OX(0); OX(1); OX(2); OX(3); OX(4); OX(5); OX(6); OX(7);
353  y += 8;
354  }
355  else
356  {
357  OA(0); OA(1); OA(2); OA(3); OA(4); OA(5); OA(6); OA(7);
358  }
359  z += 8;
360  }
361 
362  word32 *const b16 = (word32 *)(bPtr+((bstart+16*32) & 31*32));
363  word32 *const b4 = (word32 *)(bPtr+((bstart+(32-4)*32) & 31*32));
364  bstart += 32;
365  word32 *const b0 = (word32 *)(bPtr+((bstart) & 31*32));
366  word32 *const b25 = (word32 *)(bPtr+((bstart+(32-25)*32) & 31*32));
367 
368  if (p)
369  {
370  US(0); US(1); US(2); US(3); US(4); US(5); US(6); US(7);
371  }
372  else
373  {
374  UL(0); UL(1); UL(2); UL(3); UL(4); UL(5); UL(6); UL(7);
375  }
376 
377  GP(0);
378  GP(1);
379  GP(2);
380  GP(3);
381  GP(4);
382  GP(5);
383  GP(6);
384  GP(7);
385  GP(8);
386  GP(9);
387  GP(10);
388  GP(11);
389  GP(12);
390  GP(13);
391  GP(14);
392  GP(15);
393  GP(16);
394 
395  T(0,1);
396 
397  if (p)
398  {
399  TS1S(0); TS1S(1); TS1S(2); TS1S(3); TS1S(4); TS1S(5); TS1S(6); TS1S(7);
400  p += 8;
401  }
402  else
403  {
404  TS1L(0); TS1L(1); TS1L(2); TS1L(3); TS1L(4); TS1L(5); TS1L(6); TS1L(7);
405  }
406 
407  TS2(0); TS2(1); TS2(2); TS2(3); TS2(4); TS2(5); TS2(6); TS2(7);
408  }
409  m_state[17] = bstart;
410 }
411 
412 namespace Weak {
413 template <class B>
414 size_t PanamaHash<B>::HashMultipleBlocks(const word32 *input, size_t length)
415 {
416  this->Iterate(length / this->BLOCKSIZE, input);
417  return length % this->BLOCKSIZE;
418 }
419 
420 template <class B>
421 void PanamaHash<B>::TruncatedFinal(byte *hash, size_t size)
422 {
423  this->ThrowIfInvalidTruncatedSize(size);
424 
425  this->PadLastBlock(this->BLOCKSIZE, 0x01);
426 
427  HashEndianCorrectedBlock(this->m_data);
428 
429  this->Iterate(32); // pull
430 
432  this->Iterate(1, NULL, buf, NULL);
433 
434  memcpy(hash, buf, size);
435 
436  this->Restart(); // reinit for next use
437 }
438 }
439 
440 template <class B>
441 void PanamaCipherPolicy<B>::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
442 {
443  assert(length==32);
444  memcpy(m_key, key, 32);
445 }
446 
447 template <class B>
448 void PanamaCipherPolicy<B>::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
449 {
450  assert(length==32);
451  this->Reset();
452  this->Iterate(1, m_key);
453  if (iv && IsAligned<word32>(iv))
454  this->Iterate(1, (const word32 *)iv);
455  else
456  {
458  if (iv)
459  memcpy(buf, iv, 32);
460  else
461  memset(buf, 0, 32);
462  this->Iterate(1, buf);
463  }
464 
465 #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE)
466  if (B::ToEnum() == LITTLE_ENDIAN_ORDER && HasSSE2() && !IsP4()) // SSE2 code is slower on P4 Prescott
467  Panama_SSE2_Pull(32, this->m_state, NULL, NULL);
468  else
469 #endif
470  this->Iterate(32);
471 }
472 
473 #if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X64
474 template <class B>
475 unsigned int PanamaCipherPolicy<B>::GetAlignment() const
476 {
477 #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE)
478  if (B::ToEnum() == LITTLE_ENDIAN_ORDER && HasSSE2())
479  return 16;
480  else
481 #endif
482  return 1;
483 }
484 #endif
485 
486 template <class B>
487 void PanamaCipherPolicy<B>::OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount)
488 {
489 #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE || defined(CRYPTOPP_X64_MASM_AVAILABLE)
490  if (B::ToEnum() == LITTLE_ENDIAN_ORDER && HasSSE2())
491  Panama_SSE2_Pull(iterationCount, this->m_state, (word32 *)output, (const word32 *)input);
492  else
493 #endif
494  this->Iterate(iterationCount, NULL, (word32 *)output, (const word32 *)input);
495 }
496 
497 template class Panama<BigEndian>;
498 template class Panama<LittleEndian>;
499 
500 template class Weak::PanamaHash<BigEndian>;
501 template class Weak::PanamaHash<LittleEndian>;
502 
503 template class PanamaCipherPolicy<BigEndian>;
504 template class PanamaCipherPolicy<LittleEndian>;
505 
506 NAMESPACE_END
507 
508 #endif // #ifndef CRYPTOPP_GENERATE_X64_MASM