Crypto++ 8.2
Free C&
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#ifndef CRYPTOPP_INLINE
11# if defined(CRYPTOPP_DEBUG)
12# define CRYPTOPP_INLINE static
13# else
14# define CRYPTOPP_INLINE inline
15# endif
16#endif
17
18// Uncomment for benchmarking C++ against SSE or NEON.
19// Do so in both speck.cpp and speck-simd.cpp.
20// #undef CRYPTOPP_SSSE3_AVAILABLE
21// #undef CRYPTOPP_SSE41_AVAILABLE
22// #undef CRYPTOPP_ARM_NEON_AVAILABLE
23
24ANONYMOUS_NAMESPACE_BEGIN
25
26using CryptoPP::word32;
27using CryptoPP::word64;
28using CryptoPP::rotlConstant;
29using CryptoPP::rotrConstant;
30
31/// \brief Forward round transformation
32/// \tparam W word type
33/// \details TF83() is the forward round transformation using a=8 and b=3 rotations.
34/// The initial test implementation provided template parameters, but they were
35/// removed because SPECK32 using a=7 and b=2 was not on the road map. The
36/// additional template parameters also made calling SPECK_Encrypt and SPECK_Decrypt
37/// kind of messy.
38template <class W>
39CRYPTOPP_INLINE void TF83(W& x, W& y, const W k)
40{
41 x = rotrConstant<8>(x);
42 x += y; x ^= k;
43 y = rotlConstant<3>(y);
44 y ^= x;
45}
46
47/// \brief Reverse round transformation
48/// \tparam W word type
49/// \details TR83() is the reverse round transformation using a=8 and b=3 rotations.
50/// The initial test implementation provided template parameters, but they were
51/// removed because SPECK32 using a=7 and b=2 was not on the road map. The
52/// additional template parameters also made calling SPECK_Encrypt and SPECK_Decrypt
53/// kind of messy.
54template <class W>
55CRYPTOPP_INLINE void TR83(W& x, W& y, const W k)
56{
57 y ^= x;
58 y = rotrConstant<3>(y);
59 x ^= k; x -= y;
60 x = rotlConstant<8>(x);
61}
62
63/// \brief Forward transformation
64/// \tparam W word type
65/// \tparam R number of rounds
66/// \param c output array
67/// \param p input array
68/// \param k subkey array
69template <class W, unsigned int R>
70CRYPTOPP_INLINE void SPECK_Encrypt(W c[2], const W p[2], const W k[R])
71{
72 c[0]=p[0]; c[1]=p[1];
73
74 // Don't unroll this loop. Things slow down.
75 for (int i = 0; i < static_cast<int>(R); ++i)
76 TF83(c[0], c[1], k[i]);
77}
78
79/// \brief Reverse transformation
80/// \tparam W word type
81/// \tparam R number of rounds
82/// \param p output array
83/// \param c input array
84/// \param k subkey array
85template <class W, unsigned int R>
86CRYPTOPP_INLINE void SPECK_Decrypt(W p[2], const W c[2], const W k[R])
87{
88 p[0]=c[0]; p[1]=c[1];
89
90 // Don't unroll this loop. Things slow down.
91 for (int i = static_cast<int>(R-1); i >= 0; --i)
92 TR83(p[0], p[1], k[i]);
93}
94
95/// \brief Subkey generation function
96/// \details Used when the user key consists of 2 words
97/// \tparam W word type
98/// \tparam R number of rounds
99/// \param key empty subkey array
100/// \param k user key array
101template <class W, unsigned int R>
102CRYPTOPP_INLINE void SPECK_ExpandKey_2W(W key[R], const W k[2])
103{
104 CRYPTOPP_ASSERT(R==32);
105 W i=0, B=k[0], A=k[1];
106
107 while (i<R-1)
108 {
109 key[i]=A; TF83(B, A, i);
110 i++;
111 }
112 key[R-1]=A;
113}
114
115/// \brief Subkey generation function
116/// \details Used when the user key consists of 3 words
117/// \tparam W word type
118/// \tparam R number of rounds
119/// \param key empty subkey array
120/// \param k user key array
121template <class W, unsigned int R>
122CRYPTOPP_INLINE void SPECK_ExpandKey_3W(W key[R], const W k[3])
123{
124 CRYPTOPP_ASSERT(R==33 || R==26);
125 W i=0, C=k[0], B=k[1], A=k[2];
126
127 unsigned int blocks = R/2;
128 while (blocks--)
129 {
130 key[i+0]=A; TF83(B, A, i+0);
131 key[i+1]=A; TF83(C, A, i+1);
132 i+=2;
133 }
134
135 // The constexpr residue should allow the optimizer to remove unneeded statements
136 if(R%2 == 1)
137 {
138 key[R-1]=A;
139 }
140}
141
142/// \brief Subkey generation function
143/// \details Used when the user key consists of 4 words
144/// \tparam W word type
145/// \tparam R number of rounds
146/// \param key empty subkey array
147/// \param k user key array
148template <class W, unsigned int R>
149CRYPTOPP_INLINE void SPECK_ExpandKey_4W(W key[R], const W k[4])
150{
151 CRYPTOPP_ASSERT(R==34 || R==27);
152 W i=0, D=k[0], C=k[1], B=k[2], A=k[3];
153
154 unsigned int blocks = R/3;
155 while (blocks--)
156 {
157 key[i+0]=A; TF83(B, A, i+0);
158 key[i+1]=A; TF83(C, A, i+1);
159 key[i+2]=A; TF83(D, A, i+2);
160 i+=3;
161 }
162
163 // The constexpr residue should allow the optimizer to remove unneeded statements
164 if(R%3 == 1)
165 {
166 key[R-1]=A;
167 }
168 else if(R%3 == 2)
169 {
170 key[R-2]=A; TF83(B, A, W(R-2));
171 key[R-1]=A;
172 }
173}
174
175ANONYMOUS_NAMESPACE_END
176
177///////////////////////////////////////////////////////////
178
179NAMESPACE_BEGIN(CryptoPP)
180
181#if (CRYPTOPP_ARM_NEON_AVAILABLE)
182extern size_t SPECK64_Enc_AdvancedProcessBlocks_NEON(const word32* subKeys, size_t rounds,
183 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
184
185extern size_t SPECK64_Dec_AdvancedProcessBlocks_NEON(const word32* subKeys, size_t rounds,
186 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
187
188extern size_t SPECK128_Enc_AdvancedProcessBlocks_NEON(const word64* subKeys, size_t rounds,
189 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
190
191extern size_t SPECK128_Dec_AdvancedProcessBlocks_NEON(const word64* 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_SSE41_AVAILABLE)
196extern size_t SPECK64_Enc_AdvancedProcessBlocks_SSE41(const word32* subKeys, size_t rounds,
197 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
198
199extern size_t SPECK64_Dec_AdvancedProcessBlocks_SSE41(const word32* subKeys, size_t rounds,
200 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
201#endif
202
203#if defined(CRYPTOPP_SSSE3_AVAILABLE)
204extern size_t SPECK128_Enc_AdvancedProcessBlocks_SSSE3(const word64* subKeys, size_t rounds,
205 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
206
207extern size_t SPECK128_Dec_AdvancedProcessBlocks_SSSE3(const word64* subKeys, size_t rounds,
208 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
209#endif
210
211#if defined(CRYPTOPP_ALTIVEC_AVAILABLE)
212extern size_t SPECK64_Enc_AdvancedProcessBlocks_ALTIVEC(const word32* subKeys, size_t rounds,
213 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
214
215extern size_t SPECK64_Dec_AdvancedProcessBlocks_ALTIVEC(const word32* subKeys, size_t rounds,
216 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
217#endif
218
219#if defined(CRYPTOPP_POWER8_AVAILABLE)
220extern size_t SPECK128_Enc_AdvancedProcessBlocks_POWER8(const word64* subKeys, size_t rounds,
221 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
222
223extern size_t SPECK128_Dec_AdvancedProcessBlocks_POWER8(const word64* subKeys, size_t rounds,
224 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
225#endif
226
227std::string SPECK64::Base::AlgorithmProvider() const
228{
229#if (CRYPTOPP_SPECK64_ADVANCED_PROCESS_BLOCKS)
230# if (CRYPTOPP_SSE41_AVAILABLE)
231 if (HasSSE41())
232 return "SSE4.1";
233# endif
234# if (CRYPTOPP_ARM_NEON_AVAILABLE)
235 if (HasNEON())
236 return "NEON";
237# endif
238# if (CRYPTOPP_POWER8_AVAILABLE)
239 if (HasPower8())
240 return "Power8";
241# endif
242# if (CRYPTOPP_ALTIVEC_AVAILABLE)
243 if (HasAltivec())
244 return "Altivec";
245# endif
246#endif
247 return "C++";
248}
249
250void SPECK64::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
251{
252 CRYPTOPP_ASSERT(keyLength == 12 || keyLength == 16);
253 CRYPTOPP_UNUSED(params);
254
255 // Building the key schedule table requires {3,4} words workspace.
256 // Encrypting and decrypting requires 4 words workspace.
257 m_kwords = keyLength/sizeof(word32);
258 m_wspace.New(4U);
259
260 // Do the endian gyrations from the paper and align pointers
261 typedef GetBlock<word32, LittleEndian> KeyBlock;
262 KeyBlock kblk(userKey);
263
264 switch (m_kwords)
265 {
266 case 3:
267 m_rkeys.New((m_rounds = 26));
268 kblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
269 SPECK_ExpandKey_3W<word32, 26>(m_rkeys, m_wspace);
270 break;
271 case 4:
272 m_rkeys.New((m_rounds = 27));
273 kblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
274 SPECK_ExpandKey_4W<word32, 27>(m_rkeys, m_wspace);
275 break;
276 default:
277 CRYPTOPP_ASSERT(0);;
278 }
279
280 // Altivec loads the current subkey as a 16-byte vector
281 // The extra elements ensure memory backs the last subkey.
282#if CRYPTOPP_ALTIVEC_AVAILABLE
283 m_rkeys.Grow(m_rkeys.size()+4);
284#endif
285}
286
287void SPECK64::Enc::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 26:
296 SPECK_Encrypt<word32, 26>(m_wspace+2, m_wspace+0, m_rkeys);
297 break;
298 case 27:
299 SPECK_Encrypt<word32, 27>(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
310void SPECK64::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
311{
312 // Do the endian gyrations from the paper and align pointers
313 typedef GetBlock<word32, LittleEndian> InBlock;
314 InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
315
316 switch (m_rounds)
317 {
318 case 26:
319 SPECK_Decrypt<word32, 26>(m_wspace+2, m_wspace+0, m_rkeys);
320 break;
321 case 27:
322 SPECK_Decrypt<word32, 27>(m_wspace+2, m_wspace+0, m_rkeys);
323 break;
324 default:
325 CRYPTOPP_ASSERT(0);;
326 }
327
328 // Do the endian gyrations from the paper and align pointers
329 typedef PutBlock<word32, LittleEndian> OutBlock;
330 OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
331}
332
333///////////////////////////////////////////////////////////
334
335std::string SPECK128::Base::AlgorithmProvider() const
336{
337#if (CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS)
338# if (CRYPTOPP_SSSE3_AVAILABLE)
339 if (HasSSSE3())
340 return "SSSE3";
341# endif
342# if (CRYPTOPP_ARM_NEON_AVAILABLE)
343 if (HasNEON())
344 return "NEON";
345# endif
346# if (CRYPTOPP_POWER8_AVAILABLE)
347 if (HasPower8())
348 return "Power8";
349# endif
350#endif
351 return "C++";
352}
353
354void SPECK128::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
355{
356 CRYPTOPP_ASSERT(keyLength == 16 || keyLength == 24 || keyLength == 32);
357 CRYPTOPP_UNUSED(params);
358
359 // Building the key schedule table requires {2,3,4} words workspace.
360 // Encrypting and decrypting requires 4 words workspace.
361 m_kwords = keyLength/sizeof(word64);
362 m_wspace.New(4U);
363
364 // Do the endian gyrations from the paper and align pointers
365 typedef GetBlock<word64, LittleEndian> KeyBlock;
366 KeyBlock kblk(userKey);
367
368 switch (m_kwords)
369 {
370 case 2:
371 m_rkeys.New((m_rounds = 32));
372 kblk(m_wspace[1])(m_wspace[0]);
373 SPECK_ExpandKey_2W<word64, 32>(m_rkeys, m_wspace);
374 break;
375 case 3:
376 m_rkeys.New((m_rounds = 33));
377 kblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
378 SPECK_ExpandKey_3W<word64, 33>(m_rkeys, m_wspace);
379 break;
380 case 4:
381 m_rkeys.New((m_rounds = 34));
382 kblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
383 SPECK_ExpandKey_4W<word64, 34>(m_rkeys, m_wspace);
384 break;
385 default:
386 CRYPTOPP_ASSERT(0);;
387 }
388}
389
390void SPECK128::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
391{
392 // Do the endian gyrations from the paper and align pointers
393 typedef GetBlock<word64, LittleEndian> InBlock;
394 InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
395
396 switch (m_rounds)
397 {
398 case 32:
399 SPECK_Encrypt<word64, 32>(m_wspace+2, m_wspace+0, m_rkeys);
400 break;
401 case 33:
402 SPECK_Encrypt<word64, 33>(m_wspace+2, m_wspace+0, m_rkeys);
403 break;
404 case 34:
405 SPECK_Encrypt<word64, 34>(m_wspace+2, m_wspace+0, m_rkeys);
406 break;
407 default:
408 CRYPTOPP_ASSERT(0);;
409 }
410
411 // Do the endian gyrations from the paper and align pointers
412 typedef PutBlock<word64, LittleEndian> OutBlock;
413 OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
414}
415
416void SPECK128::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
417{
418 // Do the endian gyrations from the paper and align pointers
419 typedef GetBlock<word64, LittleEndian> InBlock;
420 InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
421
422 switch (m_rounds)
423 {
424 case 32:
425 SPECK_Decrypt<word64, 32>(m_wspace+2, m_wspace+0, m_rkeys);
426 break;
427 case 33:
428 SPECK_Decrypt<word64, 33>(m_wspace+2, m_wspace+0, m_rkeys);
429 break;
430 case 34:
431 SPECK_Decrypt<word64, 34>(m_wspace+2, m_wspace+0, m_rkeys);
432 break;
433 default:
434 CRYPTOPP_ASSERT(0);;
435 }
436
437 // Do the endian gyrations from the paper and align pointers
438 typedef PutBlock<word64, LittleEndian> OutBlock;
439 OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
440}
441
442#if defined(CRYPTOPP_SPECK64_ADVANCED_PROCESS_BLOCKS)
443size_t SPECK64::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
444 byte *outBlocks, size_t length, word32 flags) const
445{
446#if defined(CRYPTOPP_SSE41_AVAILABLE)
447 if (HasSSE41())
448 return SPECK64_Enc_AdvancedProcessBlocks_SSE41(m_rkeys, (size_t)m_rounds,
449 inBlocks, xorBlocks, outBlocks, length, flags);
450#endif
451#if (CRYPTOPP_ARM_NEON_AVAILABLE)
452 if (HasNEON())
453 return SPECK64_Enc_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
454 inBlocks, xorBlocks, outBlocks, length, flags);
455#endif
456#if (CRYPTOPP_ALTIVEC_AVAILABLE)
457 if (HasAltivec())
458 return SPECK64_Enc_AdvancedProcessBlocks_ALTIVEC(m_rkeys, (size_t)m_rounds,
459 inBlocks, xorBlocks, outBlocks, length, flags);
460#endif
461 return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
462}
463
464size_t SPECK64::Dec::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
465 byte *outBlocks, size_t length, word32 flags) const
466{
467#if defined(CRYPTOPP_SSE41_AVAILABLE)
468 if (HasSSE41())
469 return SPECK64_Dec_AdvancedProcessBlocks_SSE41(m_rkeys, (size_t)m_rounds,
470 inBlocks, xorBlocks, outBlocks, length, flags);
471#endif
472#if (CRYPTOPP_ARM_NEON_AVAILABLE)
473 if (HasNEON())
474 return SPECK64_Dec_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
475 inBlocks, xorBlocks, outBlocks, length, flags);
476#endif
477#if (CRYPTOPP_ALTIVEC_AVAILABLE)
478 if (HasAltivec())
479 return SPECK64_Dec_AdvancedProcessBlocks_ALTIVEC(m_rkeys, (size_t)m_rounds,
480 inBlocks, xorBlocks, outBlocks, length, flags);
481#endif
482 return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
483}
484#endif // CRYPTOPP_SPECK64_ADVANCED_PROCESS_BLOCKS
485
486#if defined(CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS)
487size_t SPECK128::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
488 byte *outBlocks, size_t length, word32 flags) const
489{
490#if defined(CRYPTOPP_SSSE3_AVAILABLE)
491 if (HasSSSE3())
492 return SPECK128_Enc_AdvancedProcessBlocks_SSSE3(m_rkeys, (size_t)m_rounds,
493 inBlocks, xorBlocks, outBlocks, length, flags);
494#endif
495#if (CRYPTOPP_ARM_NEON_AVAILABLE)
496 if (HasNEON())
497 return SPECK128_Enc_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
498 inBlocks, xorBlocks, outBlocks, length, flags);
499#endif
500#if (CRYPTOPP_POWER8_AVAILABLE)
501 if (HasPower8())
502 return SPECK128_Enc_AdvancedProcessBlocks_POWER8(m_rkeys, (size_t)m_rounds,
503 inBlocks, xorBlocks, outBlocks, length, flags);
504#endif
505 return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
506}
507
508size_t SPECK128::Dec::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
509 byte *outBlocks, size_t length, word32 flags) const
510{
511#if defined(CRYPTOPP_SSSE3_AVAILABLE)
512 if (HasSSSE3())
513 return SPECK128_Dec_AdvancedProcessBlocks_SSSE3(m_rkeys, (size_t)m_rounds,
514 inBlocks, xorBlocks, outBlocks, length, flags);
515#endif
516#if (CRYPTOPP_ARM_NEON_AVAILABLE)
517 if (HasNEON())
518 return SPECK128_Dec_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
519 inBlocks, xorBlocks, outBlocks, length, flags);
520#endif
521#if (CRYPTOPP_POWER8_AVAILABLE)
522 if (HasPower8())
523 return SPECK128_Dec_AdvancedProcessBlocks_POWER8(m_rkeys, (size_t)m_rounds,
524 inBlocks, xorBlocks, outBlocks, length, flags);
525#endif
526 return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
527}
528#endif // CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS
529
530NAMESPACE_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.
Definition: cryptlib.cpp:141
Access a block of memory.
Definition: misc.h:2455
Interface for retrieving values given their names.
Definition: cryptlib.h:294
Access a block of memory.
Definition: misc.h:2496
Library configuration file.
Functions for CPU features and intrinsics.
bool HasAltivec()
Determine if a PowerPC processor has Altivec available.
Definition: cpu.h:614
bool HasNEON()
Determine if an ARM processor has Advanced SIMD available.
Definition: cpu.h:387
bool HasSSSE3()
Determines SSSE3 availability.
Definition: cpu.h:131
bool HasPower8()
Determine if a PowerPC processor has Power8 available.
Definition: cpu.h:640
bool HasSSE41()
Determines SSE4.1 availability.
Definition: cpu.h:142
Utility functions for the Crypto++ library.
Crypto++ library namespace.
Precompiled header file.
Classes for the Speck block cipher.
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:69