Crypto++  8.8 Free C++ class library of cryptographic schemes
simon.cpp
1 // simon.h - written and placed in the public domain by Jeffrey Walton
2
3 #include "pch.h"
4 #include "config.h"
5
6 #include "simon.h"
7 #include "misc.h"
8 #include "cpu.h"
9
10 // Uncomment for benchmarking C++ against SSE or NEON.
11 // Do so in both simon.cpp and simon_simd.cpp.
12 // #undef CRYPTOPP_SSSE3_AVAILABLE
13 // #undef CRYPTOPP_SSE41_AVAILABLE
14 // #undef CRYPTOPP_ARM_NEON_AVAILABLE
15
16 ANONYMOUS_NAMESPACE_BEGIN
17
18 using CryptoPP::word32;
19 using CryptoPP::word64;
22
23 /// \brief Round transformation helper
24 /// \tparam W word type
25 /// \param v value
26 template <class W>
27 inline W f(const W v)
28 {
29  return (rotlConstant<1>(v) & rotlConstant<8>(v)) ^ rotlConstant<2>(v);
30 }
31
32 /// \brief Round transformation
33 /// \tparam W word type
34 /// \param x value
35 /// \param y value
36 /// \param k value
37 /// \param l value
38 template <class W>
39 inline void R2(W& x, W& y, const W k, const W l)
40 {
41  y ^= f(x); y ^= k;
42  x ^= f(y); x ^= l;
43 }
44
45 /// \brief Forward transformation
46 /// \tparam W word type
47 /// \tparam R number of rounds
48 /// \param c output array
49 /// \param p input array
50 /// \param k subkey array
51 template <class W, unsigned int R>
52 inline void SIMON_Encrypt(W c[2], const W p[2], const W k[R])
53 {
54  c[0]=p[0]; c[1]=p[1];
55
56  for (int i = 0; i < static_cast<int>(R-1); i += 2)
57  R2(c[0], c[1], k[i], k[i + 1]);
58
59  if (R & 1)
60  {
61  c[1] ^= f(c[0]); c[1] ^= k[R-1];
62  W t = c[0]; c[0] = c[1]; c[1] = t;
63  }
64 }
65
66 /// \brief Reverse transformation
67 /// \tparam W word type
68 /// \tparam R number of rounds
69 /// \param p output array
70 /// \param c input array
71 /// \param k subkey array
72 template <class W, unsigned int R>
73 inline void SIMON_Decrypt(W p[2], const W c[2], const W k[R])
74 {
75  p[0]=c[0]; p[1]=c[1];
76  unsigned int rounds = R;
77
78  if (R & 1)
79  {
80  const W t = p[1]; p[1] = p[0]; p[0] = t;
81  p[1] ^= k[R - 1]; p[1] ^= f(p[0]);
82  rounds--;
83  }
84
85  for (int i = static_cast<int>(rounds - 2); i >= 0; i -= 2)
86  R2(p[1], p[0], k[i + 1], k[i]);
87 }
88
89 /// \brief Subkey generation function
90 /// \details Used for SIMON-64 with 96-bit key and 42 rounds. A template was
91 /// not worthwhile because all instantiations would need specialization.
92 /// \param key empty subkey array
93 /// \param k user key array
94 inline void SIMON64_ExpandKey_3W(word32 key[42], const word32 k[3])
95 {
96  const word32 c = 0xfffffffc;
97  word64 z = W64LIT(0x7369f885192c0ef5);
98
99  key[0] = k[2]; key[1] = k[1]; key[2] = k[0];
100  for (size_t i = 3; i<42; ++i)
101  {
102  key[i] = static_cast<word32>(c ^ (z & 1) ^ key[i - 3] ^
103  rotrConstant<3>(key[i - 1]) ^ rotrConstant<4>(key[i - 1]));
104  z >>= 1;
105  }
106 }
107
108 /// \brief Subkey generation function
109 /// \details Used for SIMON-64 with 128-bit key and 44 rounds. A template was
110 /// not worthwhile because all instantiations would need specialization.
111 /// \param key empty subkey array
112 /// \param k user key array
113 inline void SIMON64_ExpandKey_4W(word32 key[44], const word32 k[4])
114 {
115  const word32 c = 0xfffffffc;
116  word64 z = W64LIT(0xfc2ce51207a635db);
117
118  key[0] = k[3]; key[1] = k[2]; key[2] = k[1]; key[3] = k[0];
119  for (size_t i = 4; i<44; ++i)
120  {
121  key[i] = static_cast<word32>(c ^ (z & 1) ^ key[i - 4] ^
122  rotrConstant<3>(key[i - 1]) ^ key[i - 3] ^ rotrConstant<4>(key[i - 1]) ^
123  rotrConstant<1>(key[i - 3]));
124  z >>= 1;
125  }
126 }
127
128 /// \brief Subkey generation function
129 /// \details Used for SIMON-128 with 128-bit key and 68 rounds. A template was
130 /// not worthwhile because all instantiations would need specialization.
131 /// \param key empty subkey array
132 /// \param k user key array
133 inline void SIMON128_ExpandKey_2W(word64 key[68], const word64 k[2])
134 {
135  const word64 c = W64LIT(0xfffffffffffffffc);
136  word64 z = W64LIT(0x7369f885192c0ef5);
137
138  key[0] = k[1]; key[1] = k[0];
139  for (size_t i=2; i<66; ++i)
140  {
141  key[i] = c ^ (z & 1) ^ key[i - 2] ^ rotrConstant<3>(key[i - 1]) ^ rotrConstant<4>(key[i - 1]);
142  z>>=1;
143  }
144
145  key[66] = c ^ 1 ^ key[64] ^ rotrConstant<3>(key[65]) ^ rotrConstant<4>(key[65]);
146  key[67] = c^key[65] ^ rotrConstant<3>(key[66]) ^ rotrConstant<4>(key[66]);
147 }
148
149 /// \brief Subkey generation function
150 /// \details Used for SIMON-128 with 192-bit key and 69 rounds. A template was
151 /// not worthwhile because all instantiations would need specialization.
152 /// \param key empty subkey array
153 /// \param k user key array
154 inline void SIMON128_ExpandKey_3W(word64 key[69], const word64 k[3])
155 {
156  const word64 c = W64LIT(0xfffffffffffffffc);
157  word64 z = W64LIT(0xfc2ce51207a635db);
158
159  key[0]=k[2]; key[1]=k[1]; key[2]=k[0];
160  for (size_t i=3; i<67; ++i)
161  {
162  key[i] = c ^ (z & 1) ^ key[i - 3] ^ rotrConstant<3>(key[i - 1]) ^ rotrConstant<4>(key[i - 1]);
163  z>>=1;
164  }
165
166  key[67] = c^key[64] ^ rotrConstant<3>(key[66]) ^ rotrConstant<4>(key[66]);
167  key[68] = c ^ 1 ^ key[65] ^ rotrConstant<3>(key[67]) ^ rotrConstant<4>(key[67]);
168 }
169
170 /// \brief Subkey generation function
171 /// \details Used for SIMON-128 with 256-bit key and 72 rounds. A template was
172 /// not worthwhile because all instantiations would need specialization.
173 /// \param key empty subkey array
174 /// \param k user key array
175 inline void SIMON128_ExpandKey_4W(word64 key[72], const word64 k[4])
176 {
177  const word64 c = W64LIT(0xfffffffffffffffc);
178  word64 z = W64LIT(0xfdc94c3a046d678b);
179
180  key[0]=k[3]; key[1]=k[2]; key[2]=k[1]; key[3]=k[0];
181  for (size_t i=4; i<68; ++i)
182  {
183  key[i] = c ^ (z & 1) ^ key[i - 4] ^ rotrConstant<3>(key[i - 1]) ^ key[i - 3] ^ rotrConstant<4>(key[i - 1]) ^ rotrConstant<1>(key[i - 3]);
184  z>>=1;
185  }
186
187  key[68] = c^key[64] ^ rotrConstant<3>(key[67]) ^ key[65] ^ rotrConstant<4>(key[67]) ^ rotrConstant<1>(key[65]);
188  key[69] = c ^ 1 ^ key[65] ^ rotrConstant<3>(key[68]) ^ key[66] ^ rotrConstant<4>(key[68]) ^ rotrConstant<1>(key[66]);
189  key[70] = c^key[66] ^ rotrConstant<3>(key[69]) ^ key[67] ^ rotrConstant<4>(key[69]) ^ rotrConstant<1>(key[67]);
190  key[71] = c^key[67] ^ rotrConstant<3>(key[70]) ^ key[68] ^ rotrConstant<4>(key[70]) ^ rotrConstant<1>(key[68]);
191 }
192
193 ANONYMOUS_NAMESPACE_END
194
195 ///////////////////////////////////////////////////////////
196
197 NAMESPACE_BEGIN(CryptoPP)
198
199 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
200 extern size_t SIMON128_Enc_AdvancedProcessBlocks_NEON(const word64* subKeys, size_t rounds,
201  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
202
203 extern size_t SIMON128_Dec_AdvancedProcessBlocks_NEON(const word64* subKeys, size_t rounds,
204  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
205 #endif
206
207 #if (CRYPTOPP_SSSE3_AVAILABLE)
208 extern size_t SIMON128_Enc_AdvancedProcessBlocks_SSSE3(const word64* subKeys, size_t rounds,
209  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
210
211 extern size_t SIMON128_Dec_AdvancedProcessBlocks_SSSE3(const word64* subKeys, size_t rounds,
212  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
213 #endif
214
215 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
216 extern size_t SIMON128_Enc_AdvancedProcessBlocks_ALTIVEC(const word64* subKeys, size_t rounds,
217  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
218
219 extern size_t SIMON128_Dec_AdvancedProcessBlocks_ALTIVEC(const word64* subKeys, size_t rounds,
220  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
221 #endif
222
223 std::string SIMON64::Base::AlgorithmProvider() const
224 {
225  return "C++";
226 }
227
229 {
230  return GetAlignmentOf<word32>();
231 }
232
233 void SIMON64::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
234 {
235  CRYPTOPP_ASSERT(keyLength == 12 || keyLength == 16);
236  CRYPTOPP_UNUSED(params);
237
238  // Building the key schedule table requires {3,4} words workspace.
239  // Encrypting and decrypting requires 4 words workspace.
240  m_kwords = keyLength/sizeof(word32);
241  m_wspace.New(4U);
242
243  // Do the endian gyrations from the paper and align pointers
244  typedef GetBlock<word32, LittleEndian> KeyBlock;
245  KeyBlock kblk(userKey);
246
247  switch (m_kwords)
248  {
249  case 3:
250  m_rkeys.New((m_rounds = 42));
251  kblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
252  SIMON64_ExpandKey_3W(m_rkeys, m_wspace);
253  break;
254  case 4:
255  m_rkeys.New((m_rounds = 44));
256  kblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
257  SIMON64_ExpandKey_4W(m_rkeys, m_wspace);
258  break;
259  default:
260  CRYPTOPP_ASSERT(0);
261  }
262 }
263
264 void SIMON64::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
265 {
266  // Do the endian gyrations from the paper and align pointers
267  typedef GetBlock<word32, LittleEndian> InBlock;
268  InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
269
270  switch (m_rounds)
271  {
272  case 42:
273  SIMON_Encrypt<word32, 42>(m_wspace+2, m_wspace+0, m_rkeys);
274  break;
275  case 44:
276  SIMON_Encrypt<word32, 44>(m_wspace+2, m_wspace+0, m_rkeys);
277  break;
278  default:
279  CRYPTOPP_ASSERT(0);
280  }
281
282  // Do the endian gyrations from the paper and align pointers
283  typedef PutBlock<word32, LittleEndian> OutBlock;
284  OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
285 }
286
287 void SIMON64::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
288 {
289  // Do the endian gyrations from the paper and align pointers
290  typedef GetBlock<word32, LittleEndian> InBlock;
291  InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
292
293  switch (m_rounds)
294  {
295  case 42:
296  SIMON_Decrypt<word32, 42>(m_wspace+2, m_wspace+0, m_rkeys);
297  break;
298  case 44:
299  SIMON_Decrypt<word32, 44>(m_wspace+2, m_wspace+0, m_rkeys);
300  break;
301  default:
302  CRYPTOPP_ASSERT(0);
303  }
304
305  // Do the endian gyrations from the paper and align pointers
306  typedef PutBlock<word32, LittleEndian> OutBlock;
307  OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
308 }
309
310 ///////////////////////////////////////////////////////////
311
312 std::string SIMON128::Base::AlgorithmProvider() const
313 {
315 # if (CRYPTOPP_SSSE3_AVAILABLE)
316  if (HasSSSE3())
317  return "SSSE3";
318 # endif
319 # if (CRYPTOPP_ARM_NEON_AVAILABLE)
320  if (HasNEON())
321  return "NEON";
322 # endif
323 # if (CRYPTOPP_ALTIVEC_AVAILABLE)
324  if (HasAltivec())
325  return "Altivec";
326 # endif
327 #endif
328  return "C++";
329 }
330
332 {
334 # if (CRYPTOPP_SSSE3_AVAILABLE)
335  if (HasSSSE3())
336  return 16; // load __m128i
337 # endif
338 # if (CRYPTOPP_ARM_NEON_AVAILABLE)
339  if (HasNEON())
340  return 8; // load uint64x2_t
341 # endif
342 # if (CRYPTOPP_ALTIVEC_AVAILABLE)
343  if (HasAltivec())
344  return 16; // load uint64x2_p
345 # endif
346 #endif
347  return GetAlignmentOf<word64>();
348 }
349
350 void SIMON128::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
351 {
352  CRYPTOPP_ASSERT(keyLength == 16 || keyLength == 24 || keyLength == 32);
353  CRYPTOPP_UNUSED(params);
354
355  // Building the key schedule table requires {2,3,4} words workspace.
356  // Encrypting and decrypting requires 4 words workspace.
357  m_kwords = keyLength/sizeof(word64);
358  m_wspace.New(4U);
359
360  // Do the endian gyrations from the paper and align pointers
361  typedef GetBlock<word64, LittleEndian> KeyBlock;
362  KeyBlock kblk(userKey);
363
364  switch (m_kwords)
365  {
366  case 2:
367  m_rkeys.New((m_rounds = 68));
368  kblk(m_wspace[1])(m_wspace[0]);
369  SIMON128_ExpandKey_2W(m_rkeys, m_wspace);
370  break;
371  case 3:
372  m_rkeys.New((m_rounds = 69));
373  kblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
374  SIMON128_ExpandKey_3W(m_rkeys, m_wspace);
375  break;
376  case 4:
377  m_rkeys.New((m_rounds = 72));
378  kblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
379  SIMON128_ExpandKey_4W(m_rkeys, m_wspace);
380  break;
381  default:
382  CRYPTOPP_ASSERT(0);
383  }
384
386
387  // Pre-splat the round keys for Altivec forward transformation
388 #if CRYPTOPP_ALTIVEC_AVAILABLE
389  if (IsForwardTransformation() && HasAltivec())
390  {
391  AlignedSecBlock presplat(m_rkeys.size()*2);
392  for (size_t i=0, j=0; i<m_rkeys.size(); i++, j+=2)
393  presplat[j+0] = presplat[j+1] = m_rkeys[i];
394  m_rkeys.swap(presplat);
395  }
396 #elif CRYPTOPP_SSSE3_AVAILABLE
397  if (IsForwardTransformation() && HasSSSE3())
398  {
399  AlignedSecBlock presplat(m_rkeys.size()*2);
400  for (size_t i=0, j=0; i<m_rkeys.size(); i++, j+=2)
401  presplat[j+0] = presplat[j+1] = m_rkeys[i];
402  m_rkeys.swap(presplat);
403  }
404 #endif
405
406 #endif // CRYPTOPP_SIMON128_ADVANCED_PROCESS_BLOCKS
407 }
408
409 void SIMON128::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
410 {
411  // Do the endian gyrations from the paper and align pointers
412  typedef GetBlock<word64, LittleEndian> InBlock;
413  InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
414
415  switch (m_rounds)
416  {
417  case 68:
418  SIMON_Encrypt<word64, 68>(m_wspace+2, m_wspace+0, m_rkeys);
419  break;
420  case 69:
421  SIMON_Encrypt<word64, 69>(m_wspace+2, m_wspace+0, m_rkeys);
422  break;
423  case 72:
424  SIMON_Encrypt<word64, 72>(m_wspace+2, m_wspace+0, m_rkeys);
425  break;
426  default:
427  CRYPTOPP_ASSERT(0);
428  }
429
430  // Do the endian gyrations from the paper and align pointers
431  typedef PutBlock<word64, LittleEndian> OutBlock;
432  OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
433 }
434
435 void SIMON128::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
436 {
437  // Do the endian gyrations from the paper and align pointers
438  typedef GetBlock<word64, LittleEndian> InBlock;
439  InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
440
441  switch (m_rounds)
442  {
443  case 68:
444  SIMON_Decrypt<word64, 68>(m_wspace+2, m_wspace+0, m_rkeys);
445  break;
446  case 69:
447  SIMON_Decrypt<word64, 69>(m_wspace+2, m_wspace+0, m_rkeys);
448  break;
449  case 72:
450  SIMON_Decrypt<word64, 72>(m_wspace+2, m_wspace+0, m_rkeys);
451  break;
452  default:
453  CRYPTOPP_ASSERT(0);
454  }
455
456  // Do the endian gyrations from the paper and align pointers
457  typedef PutBlock<word64, LittleEndian> OutBlock;
458  OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
459 }
460
462 size_t SIMON128::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
463  byte *outBlocks, size_t length, word32 flags) const
464 {
465 #if (CRYPTOPP_SSSE3_AVAILABLE)
466  if (HasSSSE3())
467  return SIMON128_Enc_AdvancedProcessBlocks_SSSE3(m_rkeys, (size_t)m_rounds,
468  inBlocks, xorBlocks, outBlocks, length, flags);
469 #endif
470 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
471  if (HasNEON())
472  return SIMON128_Enc_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
473  inBlocks, xorBlocks, outBlocks, length, flags);
474 #endif
475 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
476  if (HasAltivec())
477  return SIMON128_Enc_AdvancedProcessBlocks_ALTIVEC(m_rkeys, (size_t)m_rounds,
478  inBlocks, xorBlocks, outBlocks, length, flags);
479 #endif
480  return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
481 }
482
483 size_t SIMON128::Dec::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
484  byte *outBlocks, size_t length, word32 flags) const
485 {
486 #if (CRYPTOPP_SSSE3_AVAILABLE)
487  if (HasSSSE3())
488  return SIMON128_Dec_AdvancedProcessBlocks_SSSE3(m_rkeys, (size_t)m_rounds,
489  inBlocks, xorBlocks, outBlocks, length, flags);
490 #endif
491 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
492  if (HasNEON())
493  return SIMON128_Dec_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
494  inBlocks, xorBlocks, outBlocks, length, flags);
495 #endif
496 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
497  if (HasAltivec())
498  return SIMON128_Dec_AdvancedProcessBlocks_ALTIVEC(m_rkeys, (size_t)m_rounds,
499  inBlocks, xorBlocks, outBlocks, length, flags);
500 #endif
501  return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
502 }
503 #endif // CRYPTOPP_SIMON128_ADVANCED_PROCESS_BLOCKS
504
505 NAMESPACE_END
virtual size_t AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags) const
Encrypt and xor multiple blocks using additional flags.
Access a block of memory.
Definition: misc.h:2975
Interface for retrieving values given their names.
Definition: cryptlib.h:327
Access a block of memory.
Definition: misc.h:3016
unsigned int OptimalDataAlignment() const
Provides input and output data alignment for optimal performance.
Definition: simon.cpp:331
unsigned int OptimalDataAlignment() const
Provides input and output data alignment for optimal performance.
Definition: simon.cpp:228
Library configuration file.
#define W64LIT(x)
Declare an unsigned word64.
Definition: config_int.h:129
unsigned int word32
32-bit unsigned datatype
Definition: config_int.h:72
unsigned long long word64
64-bit unsigned datatype
Definition: config_int.h:101
Functions for CPU features and intrinsics.
Utility functions for the Crypto++ library.
T rotlConstant(T x)
Performs a left rotate.
Definition: misc.h:1757
T rotrConstant(T x)
Performs a right rotate.
Definition: misc.h:1783
Crypto++ library namespace.
Classes for the Simon block cipher.
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:68