Crypto++  6.1
Free C++ class library of cryptographic schemes
speck.cpp
1 // speck.cpp - written and placed in the public domain by Jeffrey Walton
2 
3 #include "pch.h"
4 #include "config.h"
5 
6 #include "speck.h"
7 #include "misc.h"
8 #include "cpu.h"
9 
10 // Uncomment for benchmarking C++ against SSE or NEON.
11 // Do so in both speck.cpp and speck-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 Forward round transformation
24 /// \tparam W word type
25 /// \details TF83() is the forward round transformation using a=8 and b=3 rotations.
26 /// The initial test implementation provided template parameters, but they were
27 /// removed because SPECK32 using a=7 and b=2 was not on the road map. The
28 /// additional template parameters also made calling SPECK_Encrypt and SPECK_Decrypt
29 /// kind of messy.
30 template <class W>
31 inline void TF83(W& x, W& y, const W k)
32 {
33  x = rotrConstant<8>(x);
34  x += y; x ^= k;
35  y = rotlConstant<3>(y);
36  y ^= x;
37 }
38 
39 /// \brief Reverse round transformation
40 /// \tparam W word type
41 /// \details TR83() is the reverse round transformation using a=8 and b=3 rotations.
42 /// The initial test implementation provided template parameters, but they were
43 /// removed because SPECK32 using a=7 and b=2 was not on the road map. The
44 /// additional template parameters also made calling SPECK_Encrypt and SPECK_Decrypt
45 /// kind of messy.
46 template <class W>
47 inline void TR83(W& x, W& y, const W k)
48 {
49  y ^= x;
50  y = rotrConstant<3>(y);
51  x ^= k; x -= y;
52  x = rotlConstant<8>(x);
53 }
54 
55 /// \brief Forward transformation
56 /// \tparam W word type
57 /// \tparam R number of rounds
58 /// \param c output array
59 /// \param p input array
60 /// \param k subkey array
61 template <class W, unsigned int R>
62 inline void SPECK_Encrypt(W c[2], const W p[2], const W k[R])
63 {
64  c[0]=p[0]; c[1]=p[1];
65 
66  // Don't unroll this loop. Things slow down.
67  for (int i = 0; i < static_cast<int>(R); ++i)
68  TF83(c[0], c[1], k[i]);
69 }
70 
71 /// \brief Reverse transformation
72 /// \tparam W word type
73 /// \tparam R number of rounds
74 /// \param p output array
75 /// \param c input array
76 /// \param k subkey array
77 template <class W, unsigned int R>
78 inline void SPECK_Decrypt(W p[2], const W c[2], const W k[R])
79 {
80  p[0]=c[0]; p[1]=c[1];
81 
82  // Don't unroll this loop. Things slow down.
83  for (int i = static_cast<int>(R-1); i >= 0; --i)
84  TR83(p[0], p[1], k[i]);
85 }
86 
87 /// \brief Subkey generation function
88 /// \details Used when the user key consists of 2 words
89 /// \tparam W word type
90 /// \tparam R number of rounds
91 /// \param key empty subkey array
92 /// \param k user key array
93 template <class W, unsigned int R>
94 inline void SPECK_ExpandKey_2W(W key[R], const W k[2])
95 {
96  CRYPTOPP_ASSERT(R==32);
97  W i=0, B=k[0], A=k[1];
98 
99  while (i<R-1)
100  {
101  key[i]=A; TF83(B, A, i);
102  i++;
103  }
104  key[R-1]=A;
105 }
106 
107 /// \brief Subkey generation function
108 /// \details Used when the user key consists of 3 words
109 /// \tparam W word type
110 /// \tparam R number of rounds
111 /// \param key empty subkey array
112 /// \param k user key array
113 template <class W, unsigned int R>
114 inline void SPECK_ExpandKey_3W(W key[R], const W k[3])
115 {
116  CRYPTOPP_ASSERT(R==33 || R==26);
117  W i=0, C=k[0], B=k[1], A=k[2];
118 
119  unsigned int blocks = R/2;
120  while (blocks--)
121  {
122  key[i+0]=A; TF83(B, A, i+0);
123  key[i+1]=A; TF83(C, A, i+1);
124  i+=2;
125  }
126 
127  // The constexpr residue should allow the optimizer to remove unneeded statements
128  if(R%2 == 1)
129  {
130  key[R-1]=A;
131  }
132 }
133 
134 /// \brief Subkey generation function
135 /// \details Used when the user key consists of 4 words
136 /// \tparam W word type
137 /// \tparam R number of rounds
138 /// \param key empty subkey array
139 /// \param k user key array
140 template <class W, unsigned int R>
141 inline void SPECK_ExpandKey_4W(W key[R], const W k[4])
142 {
143  CRYPTOPP_ASSERT(R==34 || R==27);
144  W i=0, D=k[0], C=k[1], B=k[2], A=k[3];
145 
146  unsigned int blocks = R/3;
147  while (blocks--)
148  {
149  key[i+0]=A; TF83(B, A, i+0);
150  key[i+1]=A; TF83(C, A, i+1);
151  key[i+2]=A; TF83(D, A, i+2);
152  i+=3;
153  }
154 
155  // The constexpr residue should allow the optimizer to remove unneeded statements
156  if(R%3 == 1)
157  {
158  key[R-1]=A;
159  }
160  else if(R%3 == 2)
161  {
162  key[R-2]=A; TF83(B, A, W(R-2));
163  key[R-1]=A;
164  }
165 }
166 
167 ANONYMOUS_NAMESPACE_END
168 
169 ///////////////////////////////////////////////////////////
170 
171 NAMESPACE_BEGIN(CryptoPP)
172 
173 #if defined(CRYPTOPP_ARM_NEON_AVAILABLE)
174 extern size_t SPECK64_Enc_AdvancedProcessBlocks_NEON(const word32* subKeys, size_t rounds,
175  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
176 
177 extern size_t SPECK64_Dec_AdvancedProcessBlocks_NEON(const word32* subKeys, size_t rounds,
178  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
179 
180 extern size_t SPECK128_Enc_AdvancedProcessBlocks_NEON(const word64* subKeys, size_t rounds,
181  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
182 
183 extern size_t SPECK128_Dec_AdvancedProcessBlocks_NEON(const word64* subKeys, size_t rounds,
184  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
185 #endif
186 
187 #if defined(CRYPTOPP_SSE41_AVAILABLE)
188 extern size_t SPECK64_Enc_AdvancedProcessBlocks_SSE41(const word32* subKeys, size_t rounds,
189  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
190 
191 extern size_t SPECK64_Dec_AdvancedProcessBlocks_SSE41(const word32* subKeys, size_t rounds,
192  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
193 #endif
194 
195 #if defined(CRYPTOPP_SSSE3_AVAILABLE)
196 extern size_t SPECK128_Enc_AdvancedProcessBlocks_SSSE3(const word64* subKeys, size_t rounds,
197  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
198 
199 extern size_t SPECK128_Dec_AdvancedProcessBlocks_SSSE3(const word64* subKeys, size_t rounds,
200  const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
201 #endif
202 
203 void SPECK64::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
204 {
205  CRYPTOPP_ASSERT(keyLength == 12 || keyLength == 16);
206  CRYPTOPP_UNUSED(params);
207 
208  // Building the key schedule table requires {3,4} words workspace.
209  // Encrypting and decrypting requires 4 words workspace.
210  m_kwords = keyLength/sizeof(word32);
211  m_wspace.New(4U);
212 
213  // Do the endian gyrations from the paper and align pointers
214  typedef GetBlock<word32, LittleEndian, false> KeyBlock;
215  KeyBlock kblk(userKey);
216 
217  switch (m_kwords)
218  {
219  case 3:
220  m_rkeys.New((m_rounds = 26));
221  kblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
222  SPECK_ExpandKey_3W<word32, 26>(m_rkeys, m_wspace);
223  break;
224  case 4:
225  m_rkeys.New((m_rounds = 27));
226  kblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
227  SPECK_ExpandKey_4W<word32, 27>(m_rkeys, m_wspace);
228  break;
229  default:
230  CRYPTOPP_ASSERT(0);;
231  }
232 }
233 
234 void SPECK64::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
235 {
236  // Do the endian gyrations from the paper and align pointers
238  InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
239 
240  switch (m_rounds)
241  {
242  case 26:
243  SPECK_Encrypt<word32, 26>(m_wspace+2, m_wspace+0, m_rkeys);
244  break;
245  case 27:
246  SPECK_Encrypt<word32, 27>(m_wspace+2, m_wspace+0, m_rkeys);
247  break;
248  default:
249  CRYPTOPP_ASSERT(0);;
250  }
251 
252  // Do the endian gyrations from the paper and align pointers
253  typedef PutBlock<word32, LittleEndian, false> OutBlock;
254  OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
255 }
256 
257 void SPECK64::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
258 {
259  // Do the endian gyrations from the paper and align pointers
261  InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
262 
263  switch (m_rounds)
264  {
265  case 26:
266  SPECK_Decrypt<word32, 26>(m_wspace+2, m_wspace+0, m_rkeys);
267  break;
268  case 27:
269  SPECK_Decrypt<word32, 27>(m_wspace+2, m_wspace+0, m_rkeys);
270  break;
271  default:
272  CRYPTOPP_ASSERT(0);;
273  }
274 
275  // Do the endian gyrations from the paper and align pointers
276  typedef PutBlock<word32, LittleEndian, false> OutBlock;
277  OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
278 }
279 
280 ///////////////////////////////////////////////////////////
281 
282 void SPECK128::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
283 {
284  CRYPTOPP_ASSERT(keyLength == 16 || keyLength == 24 || keyLength == 32);
285  CRYPTOPP_UNUSED(params);
286 
287  // Building the key schedule table requires {2,3,4} words workspace.
288  // Encrypting and decrypting requires 4 words workspace.
289  m_kwords = keyLength/sizeof(word64);
290  m_wspace.New(4U);
291 
292  // Do the endian gyrations from the paper and align pointers
293  typedef GetBlock<word64, LittleEndian, false> KeyBlock;
294  KeyBlock kblk(userKey);
295 
296  switch (m_kwords)
297  {
298  case 2:
299  m_rkeys.New((m_rounds = 32));
300  kblk(m_wspace[1])(m_wspace[0]);
301  SPECK_ExpandKey_2W<word64, 32>(m_rkeys, m_wspace);
302  break;
303  case 3:
304  m_rkeys.New((m_rounds = 33));
305  kblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
306  SPECK_ExpandKey_3W<word64, 33>(m_rkeys, m_wspace);
307  break;
308  case 4:
309  m_rkeys.New((m_rounds = 34));
310  kblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
311  SPECK_ExpandKey_4W<word64, 34>(m_rkeys, m_wspace);
312  break;
313  default:
314  CRYPTOPP_ASSERT(0);;
315  }
316 }
317 
318 void SPECK128::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
319 {
320  // Do the endian gyrations from the paper and align pointers
322  InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
323 
324  switch (m_rounds)
325  {
326  case 32:
327  SPECK_Encrypt<word64, 32>(m_wspace+2, m_wspace+0, m_rkeys);
328  break;
329  case 33:
330  SPECK_Encrypt<word64, 33>(m_wspace+2, m_wspace+0, m_rkeys);
331  break;
332  case 34:
333  SPECK_Encrypt<word64, 34>(m_wspace+2, m_wspace+0, m_rkeys);
334  break;
335  default:
336  CRYPTOPP_ASSERT(0);;
337  }
338 
339  // Do the endian gyrations from the paper and align pointers
340  typedef PutBlock<word64, LittleEndian, false> OutBlock;
341  OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
342 }
343 
344 void SPECK128::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
345 {
346  // Do the endian gyrations from the paper and align pointers
348  InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
349 
350  switch (m_rounds)
351  {
352  case 32:
353  SPECK_Decrypt<word64, 32>(m_wspace+2, m_wspace+0, m_rkeys);
354  break;
355  case 33:
356  SPECK_Decrypt<word64, 33>(m_wspace+2, m_wspace+0, m_rkeys);
357  break;
358  case 34:
359  SPECK_Decrypt<word64, 34>(m_wspace+2, m_wspace+0, m_rkeys);
360  break;
361  default:
362  CRYPTOPP_ASSERT(0);;
363  }
364 
365  // Do the endian gyrations from the paper and align pointers
366  typedef PutBlock<word64, LittleEndian, false> OutBlock;
367  OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
368 }
369 
370 #if defined(CRYPTOPP_SPECK64_ADVANCED_PROCESS_BLOCKS)
371 size_t SPECK64::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
372  byte *outBlocks, size_t length, word32 flags) const
373 {
374 #if defined(CRYPTOPP_SSE41_AVAILABLE)
375  if (HasSSE41())
376  return SPECK64_Enc_AdvancedProcessBlocks_SSE41(m_rkeys, (size_t)m_rounds,
377  inBlocks, xorBlocks, outBlocks, length, flags);
378 #endif
379 #if defined(CRYPTOPP_ARM_NEON_AVAILABLE)
380  if (HasNEON())
381  return SPECK64_Enc_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
382  inBlocks, xorBlocks, outBlocks, length, flags);
383 #endif
384  return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
385 }
386 
387 size_t SPECK64::Dec::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
388  byte *outBlocks, size_t length, word32 flags) const
389 {
390 #if defined(CRYPTOPP_SSE41_AVAILABLE)
391  if (HasSSE41())
392  return SPECK64_Dec_AdvancedProcessBlocks_SSE41(m_rkeys, (size_t)m_rounds,
393  inBlocks, xorBlocks, outBlocks, length, flags);
394 #endif
395 #if defined(CRYPTOPP_ARM_NEON_AVAILABLE)
396  if (HasNEON())
397  return SPECK64_Dec_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
398  inBlocks, xorBlocks, outBlocks, length, flags);
399 #endif
400  return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
401 }
402 #endif // CRYPTOPP_SPECK64_ADVANCED_PROCESS_BLOCKS
403 
404 #if defined(CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS)
405 size_t SPECK128::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
406  byte *outBlocks, size_t length, word32 flags) const
407 {
408 #if defined(CRYPTOPP_SSSE3_AVAILABLE)
409  if (HasSSSE3())
410  return SPECK128_Enc_AdvancedProcessBlocks_SSSE3(m_rkeys, (size_t)m_rounds,
411  inBlocks, xorBlocks, outBlocks, length, flags);
412 #endif
413 #if defined(CRYPTOPP_ARM_NEON_AVAILABLE)
414  if (HasNEON())
415  return SPECK128_Enc_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
416  inBlocks, xorBlocks, outBlocks, length, flags);
417 #endif
418  return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
419 }
420 
421 size_t SPECK128::Dec::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
422  byte *outBlocks, size_t length, word32 flags) const
423 {
424 #if defined(CRYPTOPP_SSSE3_AVAILABLE)
425  if (HasSSSE3())
426  return SPECK128_Dec_AdvancedProcessBlocks_SSSE3(m_rkeys, (size_t)m_rounds,
427  inBlocks, xorBlocks, outBlocks, length, flags);
428 #endif
429 #if defined(CRYPTOPP_ARM_NEON_AVAILABLE)
430  if (HasNEON())
431  return SPECK128_Dec_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
432  inBlocks, xorBlocks, outBlocks, length, flags);
433 #endif
434  return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
435 }
436 #endif // CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS
437 
438 NAMESPACE_END
Utility functions for the Crypto++ library.
bool HasSSSE3()
Determines SSSE3 availability.
Definition: cpu.h:129
Library configuration file.
void New(size_type newSize)
Change size without preserving contents.
Definition: secblock.h:729
T rotlConstant(T x)
Performs a left rotate.
Definition: misc.h:1365
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.
Definition: cryptlib.cpp:147
Precompiled header file.
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:60
Functions for CPU features and intrinsics.
Classes for the Speck block cipher.
T rotrConstant(T x)
Performs a right rotate.
Definition: misc.h:1391
Access a block of memory.
Definition: misc.h:2324
bool HasSSE41()
Determines SSE4.1 availability.
Definition: cpu.h:140
Access a block of memory.
Definition: misc.h:2365
Crypto++ library namespace.
bool HasNEON()
Determine if an ARM processor has Advanced SIMD available.
Definition: cpu.h:329
Interface for retrieving values given their names.
Definition: cryptlib.h:290